package lienol: sync

This commit is contained in:
CN_SZTL 2020-02-24 13:55:07 +08:00
parent 9c81dd407f
commit e08dd4b781
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
191 changed files with 15309 additions and 218 deletions

View File

@ -0,0 +1,18 @@
# Copyright (C) 2018-2020 Lienol <lawlienol@gmail.com>
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for IPSec VPN Server
LUCI_DEPENDS:=+strongswan +strongswan-minimal +strongswan-mod-xauth-generic
LUCI_PKGARCH:=all
PKG_VERSION:=1.0
PKG_RELEASE:=13-20191213
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -0,0 +1,24 @@
-- Copyright 2018-2019 Lienol <lawlienol@gmail.com>
module("luci.controller.ipsec-server", package.seeall)
function index()
if not nixio.fs.access("/etc/config/ipsec") then return end
entry({"admin", "vpn"}, firstchild(), "VPN", 45).dependent = false
entry({"admin", "vpn", "ipsec-server"},
alias("admin", "vpn", "ipsec-server", "settings"),
_("IPSec VPN Server"), 49).dependent = false
entry({"admin", "vpn", "ipsec-server", "settings"},
cbi("ipsec-server/settings"), _("General Settings"), 10).leaf = true
entry({"admin", "vpn", "ipsec-server", "users"}, cbi("ipsec-server/users"),
_("Users Manager"), 20).leaf = true
entry({"admin", "vpn", "ipsec-server", "status"}, call("status")).leaf =
true
end
function status()
local e = {}
e.status = luci.sys.call("/usr/bin/pgrep ipsec > /dev/null") == 0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end

View File

@ -0,0 +1,121 @@
local s = require "luci.sys"
local net = require"luci.model.network".init()
local ifaces = s.net:devices()
local m, s, o
mp = Map("ipsec", translate("IPSec VPN Server"))
mp.description = translate(
"IPSec VPN connectivity using the native built-in VPN Client on iOS or Andriod (IKEv1 with PSK and Xauth)")
mp.template = "ipsec-server/index"
s = mp:section(TypedSection, "service")
s.anonymous = true
o = s:option(DummyValue, "ipsec-server_status", translate("Current Condition"))
o.template = "ipsec-server/status"
enabled = s:option(Flag, "enabled", translate("Enable"))
enabled.default = 0
enabled.rmempty = false
clientip = s:option(Value, "clientip", translate("VPN Client IP"))
clientip.datatype = "ip4addr"
clientip.description = translate(
"VPN Client reserved started IP addresses with the same subnet mask")
clientip.optional = false
clientip.rmempty = false
clientdns = s:option(Value, "clientdns", translate("VPN Client DNS"))
clientdns.datatype = "ip4addr"
clientdns.description = translate("DNS using in VPN tunnel.")
clientdns.optional = false
clientdns.rmempty = false
secret = s:option(Value, "secret", translate("Secret Pre-Shared Key"))
secret.password = true
o = s:option(Flag, "is_nat", translate("is_nat"))
o.rmempty = false
o = s:option(ListValue, "export_interface", translate("Interface"),
translate("Specify interface forwarding traffic."))
o:value("default", translate("default"))
for _, iface in ipairs(ifaces) do
if (iface:match("^br*") or iface:match("^eth*") or iface:match("^pppoe*") or
iface:match("wlan*")) then
local nets = net:get_interface(iface)
nets = nets and nets:get_networks() or {}
for k, v in pairs(nets) do nets[k] = nets[k].sid end
nets = table.concat(nets, ",")
o:value(iface, ((#nets > 0) and "%s (%s)" % {iface, nets} or iface))
end
end
o:depends("is_nat", "1")
function mp.on_save(self)
require "luci.model.uci"
require "luci.sys"
local have_ike_rule = false
local have_ipsec_rule = false
local have_ah_rule = false
local have_esp_rule = false
luci.model.uci.cursor():foreach('firewall', 'rule', function(section)
if section.name == 'ike' then have_ike_rule = true end
if section.name == 'ipsec' then have_ipsec_rule = true end
if section.name == 'ah' then have_ah_rule = true end
if section.name == 'esp' then have_esp_rule = true end
end)
if not have_ike_rule then
local cursor = luci.model.uci.cursor()
local ike_rulename = cursor:add('firewall', 'rule')
cursor:tset('firewall', ike_rulename, {
['name'] = 'ike',
['target'] = 'ACCEPT',
['src'] = 'wan',
['proto'] = 'udp',
['dest_port'] = 500
})
cursor:save('firewall')
cursor:commit('firewall')
end
if not have_ipsec_rule then
local cursor = luci.model.uci.cursor()
local ipsec_rulename = cursor:add('firewall', 'rule')
cursor:tset('firewall', ipsec_rulename, {
['name'] = 'ipsec',
['target'] = 'ACCEPT',
['src'] = 'wan',
['proto'] = 'udp',
['dest_port'] = 4500
})
cursor:save('firewall')
cursor:commit('firewall')
end
if not have_ah_rule then
local cursor = luci.model.uci.cursor()
local ah_rulename = cursor:add('firewall', 'rule')
cursor:tset('firewall', ah_rulename, {
['name'] = 'ah',
['target'] = 'ACCEPT',
['src'] = 'wan',
['proto'] = 'ah'
})
cursor:save('firewall')
cursor:commit('firewall')
end
if not have_esp_rule then
local cursor = luci.model.uci.cursor()
local esp_rulename = cursor:add('firewall', 'rule')
cursor:tset('firewall', esp_rulename, {
['name'] = 'esp',
['target'] = 'ACCEPT',
['src'] = 'wan',
['proto'] = 'esp'
})
cursor:save('firewall')
cursor:commit('firewall')
end
end
return mp

View File

@ -0,0 +1,18 @@
mp = Map("ipsec", translate("IPSec VPN Server"))
mp.description = translate(
"IPSec VPN connectivity using the native built-in VPN Client on iOS or Andriod (IKEv1 with PSK and Xauth)")
s = mp:section(TypedSection, "users", translate("Users Manager"))
s.addremove = true
s.anonymous = true
s.template = "cbi/tblsection"
enabled = s:option(Flag, "enabled", translate("Enabled"))
enabled.rmempty = false
username = s:option(Value, "username", translate("User name"))
username.placeholder = translate("User name")
username.rmempty = true
password = s:option(Value, "password", translate("Password"))
password.rmempty = true
return mp

View File

@ -0,0 +1,13 @@
<% include("cbi/map") %>
<script type="text/javascript">//<![CDATA[
XHR.poll(2, '<%=luci.dispatcher.build_url("admin", "vpn", "ipsec-server", "status")%>', null,
function(x, result)
{
var status = document.getElementsByClassName('ipsec-server_status')[0];
status.setAttribute("style","font-weight:bold;");
status.setAttribute("color",result.status ? "green":"red");
status.innerHTML = result.status?'<%=translate("RUNNING")%>':'<%=translate("NOT RUNNING")%>';
}
)
//]]>
</script>

View File

@ -0,0 +1,3 @@
<%+cbi/valueheader%>
<font class="ipsec-server_status"><%=pcdata(self:cfgvalue(section) or self.default or "")%></font>
<%+cbi/valuefooter%>

View File

@ -0,0 +1,50 @@
msgid "IPSec VPN Server"
msgstr "IPSec VPN 服务器"
msgid "IPSec VPN connectivity using the native built-in VPN Client on iOS or Andriod (IKEv1 with PSK and Xauth)"
msgstr "使用iOS 或者 Andriod (IKEv1 with PSK and Xauth) 原生内置 IPSec VPN 客户端进行连接"
msgid "IPSec VPN Server status"
msgstr "IPSec VPN 服务器运行状态"
msgid "Current Condition"
msgstr "当前状态"
msgid "General settings"
msgstr "基本设置"
msgid "VPN Client IP"
msgstr "VPN客户端地址段"
msgid "VPN Client reserved started IP addresses with the same subnet mask"
msgstr "VPN客户端获取IP的起始地址例如 192.168.100.10/24"
msgid "VPN Client DNS"
msgstr "VPN客户端DNS服务器"
msgid "DNS using in VPN tunnel."
msgstr "指定VPN客户端的DNS地址。"
msgid "Secret Pre-Shared Key"
msgstr "PSK密钥"
msgid "is_nat"
msgstr "NAT转发"
msgid "Interface"
msgstr "接口"
msgid "Specify interface forwarding traffic."
msgstr "指定接口转发流量。"
msgid "Disable from startup"
msgstr "禁止开机启动"
msgid "Enable on startup"
msgstr "允许开机启动"
msgid "NOT RUNNING"
msgstr "未运行"
msgid "RUNNING"
msgstr "运行中"

View File

@ -0,0 +1,14 @@
config service 'ipsec'
option secret 'ipsec'
option clientip '192.168.100.1/24'
option is_nat '1'
option export_interface 'default'
option clientdns '208.67.222.222'
option enabled '0'
config users
option enabled '1'
option username 'guest'
option password '123456'

View File

@ -0,0 +1,441 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2018-2019 Lienol <lawlienol@gmail.com>
START=90
STOP=10
USE_PROCD=1
PROG=/usr/lib/ipsec/starter
. $IPKG_INSTROOT/lib/functions.sh
. $IPKG_INSTROOT/lib/functions/network.sh
IPSEC_SECRETS_FILE=/etc/ipsec.secrets
IPSEC_CONN_FILE=/etc/ipsec.conf
STRONGSWAN_CONF_FILE=/etc/strongswan.conf
IPSEC_VAR_SECRETS_FILE=/var/ipsec/ipsec.secrets
IPSEC_VAR_CONN_FILE=/var/ipsec/ipsec.conf
STRONGSWAN_VAR_CONF_FILE=/var/ipsec/strongswan.conf
WAIT_FOR_INTF=0
file_reset() {
: > "$1"
}
xappend() {
local file="$1"
shift
echo "${@}" >> "${file}"
}
remove_include() {
local file="$1"
local include="$2"
sed -i "\_${include}_d" "${file}"
}
remove_includes() {
remove_include "${IPSEC_CONN_FILE}" "${IPSEC_VAR_CONN_FILE}"
remove_include "${IPSEC_SECRETS_FILE}" "${IPSEC_VAR_SECRETS_FILE}"
remove_include "${STRONGSWAN_CONF_FILE}" "${STRONGSWAN_VAR_CONF_FILE}"
}
do_include() {
local conf="$1"
local uciconf="$2"
local backup=`mktemp -t -p /tmp/ ipsec-init-XXXXXX`
[ ! -f "${conf}" ] && rm -rf "${conf}"
touch "${conf}"
cat "${conf}" | grep -v "${uciconf}" > "${backup}"
mv "${backup}" "${conf}"
xappend "${conf}" "include ${uciconf}"
file_reset "${uciconf}"
}
ipsec_reset() {
do_include "${IPSEC_CONN_FILE}" "${IPSEC_VAR_CONN_FILE}"
}
ipsec_xappend() {
xappend "${IPSEC_VAR_CONN_FILE}" "$@"
}
swan_reset() {
do_include "${STRONGSWAN_CONF_FILE}" "${STRONGSWAN_VAR_CONF_FILE}"
}
swan_xappend() {
xappend "${STRONGSWAN_VAR_CONF_FILE}" "$@"
}
secret_reset() {
do_include "${IPSEC_SECRETS_FILE}" "${IPSEC_VAR_SECRETS_FILE}"
}
secret_xappend() {
xappend "${IPSEC_VAR_SECRETS_FILE}" "$@"
}
warning() {
echo "WARNING: $@" >&2
}
add_crypto_proposal() {
local encryption_algorithm
local hash_algorithm
local dh_group
config_get encryption_algorithm "$1" encryption_algorithm
config_get hash_algorithm "$1" hash_algorithm
config_get dh_group "$1" dh_group
[ -n "${encryption_algorithm}" ] && \
crypto="${crypto:+${crypto},}${encryption_algorithm}${hash_algorithm:+-${hash_algorithm}}${dh_group:+-${dh_group}}"
}
set_crypto_proposal() {
local conf="$1"
local proposal
crypto=""
config_get crypto_proposal "$conf" crypto_proposal ""
for proposal in $crypto_proposal; do
add_crypto_proposal "$proposal"
done
[ -n "${crypto}" ] && {
local force_crypto_proposal
config_get_bool force_crypto_proposal "$conf" force_crypto_proposal
[ "${force_crypto_proposal}" = "1" ] && crypto="${crypto}!"
}
crypto_proposal="${crypto}"
}
config_conn() {
# Generic ipsec conn section shared by tunnel and transport
local mode
local local_subnet
local local_nat
local local_sourceip
local local_updown
local local_firewall
local remote_subnet
local remote_sourceip
local remote_updown
local remote_firewall
local ikelifetime
local lifetime
local margintime
local keyingtries
local dpdaction
local dpddelay
local inactivity
local keyexchange
config_get mode "$1" mode "route"
config_get local_subnet "$1" local_subnet ""
config_get local_nat "$1" local_nat ""
config_get local_sourceip "$1" local_sourceip ""
config_get local_updown "$1" local_updown ""
config_get local_firewall "$1" local_firewall ""
config_get remote_subnet "$1" remote_subnet ""
config_get remote_sourceip "$1" remote_sourceip ""
config_get remote_updown "$1" remote_updown ""
config_get remote_firewall "$1" remote_firewall ""
config_get ikelifetime "$1" ikelifetime "3h"
config_get lifetime "$1" lifetime "1h"
config_get margintime "$1" margintime "9m"
config_get keyingtries "$1" keyingtries "3"
config_get dpdaction "$1" dpdaction "none"
config_get dpddelay "$1" dpddelay "30s"
config_get inactivity "$1" inactivity
config_get keyexchange "$1" keyexchange "ikev2"
[ -n "$local_nat" ] && local_subnet=$local_nat
ipsec_xappend "conn $config_name-$1"
ipsec_xappend " left=%any"
ipsec_xappend " right=$remote_gateway"
[ -n "$local_sourceip" ] && ipsec_xappend " leftsourceip=$local_sourceip"
[ -n "$local_subnet" ] && ipsec_xappend " leftsubnet=$local_subnet"
[ -n "$local_firewall" ] && ipsec_xappend " leftfirewall=$local_firewall"
[ -n "$remote_firewall" ] && ipsec_xappend " rightfirewall=$remote_firewall"
ipsec_xappend " ikelifetime=$ikelifetime"
ipsec_xappend " lifetime=$lifetime"
ipsec_xappend " margintime=$margintime"
ipsec_xappend " keyingtries=$keyingtries"
ipsec_xappend " dpdaction=$dpdaction"
ipsec_xappend " dpddelay=$dpddelay"
[ -n "$inactivity" ] && ipsec_xappend " inactivity=$inactivity"
if [ "$auth_method" = "psk" ]; then
ipsec_xappend " leftauth=psk"
ipsec_xappend " rightauth=psk"
[ "$remote_sourceip" != "" ] && ipsec_xappend " rightsourceip=$remote_sourceip"
[ "$remote_subnet" != "" ] && ipsec_xappend " rightsubnet=$remote_subnet"
ipsec_xappend " auto=$mode"
else
warning "AuthenticationMethod $auth_method not supported"
fi
[ -n "$local_identifier" ] && ipsec_xappend " leftid=$local_identifier"
[ -n "$remote_identifier" ] && ipsec_xappend " rightid=$remote_identifier"
[ -n "$local_updown" ] && ipsec_xappend " leftupdown=$local_updown"
[ -n "$remote_updown" ] && ipsec_xappend " rightupdown=$remote_updown"
ipsec_xappend " keyexchange=$keyexchange"
set_crypto_proposal "$1"
[ -n "${crypto_proposal}" ] && ipsec_xappend " esp=$crypto_proposal"
[ -n "${ike_proposal}" ] && ipsec_xappend " ike=$ike_proposal"
}
config_tunnel() {
config_conn "$1"
# Specific for the tunnel part
ipsec_xappend " type=tunnel"
}
config_transport() {
config_conn "$1"
# Specific for the transport part
ipsec_xappend " type=transport"
}
config_remote() {
local enabled
local gateway
local pre_shared_key
local auth_method
config_name=$1
config_get_bool enabled "$1" enabled 0
[ $enabled -eq 0 ] && return
config_get gateway "$1" gateway
config_get pre_shared_key "$1" pre_shared_key
config_get auth_method "$1" authentication_method
config_get local_identifier "$1" local_identifier ""
config_get remote_identifier "$1" remote_identifier ""
[ "$gateway" = "any" ] && remote_gateway="%any" || remote_gateway="$gateway"
[ -z "$local_identifier" ] && {
local ipdest
[ "$remote_gateway" = "%any" ] && ipdest="1.1.1.1" || ipdest="$remote_gateway"
local_gateway=`ip route get $ipdest | awk -F"src" '/src/{gsub(/ /,"");print $2}'`
}
[ -n "$local_identifier" ] && secret_xappend -n "$local_identifier " || secret_xappend -n "$local_gateway "
[ -n "$remote_identifier" ] && secret_xappend -n "$remote_identifier " || secret_xappend -n "$remote_gateway "
secret_xappend ": PSK \"$pre_shared_key\""
set_crypto_proposal "$1"
ike_proposal="$crypto_proposal"
config_list_foreach "$1" tunnel config_tunnel
config_list_foreach "$1" transport config_transport
ipsec_xappend ""
}
config_ipsec() {
local debug
local rtinstall_enabled
local routing_tables_ignored
local routing_table
local routing_table_id
local interface
local device_list
ipsec_reset
secret_reset
swan_reset
ipsec_xappend "# generated by /etc/init.d/ipsecvpn"
ipsec_xappend "version 2"
ipsec_xappend ""
secret_xappend "# generated by /etc/init.d/ipsecvpn"
config_get debug "$1" debug 0
config_get_bool rtinstall_enabled "$1" rtinstall_enabled 1
[ $rtinstall_enabled -eq 1 ] && install_routes=yes || install_routes=no
# prepare extra charon config option ignore_routing_tables
for routing_table in $(config_get "$1" "ignore_routing_tables"); do
if [ "$routing_table" -ge 0 ] 2>/dev/null; then
routing_table_id=$routing_table
else
routing_table_id=$(sed -n '/[ \t]*[0-9]\+[ \t]\+'$routing_table'[ \t]*$/s/[ \t]*\([0-9]\+\).*/\1/p' /etc/iproute2/rt_tables)
fi
[ -n "$routing_table_id" ] && append routing_tables_ignored "$routing_table_id"
done
local interface_list=$(config_get "$1" "interface")
if [ -z "$interface_list" ]; then
WAIT_FOR_INTF=0
else
for interface in $interface_list; do
network_get_device device $interface
[ -n "$device" ] && append device_list "$device" ","
done
[ -n "$device_list" ] && WAIT_FOR_INTF=0 || WAIT_FOR_INTF=1
fi
swan_xappend "# generated by /etc/init.d/ipsecvpn"
swan_xappend "charon {"
swan_xappend " load_modular = yes"
swan_xappend " install_routes = $install_routes"
[ -n "$routing_tables_ignored" ] && swan_xappend " ignore_routing_tables = $routing_tables_ignored"
[ -n "$device_list" ] && swan_xappend " interfaces_use = $device_list"
swan_xappend " plugins {"
swan_xappend " include /etc/strongswan.d/charon/*.conf"
swan_xappend " }"
swan_xappend " syslog {"
swan_xappend " identifier = ipsec"
swan_xappend " daemon {"
swan_xappend " default = $debug"
swan_xappend " }"
swan_xappend " auth {"
swan_xappend " default = $debug"
swan_xappend " }"
swan_xappend " }"
swan_xappend "}"
}
prepare_env() {
mkdir -p /var/ipsec
remove_includes
config_load ipsec
config_foreach config_ipsec ipsec
config_foreach config_remote remote
}
service_running() {
ipsec status > /dev/null 2>&1
}
reload_service() {
local bool vt_enabled=`uci get ipsec.@service[0].enabled 2>/dev/null`
[ "$vt_enabled" = 0 ] && /etc/init.d/ipsecvpn stop > /dev/null 2>&1 && return
running && {
prepare_env
[ $WAIT_FOR_INTF -eq 0 ] && {
ipsec rereadall
ipsec reload
return
}
}
[ "$vt_enabled" = 1 ] && start
}
check_ipsec_interface() {
local intf
for intf in $(config_get "$1" interface); do
procd_add_interface_trigger "interface.*" "$intf" /etc/init.d/ipsecvpn reload
done
}
service_triggers() {
procd_add_reload_trigger "ipsec"
config load "ipsec"
config_foreach check_ipsec_interface ipsec
}
start_service() {
fw3 reload
local vt_enabled=`uci get ipsec.@service[0].enabled 2>/dev/null`
local vt_clientip=`uci get ipsec.@service[0].clientip`
local vt_clientdns=`uci get ipsec.@service[0].clientdns`
local vt_secret=`uci get ipsec.@service[0].secret 2>/dev/null`
local vt_is_nat=`uci get ipsec.@service[0].is_nat 2>/dev/null`
local vt_export_interface=`uci get ipsec.@service[0].export_interface 2>/dev/null`
[ "$vt_enabled" = 0 ] && /etc/init.d/ipsecvpn stop > /dev/null 2>&1 && return
cat > /etc/ipsec.conf <<EOF
# ipsec.conf - strongSwan IPsec configuration file
# basic configuration
config setup
# strictcrlpolicy=yes
uniqueids=never
# Add connections here.
conn xauth_psk
keyexchange=ikev1
ike=aes128-sha1-modp2048,aes128-sha1-modp1024,3des-sha1-modp1024,3des-sha1-modp1536
esp=aes128-sha1,3des-sha1
left=%defaultroute
leftauth=psk
leftsubnet=0.0.0.0/0
right=%any
rightauth=psk
rightauth2=xauth
rightsourceip=$vt_clientip
rightdns=$vt_clientdns
auto=add
EOF
cat > /etc/ipsec.secrets <<EOF
# /etc/ipsec.secrets - strongSwan IPsec secrets file
: PSK "$vt_secret"
EOF
config_load ipsec
config_foreach setup_login users
prepare_env
[ $WAIT_FOR_INTF -eq 1 ] && return
procd_open_instance
procd_set_param command $PROG --daemon charon --nofork
procd_set_param file $IPSEC_CONN_FILE
procd_append_param file $IPSEC_SECRETS_FILE
procd_append_param file $STRONGSWAN_CONF_FILE
procd_append_param file /etc/strongswan.d/*.conf
procd_append_param file /etc/strongswan.d/charon/*.conf
procd_set_param respawn
procd_close_instance
}
setup_login() {
config_get enabled $1 enabled
[ "$enabled" -eq 0 ] && return 0
config_get username $1 username
config_get password $1 password
[ -n "$username" ] || return 0
[ -n "$password" ] || return 0
echo "$username : XAUTH '$password'" >> /etc/ipsec.secrets
}

View File

@ -0,0 +1,23 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete firewall.ipsecvpn
set firewall.ipsecvpn=include
set firewall.ipsecvpn.type=script
set firewall.ipsecvpn.path=/usr/share/ipsecvpn/firewall.include
set firewall.ipsecvpn.reload=1
EOF
uci -q batch <<-EOF >/dev/null
delete ucitrack.@ipsec[-1]
add ucitrack ipsec
set ucitrack.@ipsec[-1].exec='/etc/init.d/ipsecvpn start'
commit ucitrack
EOF
/etc/init.d/ipsec disable && /etc/init.d/ipsec stop
rm -f /etc/init.d/ipsec
chmod a+x /usr/share/ipsecvpn/* >/dev/null 2>&1
rm -f /tmp/luci-indexcache
exit 0

View File

@ -0,0 +1,45 @@
#!/bin/sh
iptables -D INPUT -p udp -m multiport --dports 500,4500 -m comment --comment "Rule For IPSec VPN Server" -j ACCEPT 2> /dev/null
ipsec_nums=`iptables -t nat -L POSTROUTING 2> /dev/null|grep -c "Rule For IPSec VPN Server"`
if [ -n "$ipsec_nums" ]; then
until [ "$ipsec_nums" = 0 ]
do
rules=`iptables -t nat -L POSTROUTING --line-num 2> /dev/null|grep "Rule For IPSec VPN Server" |awk '{print $1}'`
for rule in $rules
do
iptables -t nat -D POSTROUTING $rule 2> /dev/null
break
done
ipsec_nums=`expr $ipsec_nums - 1`
done
fi
nums=`iptables -L forwarding_rule 2> /dev/null|grep -c "Rule For IPSec VPN Server"`
if [ -n "$nums" ]; then
until [ "$nums" = 0 ]
do
rules=`iptables -L forwarding_rule --line-num 2> /dev/null|grep "Rule For IPSec VPN Server" |awk '{print $1}'`
for rule in $rules
do
iptables -D forwarding_rule $rule 2> /dev/null
break
done
nums=`expr $nums - 1`
done
fi
enable=$(uci get ipsec.ipsec.enabled)
if [ $enable -eq 1 ]; then
is_nat=$(uci get ipsec.ipsec.is_nat)
if [ "$is_nat" -eq 1 ];then
clientip=$(uci get ipsec.ipsec.clientip)
export_interface=$(uci get ipsec.ipsec.export_interface)
if [ "$export_interface" != "default" ];then
iptables -t nat -I POSTROUTING -s ${clientip%.*}.0/24 -o ${export_interface} -m comment --comment "Rule For IPSec VPN Server" -j MASQUERADE
else
iptables -t nat -I POSTROUTING -s ${clientip%.*}.0/24 -m comment --comment "Rule For IPSec VPN Server" -j MASQUERADE
fi
iptables -I forwarding_rule -s ${clientip%.*}.0/24 -m comment --comment "Rule For IPSec VPN Server" -j ACCEPT
fi
iptables -I INPUT -p udp -m multiport --dports 500,4500 -m comment --comment "Rule For IPSec VPN Server" -j ACCEPT
fi

View File

@ -6,9 +6,9 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=3.5
PKG_RELEASE:=20
PKG_DATE:=20200223
PKG_VERSION:=3.6
PKG_RELEASE:=1
PKG_DATE:=20200224
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)

View File

@ -1,98 +1,21 @@
local ucursor = require"luci.model.uci".cursor()
local json = require "luci.jsonc"
local api = require "luci.model.cbi.passwall.api.api"
local node_section = arg[1]
local proto = arg[2]
local redir_port = arg[3]
local socks5_proxy_port = arg[4]
local node = ucursor:get_all("passwall", node_section)
local inbound_json = {}
local inboundDetour_json = nil
local vnext = {}
local inbounds = {}
local outbounds = {}
local network = proto
local routing = nil
if socks5_proxy_port ~= "nil" then
inbound_json = {
listen = "0.0.0.0",
port = socks5_proxy_port,
protocol = "socks",
settings = {auth = "noauth", udp = true, ip = "127.0.0.1"}
}
end
if redir_port ~= "nil" then
inbound_json = {
port = redir_port,
protocol = "dokodemo-door",
settings = {network = proto, followRedirect = true},
sniffing = {enabled = true, destOverride = {"http", "tls"}}
}
if proto == "tcp" and node.v2ray_tcp_socks == "1" then
inboundDetour_json = {
listen = "0.0.0.0",
port = tonumber(node.v2ray_tcp_socks_port),
protocol = "socks",
settings = {
auth = node.v2ray_tcp_socks_auth,
accounts = (node.v2ray_tcp_socks_auth == "password") and {
{
user = node.v2ray_tcp_socks_auth_username,
pass = node.v2ray_tcp_socks_auth_password
}
} or nil,
udp = true
}
}
end
end
if node.v2ray_balancing_node then
local nodes = node.v2ray_balancing_node
local length = #nodes
for i = 1, length do
local id = nodes[i]
local vnext_json = {
address = api.uci_get_type_id(id, "address"),
port = tonumber(api.uci_get_type_id(id, "port")),
users = {
{
id = api.uci_get_type_id(id, "v2ray_VMess_id"),
alterId = tonumber(api.uci_get_type_id(id,
"v2ray_VMess_alterId")),
level = tonumber(
api.uci_get_type_id(id, "v2ray_VMess_level")),
security = api.uci_get_type_id(id, "v2ray_security")
}
}
}
vnext[i] = vnext_json
end
else
vnext = {
{
address = node.address,
port = tonumber(node.port),
users = {
{
id = node.v2ray_VMess_id,
alterId = tonumber(node.v2ray_VMess_alterId),
level = tonumber(node.v2ray_VMess_level),
security = node.v2ray_security
}
}
}
}
end
local v2ray = {
log = {
-- error = "/var/log/v2ray.log",
loglevel = "warning"
},
-- 传入连接
inbounds = {inbound_json, inboundDetour_json},
-- 传出连接
outbounds = {
{
local function gen_outbound(node, tag)
local result = nil
if node then
result = {
tag = tag or node[".name"],
cbi_id = node[".name"],
protocol = node.v2ray_protocol or "vmess",
mux = {
enabled = (node.v2ray_mux == "1") and true or false,
@ -146,9 +69,151 @@ local v2ray = {
header = {type = node.v2ray_quic_guise}
} or nil
},
settings = {vnext = vnext}
}, -- 额外传出连接
{protocol = "freedom", tag = "direct", settings = {keep = ""}}
settings = {
vnext = {
{
address = node.address,
port = tonumber(node.port),
users = {
{
id = node.v2ray_VMess_id,
alterId = tonumber(node.v2ray_VMess_alterId),
level = tonumber(node.v2ray_VMess_level),
security = node.v2ray_security
}
}
}
}
}
}
end
return result
end
if socks5_proxy_port ~= "nil" then
table.insert(inbounds, {
listen = "0.0.0.0",
port = socks5_proxy_port,
protocol = "socks",
settings = {auth = "noauth", udp = true, ip = "127.0.0.1"}
})
network = "tcp,udp"
end
if redir_port ~= "nil" then
table.insert(inbounds, {
port = redir_port,
protocol = "dokodemo-door",
settings = {network = proto, followRedirect = true},
sniffing = {enabled = true, destOverride = {"http", "tls"}}
})
if proto == "tcp" and node.v2ray_tcp_socks == "1" then
table.insert(inbounds, {
listen = "0.0.0.0",
port = tonumber(node.v2ray_tcp_socks_port),
protocol = "socks",
settings = {
auth = node.v2ray_tcp_socks_auth,
accounts = (node.v2ray_tcp_socks_auth == "password") and {
{
user = node.v2ray_tcp_socks_auth_username,
pass = node.v2ray_tcp_socks_auth_password
}
} or nil,
udp = true
}
})
end
end
if node.type == "V2ray_balancing" and node.v2ray_balancing_node then
local nodes = node.v2ray_balancing_node
local length = #nodes
for i = 1, length do
local node = ucursor:get_all("passwall", nodes[i])
local outbound = gen_outbound(node)
if outbound then table.insert(outbounds, outbound) end
end
routing = {
domainStrategy = "IPOnDemand",
balancers = {{tag = "balancer", selector = nodes}},
rules = {
{type = "field", network = "tcp,udp", balancerTag = "balancer"}
}
}
elseif node.type == "V2ray_shunt" then
local rules = {}
local youtube_node = node.youtube_node or nil
if youtube_node and youtube_node ~= "nil" then
local node = ucursor:get_all("passwall", youtube_node)
local youtube_outbound = gen_outbound(node, "youtube")
if youtube_outbound then
table.insert(outbounds, youtube_outbound)
local rule = {
type = "field",
domain = {
"youtube", "youtube.com", "youtu.be", "googlevideo.com",
"gvt2.com"
},
outboundTag = "youtube"
}
table.insert(rules, rule)
end
end
local netflix_node = node.netflix_node or nil
if netflix_node and netflix_node ~= "nil" then
local node = ucursor:get_all("passwall", netflix_node)
local netflix_outbound = gen_outbound(node, "netflix")
if netflix_outbound then
table.insert(outbounds, netflix_outbound)
local rule = {
type = "field",
domain = {
"netflix", "netflix.com", "nflxso.net", "nflxext.com",
"nflximg.com", "nflximg.net", "nflxvideo.net"
},
outboundTag = "netflix"
}
table.insert(rules, rule)
end
end
local default_node = node.default_node or nil
if default_node and default_node ~= "nil" then
local node = ucursor:get_all("passwall", default_node)
local default_outbound = gen_outbound(node, "default")
if default_outbound then
table.insert(outbounds, default_outbound)
local rule = {
type = "field",
outboundTag = "default",
network = network
}
table.insert(rules, rule)
end
end
routing = {domainStrategy = "IPOnDemand", rules = rules}
else
local outbound = gen_outbound(node)
if outbound then table.insert(outbounds, outbound) end
end
-- 额外传出连接
table.insert(outbounds,
{protocol = "freedom", tag = "direct", settings = {keep = ""}})
local v2ray = {
log = {
-- error = "/var/log/v2ray.log",
loglevel = "warning"
},
-- 传入连接
inbounds = inbounds,
-- 传出连接
outbounds = outbounds,
-- 路由
routing = routing
}
print(json.stringify(v2ray, 1))

View File

@ -71,6 +71,7 @@ if is_finded("ssr-redir") then type:value("SSR", translate("ShadowsocksR")) end
if is_installed("v2ray") or is_finded("v2ray") then
type:value("V2ray", translate("V2ray"))
type:value("V2ray_balancing", translate("V2ray Balancing"))
type:value("V2ray_shunt", translate("V2ray Shunt"))
end
if is_installed("brook") or is_finded("brook") then
type:value("Brook", translate("Brook"))
@ -83,7 +84,6 @@ v2ray_protocol = s:option(ListValue, "v2ray_protocol",
translate("V2ray Protocol"))
v2ray_protocol:value("vmess", translate("Vmess"))
v2ray_protocol:depends("type", "V2ray")
v2ray_protocol:depends("type", "V2ray_balancing")
local n = {}
uci:foreach(appname, "nodes", function(e)
@ -98,10 +98,25 @@ table.sort(key_table)
v2ray_balancing_node = s:option(DynamicList, "v2ray_balancing_node",
translate("Load balancing node list"), translate(
"Load balancing node list, <a target='_blank' href='https://toutyrater.github.io/app/balance.html'>document</a>"))
"Load balancing node list, <a target='_blank' href='https://toutyrater.github.io/routing/balance2.html'>document</a>"))
for _, key in pairs(key_table) do v2ray_balancing_node:value(key, n[key]) end
v2ray_balancing_node:depends("type", "V2ray_balancing")
youtube_node = s:option(ListValue, "youtube_node", "Youtube " .. translate("Node"))
youtube_node:value("nil", translate("Close"))
for _, key in pairs(key_table) do youtube_node:value(key, n[key]) end
youtube_node:depends("type", "V2ray_shunt")
netflix_node = s:option(ListValue, "netflix_node", "Netflix " .. translate("Node"))
netflix_node:value("nil", translate("Close"))
for _, key in pairs(key_table) do netflix_node:value(key, n[key]) end
netflix_node:depends("type", "V2ray_shunt")
default_node = s:option(ListValue, "default_node", translate("Default") .. " " .. translate("Node"))
default_node:value("nil", translate("Close"))
for _, key in pairs(key_table) do default_node:value(key, n[key]) end
default_node:depends("type", "V2ray_shunt")
address = s:option(Value, "address", translate("Address (Support Domain Name)"))
address.rmempty = false
address:depends("type", "Socks5")
@ -243,7 +258,6 @@ v2ray_stream_security = s:option(ListValue, "v2ray_stream_security",
v2ray_stream_security:value("none", "none")
v2ray_stream_security:value("tls", "tls")
v2ray_stream_security:depends("type", "V2ray")
v2ray_stream_security:depends("type", "V2ray_balancing")
-- [[ TLS部分 ]] --
tls_serverName = s:option(Value, "tls_serverName", translate("Domain"))
@ -265,7 +279,6 @@ v2ray_transport:value("h2", "HTTP/2")
v2ray_transport:value("ds", "DomainSocket")
v2ray_transport:value("quic", "QUIC")
v2ray_transport:depends("type", "V2ray")
v2ray_transport:depends("type", "V2ray_balancing")
-- [[ TCP部分 ]]--
@ -363,7 +376,6 @@ v2ray_quic_guise:depends("v2ray_transport", "quic")
v2ray_mux = s:option(Flag, "v2ray_mux", translate("Mux"))
v2ray_mux:depends("type", "V2ray")
v2ray_mux:depends("type", "V2ray_balancing")
v2ray_mux_concurrency = s:option(Value, "v2ray_mux_concurrency",
translate("Mux Concurrency"))
@ -412,7 +424,6 @@ trojan_cert_path:depends("trojan_verify_cert", "1")
-- v2ray_insecure = s:option(Flag, "v2ray_insecure", translate("allowInsecure"))
-- v2ray_insecure:depends("type", "V2ray")
-- v2ray_insecure:depends("type", "V2ray_balancing")
function rmempty_restore()
address.rmempty = true

View File

@ -63,6 +63,7 @@ if api.uci_get_type("global_other", "show_group", "1") == "1" then
end
end
s.sortable = true
-- 简洁模式
if api.uci_get_type("global_other", "compact_display_nodes", "0") == "1" then
if show_group then show_group.width = "25%" end
@ -84,7 +85,6 @@ if api.uci_get_type("global_other", "compact_display_nodes", "0") == "1" then
return str
end
else
s.sortable = true
---- Add Mode
if api.uci_get_type("global_other", "show_add_mode", "1") == "1" then
o = s:option(DummyValue, "add_mode", translate("Add Mode"))

View File

@ -292,11 +292,17 @@ msgstr "V2ray 负载均衡"
msgid "V2ray_balancing"
msgstr "V2ray 负载均衡"
msgid "V2ray Shunt"
msgstr "V2ray 分流"
msgid "V2ray_shunt"
msgstr "V2ray 分流"
msgid "Load balancing node list"
msgstr "负载均衡节点列表"
msgid "Load balancing node list, <a target='_blank' href='https://toutyrater.github.io/app/balance.html'>document</a>"
msgstr "负载均衡节点列表,<a target='_blank' href='https://toutyrater.github.io/app/balance.html'>文档原理</a>"
msgid "Load balancing node list, <a target='_blank' href='https://toutyrater.github.io/routing/balance2.html'>document</a>"
msgstr "负载均衡节点列表,<a target='_blank' href='https://toutyrater.github.io/routing/balance2.html'>文档原理</a>"
msgid "Address"
msgstr "地址"
@ -484,6 +490,9 @@ msgstr "负载均衡设置"
msgid "Add a node, Export Of Multi WAN Only support Multi Wan. Load specific gravity range 1-256. Multiple primary servers can be load balanced, standby will only be enabled when the primary server is offline!"
msgstr "添加节点指定出口功能是为多WAN用户准备的。负载比重范围1-256。多个主服务器可以负载均衡备用只有在主服务器离线时才会启用"
msgid "Node"
msgstr "节点"
msgid "Node Address"
msgstr "节点地址"

View File

@ -300,24 +300,7 @@ gen_start_config() {
eval SOCKS5_NODE${5}_PORT=$port
if [ "$type" == "socks5" ]; then
echolog "Socks5节点不能使用Socks5代理节点"
elif [ "$type" == "v2ray" ]; then
lua $API_GEN_V2RAY $node nil nil $local_port >$config_file
ln_start_bin $(config_t_get global_app v2ray_file $(find_bin v2ray))/v2ray v2ray "-config=$config_file"
elif [ "$type" == "v2ray_balancing" ]; then
local balancing_node=$(config_n_get $node v2ray_balancing_node)
balancing_node_address=""
for node_id in $balancing_node
do
local address=$(config_n_get $node_id address)
local port=$(config_n_get $node_id port)
local temp=""
if [ -z "$balancing_node_address" ]; then
temp="${address}:${port}"
else
temp="${balancing_node_address}\n${address}:${port}"
fi
balancing_node_address="$temp"
done
elif [ "$type" == "v2ray" -o "$type" == "v2ray_balancing" -o "$type" == "v2ray_shunt" ]; then
lua $API_GEN_V2RAY $node nil nil $local_port >$config_file
ln_start_bin $(config_t_get global_app v2ray_file $(find_bin v2ray))/v2ray v2ray "-config=$config_file"
elif [ "$type" == "trojan" ]; then
@ -361,24 +344,7 @@ gen_start_config() {
# local redsocks_config_file=$CONFIG_PATH/UDP_$i.conf
# gen_redsocks_config $redsocks_config_file udp $port $node_address $node_port $server_username $server_password
# ln_start_bin $(find_bin redsocks2) redsocks2 "-c $redsocks_config_file"
elif [ "$type" == "v2ray" ]; then
lua $API_GEN_V2RAY $node udp $local_port nil >$config_file
ln_start_bin $(config_t_get global_app v2ray_file $(find_bin v2ray))/v2ray v2ray "-config=$config_file"
elif [ "$type" == "v2ray_balancing" ]; then
local balancing_node=$(config_n_get $node v2ray_balancing_node)
balancing_node_address=""
for node_id in $balancing_node
do
local address=$(config_n_get $node_id address)
local port=$(config_n_get $node_id port)
local temp=""
if [ -z "$balancing_node_address" ]; then
temp="${address}:${port}"
else
temp="${balancing_node_address}\n${address}:${port}"
fi
balancing_node_address="$temp"
done
elif [ "$type" == "v2ray" -o "$type" == "v2ray_balancing" -o "$type" == "v2ray_shunt" ]; then
lua $API_GEN_V2RAY $node udp $local_port nil >$config_file
ln_start_bin $(config_t_get global_app v2ray_file $(find_bin v2ray))/v2ray v2ray "-config=$config_file"
elif [ "$type" == "trojan" ]; then
@ -436,24 +402,7 @@ gen_start_config() {
# local redsocks_config_file=$CONFIG_PATH/TCP_$i.conf
# gen_redsocks_config $redsocks_config_file tcp $port $node_address $socks5_port $server_username $server_password
# ln_start_bin $(find_bin redsocks2) redsocks2 "-c $redsocks_config_file"
elif [ "$type" == "v2ray" ]; then
lua $API_GEN_V2RAY $node tcp $local_port nil >$config_file
ln_start_bin $(config_t_get global_app v2ray_file $(find_bin v2ray))/v2ray v2ray "-config=$config_file"
elif [ "$type" == "v2ray_balancing" ]; then
local balancing_node=$(config_n_get $node v2ray_balancing_node)
balancing_node_address=""
for node_id in $balancing_node
do
local address=$(config_n_get $node_id address)
local port=$(config_n_get $node_id port)
local temp=""
if [ -z "$balancing_node_address" ]; then
temp="${address}:${port}"
else
temp="${balancing_node_address}\n${address}:${port}"
fi
balancing_node_address="$temp"
done
elif [ "$type" == "v2ray" -o "$type" == "v2ray_balancing" -o "$type" == "v2ray_shunt" ]; then
lua $API_GEN_V2RAY $node tcp $local_port nil >$config_file
ln_start_bin $(config_t_get global_app v2ray_file $(find_bin v2ray))/v2ray v2ray "-config=$config_file"
elif [ "$type" == "trojan" ]; then
@ -675,6 +624,11 @@ add_dnsmasq() {
mkdir -p $TMP_DNSMASQ_PATH $DNSMASQ_PATH /var/dnsmasq.d
cat $RULE_PATH/whitelist_host | sed -e "/^$/d" | sed "s/^/ipset=&\/./g" | sed "s/$/\/&whitelist/g" | sort | awk '{if ($0!=line) print;line=$0}' > $TMP_DNSMASQ_PATH/whitelist_host.conf
local adblock=$(config_t_get global_rules adblock 0)
[ "$adblock" == "1" ] && {
[ -f "$RULE_PATH/adblock.conf" -a -s "$RULE_PATH/adblock.conf" ] && ln -s $RULE_PATH/adblock.conf $TMP_DNSMASQ_PATH/adblock.conf
}
[ "$DNS_MODE" != "nonuse" ] && {
[ -f "$RULE_PATH/blacklist_host" -a -s "$RULE_PATH/blacklist_host" ] && cat $RULE_PATH/blacklist_host | sed -e "/^$/d" | awk '{print "server=/."$1"/127.0.0.1#'$DNS_PORT'\nipset=/."$1"/blacklist"}' > $TMP_DNSMASQ_PATH/blacklist_host.conf
[ -f "$RULE_PATH/router" -a -s "$RULE_PATH/router" ] && cat $RULE_PATH/router | sed -e "/^$/d" | awk '{print "server=/."$1"/127.0.0.1#'$DNS_PORT'\nipset=/."$1"/router"}' > $TMP_DNSMASQ_PATH/router.conf

View File

@ -11,7 +11,6 @@ IPSET_WHITELIST="whitelist"
ipt_n="iptables -t nat"
ipt_m="iptables -t mangle"
ip6t_n="ip6tables -t nat"
ipt_comment="-m comment --comment PassWall"
factor() {
if [ -z "$1" ] || [ -z "$2" ]; then
@ -44,6 +43,9 @@ dst() {
echo "-m set --match-set $1 dst"
}
comment() {
echo "-m comment --comment '$1'"
}
get_action_chain() {
case "$1" in
@ -146,31 +148,31 @@ load_acl() {
fi
if [ "$proxy_mode" == "disable" ]; then
$ipt_n -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p tcp -m comment --comment "$remarks" -j RETURN
$ipt_m -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p udp -m comment --comment "$remarks" -j RETURN
$ipt_n -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p tcp $(comment "$remarks") -j RETURN
$ipt_m -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p udp $(comment "$remarks") -j RETURN
else
[ "$TCP_NODE" != "nil" ] && {
eval TCP_NODE_TYPE=$(echo $(config_get $node type) | tr 'A-Z' 'a-z')
if [ "$TCP_NODE_TYPE" == "brook" ]; then
[ "$TCP_NO_REDIR_PORTS" != "disable" ] && $ipt_m -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p tcp -m multiport --dport $TCP_NO_REDIR_PORTS -j RETURN
eval tcp_redir_port=\$TCP_REDIR_PORT$tcp_node
$ipt_m -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p tcp $(dst $IPSET_BLACKLIST) $(factor $tcp_redir_ports "-m multiport --dport") -m comment --comment "$remarks" -j TPROXY --tproxy-mark 0x1/0x1 --on-port $tcp_redir_port
$ipt_m -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p tcp $(factor $tcp_redir_ports "-m multiport --dport") -m comment --comment "$remarks" -$(get_jump_mode $proxy_mode) $(get_action_chain $proxy_mode)$tcp_node
$ipt_m -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p tcp -m comment --comment "$remarks" -j RETURN
$ipt_m -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p tcp $(dst $IPSET_BLACKLIST) $(factor $tcp_redir_ports "-m multiport --dport") $(comment "$remarks") -j TPROXY --tproxy-mark 0x1/0x1 --on-port $tcp_redir_port
$ipt_m -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p tcp $(factor $tcp_redir_ports "-m multiport --dport") $(comment "$remarks") -$(get_jump_mode $proxy_mode) $(get_action_chain $proxy_mode)$tcp_node
$ipt_m -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p tcp $(comment "$remarks") -j RETURN
else
[ "$TCP_NO_REDIR_PORTS" != "disable" ] && $ipt_n -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p tcp -m multiport --dport $TCP_NO_REDIR_PORTS -j RETURN
eval tcp_redir_port=\$TCP_REDIR_PORT$tcp_node
$ipt_n -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p tcp $(dst $IPSET_BLACKLIST) $(factor $tcp_redir_ports "-m multiport --dport") -m comment --comment "$remarks" -j REDIRECT --to-ports $tcp_redir_port
$ipt_n -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p tcp $(factor $tcp_redir_ports "-m multiport --dport") -m comment --comment "$remarks" -$(get_jump_mode $proxy_mode) $(get_action_chain $proxy_mode)$tcp_node
$ipt_n -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p tcp -m comment --comment "$remarks" -j RETURN
$ipt_n -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p tcp $(dst $IPSET_BLACKLIST) $(factor $tcp_redir_ports "-m multiport --dport") $(comment "$remarks") -j REDIRECT --to-ports $tcp_redir_port
$ipt_n -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p tcp $(factor $tcp_redir_ports "-m multiport --dport") $(comment "$remarks") -$(get_jump_mode $proxy_mode) $(get_action_chain $proxy_mode)$tcp_node
$ipt_n -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p tcp $(comment "$remarks") -j RETURN
fi
}
[ "$UDP_NODE" != "nil" ] && {
[ "$UDP_NO_REDIR_PORTS" != "disable" ] && $ipt_m -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p udp -m multiport --dport $TCP_NO_REDIR_PORTS -j RETURN
eval udp_redir_port=\$UDP_REDIR_PORT$udp_node
$ipt_m -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p udp $(dst $IPSET_BLACKLIST) -m comment --comment "$remarks" -j TPROXY --on-port $udp_redir_port --tproxy-mark 0x1/0x1
$ipt_m -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p udp $(factor $udp_redir_ports "-m multiport --dport") -m comment --comment "$remarks" -$(get_jump_mode $proxy_mode) $(get_action_chain $proxy_mode)$udp_node
$ipt_m -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p udp -m comment --comment "$remarks" -j RETURN
$ipt_m -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p udp $(dst $IPSET_BLACKLIST) $(comment "$remarks") -j TPROXY --on-port $udp_redir_port --tproxy-mark 0x1/0x1
$ipt_m -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p udp $(factor $udp_redir_ports "-m multiport --dport") $(comment "$remarks") -$(get_jump_mode $proxy_mode) $(get_action_chain $proxy_mode)$udp_node
$ipt_m -A PSW_ACL $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -p udp $(comment "$remarks") -j RETURN
}
fi
[ -z "$ip" ] && {
@ -198,13 +200,43 @@ filter_vpsip() {
if [ -n "$isip" ]; then
ipset -! add $IPSET_VPSIPLIST $isip >/dev/null 2>&1 &
else
has=$(cat $TMP_DNSMASQ_PATH/vpsiplist_host.conf | grep "$server")
has=$([ -f "$TMP_DNSMASQ_PATH/vpsiplist_host.conf" ] && cat $TMP_DNSMASQ_PATH/vpsiplist_host.conf | grep "$server")
[ -z "$has" ] && echo "$server" | sed -e "/^$/d" | sed "s/^/ipset=&\//g" | sed "s/$/\/&vpsiplist/g" | sort | awk '{if ($0!=line) print;line=$0}' >> $TMP_DNSMASQ_PATH/vpsiplist_host.conf
fi
}
}
}
filter_node() {
filter_rules() {
[ -n "$1" -a "$1" != "nil" ] && {
local type=$(echo $(config_get $1 type) | tr 'A-Z' 'a-z')
local i=$ipt_n
[ "$type" == "brook" ] && i=$ipt_m
local address=$(config_get $1 address)
local port=$(config_get $1 port)
is_exist=$($i -L PSW 2>/dev/null | grep -c "$address:$port")
[ "$is_exist" == 0 ] && $i -A PSW -p tcp -d $address --dport $port $(comment "$address:$port") -j RETURN
is_exist=$($i -L PSW_OUTPUT 2>/dev/null | grep -c "$address:$port")
[ "$is_exist" == 0 ] && $i -A PSW_OUTPUT -p tcp -d $address --dport $port $(comment "$address:$port") -j RETURN
}
}
local tmp_type=$(echo $(config_get $1 type) | tr 'A-Z' 'a-z')
if [ "$tmp_type" == "v2ray_shunt" ]; then
filter_rules $(config_get $node youtube_node)
filter_rules $(config_get $node netflix_node)
filter_rules $(config_get $node default_node)
elif [ "$tmp_type" == "v2ray_balancing" ]; then
local balancing_node=$(config_get $node v2ray_balancing_node)
for node_id in $balancing_node
do
filter_rules $node_id
done
else
filter_rules $node
fi
}
dns_hijack() {
dnshijack=$(config_t_get global dns_53)
if [ "$dnshijack" = "1" -o "$1" = "force" ]; then
@ -294,14 +326,7 @@ add_firewall_rule() {
for i in $(seq 1 $SOCKS5_NODE_NUM); do
local k=$i
eval node=\$SOCKS5_NODE$k
if [ "$node" != "nil" ]; then
local SOCKS5_NODE_PORT=$(config_get $node port)
local SOCKS5_NODE_IP=$(get_node_host_ip $node)
[ -n "$SOCKS5_NODE_IP" -a -n "$SOCKS5_NODE_PORT" ] && {
$ipt_n -A PSW -p tcp -d $SOCKS5_NODE_IP --dport $SOCKS5_NODE_PORT -j RETURN
$ipt_n -A PSW_OUTPUT -p tcp -d $SOCKS5_NODE_IP --dport $SOCKS5_NODE_PORT -j RETURN
}
fi
[ "$node" != "nil" ] && filter_node $node
done
fi
@ -313,8 +338,7 @@ add_firewall_rule() {
eval local_port=\$TCP_REDIR_PORT$k
# 生成TCP转发规则
if [ "$node" != "nil" ]; then
local TCP_NODE_PORT=$(config_get $node port)
local TCP_NODE_IP=$(get_node_host_ip $node)
filter_node $node
local TCP_NODE_TYPE=$(echo $(config_get $node type) | tr 'A-Z' 'a-z')
if [ "$TCP_NODE_TYPE" == "brook" ]; then
$ipt_m -A PSW_ACL -p tcp -m socket -j MARK --set-mark 1
@ -356,11 +380,6 @@ add_firewall_rule() {
[ "$k" == 1 ] && {
if [ "$TCP_NODE_TYPE" == "brook" ]; then
[ -n "$TCP_NODE_IP" -a -n "$TCP_NODE_PORT" ] && {
$ipt_m -A PSW -p tcp -d $TCP_NODE_IP --dport $TCP_NODE_PORT -j RETURN
$ipt_m -A PSW_OUTPUT -p tcp -d $TCP_NODE_IP --dport $TCP_NODE_PORT -j RETURN
}
[ "$use_tcp_node_resolve_dns" == 1 -a -n "$DNS_FORWARD" ] && {
for dns in $DNS_FORWARD
do
@ -378,10 +397,6 @@ add_firewall_rule() {
[ "$LOCALHOST_PROXY_MODE" == "gfwlist" ] && $ipt_m -A PSW_OUTPUT -p tcp $(dst $IPSET_GFW) $(factor $TCP_REDIR_PORTS "-m multiport --dport") -j MARK --set-mark 1
[ "$LOCALHOST_PROXY_MODE" == "chnroute" ] && $ipt_m -A PSW_OUTPUT -p tcp -m set ! --match-set $IPSET_CHN dst $(factor $TCP_REDIR_PORTS "-m multiport --dport") -j MARK --set-mark 1
else
[ -n "$TCP_NODE_IP" -a -n "$TCP_NODE_PORT" ] && {
$ipt_n -A PSW -p tcp -d $TCP_NODE_IP --dport $TCP_NODE_PORT -j RETURN
$ipt_n -A PSW_OUTPUT -p tcp -d $TCP_NODE_IP --dport $TCP_NODE_PORT -j RETURN
}
PRE_INDEX=1
KP_INDEX=$($ipt_n -L PREROUTING --line-numbers | grep "KOOLPROXY" | sed -n '$p' | awk '{print $1}')
ADBYBY_INDEX=$($ipt_n -L PREROUTING --line-numbers | grep "ADBYBY" | sed -n '$p' | awk '{print $1}')
@ -453,13 +468,8 @@ add_firewall_rule() {
eval local_port=\$UDP_REDIR_PORT$k
# 生成UDP转发规则
if [ "$node" != "nil" ]; then
local UDP_NODE_PORT=$(config_get $node port)
local UDP_NODE_IP=$(get_node_host_ip $node)
filter_node $node
local UDP_NODE_TYPE=$(echo $(config_get $node type) | tr 'A-Z' 'a-z')
[ -n "$UDP_NODE_IP" -a -n "$UDP_NODE_PORT" ] && {
$ipt_m -A PSW -p udp -d $UDP_NODE_IP --dport $UDP_NODE_PORT -j RETURN
$ipt_m -A PSW_OUTPUT -p udp -d $UDP_NODE_IP --dport $UDP_NODE_PORT -j RETURN
}
[ "$UDP_NODE_TYPE" == "brook" ] && $ipt_m -A PSW_ACL -p udp -m socket -j MARK --set-mark 1
# 全局模式
$ipt_m -A PSW_GLO$k -p udp -j TPROXY --tproxy-mark 0x1/0x1 --on-port $local_port
@ -509,44 +519,31 @@ add_firewall_rule() {
else
echolog "UDP节点未选择无法转发UDP"
fi
if [ -n "$balancing_node_address" ]; then
balancing_node_address=$(echo -e $balancing_node_address)
for balancing_node in $balancing_node_address
do
local ip=$(echo $balancing_node | awk -F ":" '{print $1}')
local port=$(echo $balancing_node | awk -F ":" '{print $2}')
$ipt_n -I PSW 2 -p tcp -d $ip --dport $port -j RETURN
$ipt_n -I PSW_OUTPUT 2 -p tcp -d $ip --dport $port -j RETURN
$ipt_m -I PSW 2 -p udp -d $ip --dport $port -j RETURN
$ipt_m -I PSW_OUTPUT 2 -p udp -d $ip --dport $port -j RETURN
done
fi
# 加载ACLS
config_foreach load_acl "acl_rule"
# 加载默认代理模式
if [ "$PROXY_MODE" == "disable" ]; then
[ "$TCP_NODE1" != "nil" ] && $ipt_n -A PSW_ACL -p tcp -m comment --comment "Default" -j $(get_action_chain $PROXY_MODE)
[ "$UDP_NODE1" != "nil" ] && $ipt_m -A PSW_ACL -p udp -m comment --comment "Default" -j $(get_action_chain $PROXY_MODE)
[ "$TCP_NODE1" != "nil" ] && $ipt_n -A PSW_ACL -p tcp $(comment "Default") -j $(get_action_chain $PROXY_MODE)
[ "$UDP_NODE1" != "nil" ] && $ipt_m -A PSW_ACL -p udp $(comment "Default") -j $(get_action_chain $PROXY_MODE)
else
[ "$TCP_NODE1" != "nil" ] && {
local TCP_NODE_TYPE1=$(echo $(config_get $TCP_NODE1 type) | tr 'A-Z' 'a-z')
if [ "$TCP_NODE_TYPE1" == "brook" ]; then
[ "$TCP_NO_REDIR_PORTS" != "disable" ] && $ipt_m -A PSW_ACL -p tcp -m multiport --dport $TCP_NO_REDIR_PORTS -m comment --comment "Default" -j RETURN
$ipt_m -A PSW_ACL -p tcp $(dst $IPSET_BLACKLIST) $(factor $TCP_REDIR_PORTS "-m multiport --dport") -m comment --comment "Default" -j TPROXY --tproxy-mark 0x1/0x1 --on-port $TCP_REDIR_PORT1
$ipt_m -A PSW_ACL -p tcp $(factor $TCP_REDIR_PORTS "-m multiport --dport") -m comment --comment "Default" -j $(get_action_chain $PROXY_MODE)1
[ "$TCP_NO_REDIR_PORTS" != "disable" ] && $ipt_m -A PSW_ACL -p tcp -m multiport --dport $TCP_NO_REDIR_PORTS $(comment "Default") -j RETURN
$ipt_m -A PSW_ACL -p tcp $(dst $IPSET_BLACKLIST) $(factor $TCP_REDIR_PORTS "-m multiport --dport") $(comment "Default") -j TPROXY --tproxy-mark 0x1/0x1 --on-port $TCP_REDIR_PORT1
$ipt_m -A PSW_ACL -p tcp $(factor $TCP_REDIR_PORTS "-m multiport --dport") $(comment "Default") -j $(get_action_chain $PROXY_MODE)1
else
[ "$TCP_NO_REDIR_PORTS" != "disable" ] && $ipt_n -A PSW_ACL -p tcp -m multiport --dport $TCP_NO_REDIR_PORTS -m comment --comment "Default" -j RETURN
$ipt_n -A PSW_ACL -p tcp $(dst $IPSET_BLACKLIST) $(factor $TCP_REDIR_PORTS "-m multiport --dport") -m comment --comment "Default" -j REDIRECT --to-ports $TCP_REDIR_PORT1
$ipt_n -A PSW_ACL -p tcp $(factor $TCP_REDIR_PORTS "-m multiport --dport") -m comment --comment "Default" -j $(get_action_chain $PROXY_MODE)1
[ "$TCP_NO_REDIR_PORTS" != "disable" ] && $ipt_n -A PSW_ACL -p tcp -m multiport --dport $TCP_NO_REDIR_PORTS $(comment "Default") -j RETURN
$ipt_n -A PSW_ACL -p tcp $(dst $IPSET_BLACKLIST) $(factor $TCP_REDIR_PORTS "-m multiport --dport") $(comment "Default") -j REDIRECT --to-ports $TCP_REDIR_PORT1
$ipt_n -A PSW_ACL -p tcp $(factor $TCP_REDIR_PORTS "-m multiport --dport") $(comment "Default") -j $(get_action_chain $PROXY_MODE)1
fi
}
[ "$UDP_NODE1" != "nil" ] && {
[ "$UDP_NO_REDIR_PORTS" != "disable" ] && $ipt_m -A PSW_ACL -p udp -m multiport --dport $UDP_NO_REDIR_PORTS -m comment --comment "Default" -j RETURN
$ipt_m -A PSW_ACL -p udp $(dst $IPSET_BLACKLIST) $(factor $UDP_REDIR_PORTS "-m multiport --dport") -m comment --comment "Default" -j TPROXY --on-port $UDP_REDIR_PORT1 --tproxy-mark 0x1/0x1
$ipt_m -A PSW_ACL -p udp $(factor $UDP_REDIR_PORTS "-m multiport --dport") -m comment --comment "Default" -j $(get_action_chain $PROXY_MODE)1
[ "$UDP_NO_REDIR_PORTS" != "disable" ] && $ipt_m -A PSW_ACL -p udp -m multiport --dport $UDP_NO_REDIR_PORTS $(comment "Default") -j RETURN
$ipt_m -A PSW_ACL -p udp $(dst $IPSET_BLACKLIST) $(factor $UDP_REDIR_PORTS "-m multiport --dport") $(comment "Default") -j TPROXY --on-port $UDP_REDIR_PORT1 --tproxy-mark 0x1/0x1
$ipt_m -A PSW_ACL -p udp $(factor $UDP_REDIR_PORTS "-m multiport --dport") $(comment "Default") -j $(get_action_chain $PROXY_MODE)1
}
fi

View File

@ -0,0 +1,18 @@
# Copyright (C) 2018-2020 Lienol <lawlienol@gmail.com>
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for PPTP VPN Server
LUCI_DEPENDS:=+pptpd +kmod-mppe +ppp
LUCI_PKGARCH:=all
PKG_VERSION:=1.0
PKG_RELEASE:=12-20190704
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -0,0 +1,24 @@
-- Copyright 2018-2019 Lienol <lawlienol@gmail.com>
module("luci.controller.pptpd", package.seeall)
function index()
if not nixio.fs.access("/etc/config/pptpd") then return end
entry({"admin", "vpn"}, firstchild(), "VPN", 45).dependent = false
entry({"admin", "vpn", "pptpd"}, alias("admin", "vpn", "pptpd", "settings"),
_("PPTP VPN Server"), 48)
entry({"admin", "vpn", "pptpd", "settings"}, cbi("pptpd/settings"),
_("General Settings"), 10).leaf = true
entry({"admin", "vpn", "pptpd", "users"}, cbi("pptpd/users"),
_("Users Manager"), 20).leaf = true
entry({"admin", "vpn", "pptpd", "online"}, cbi("pptpd/online"),
_("Online Users"), 30).leaf = true
entry({"admin", "vpn", "pptpd", "status"}, call("status")).leaf = true
end
function status()
local e = {}
e.status = luci.sys.call("pidof %s >/dev/null" % "pptpd") == 0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end

View File

@ -0,0 +1,80 @@
local e = {}
local o = require "luci.dispatcher"
local a = luci.util.execi("/bin/busybox top -bn1 | grep '/usr/sbin/pppd'")
for t in a do
local a, n, h, s, o, i = t:match(
"^ *(%d+) +(%d+) +.+options%.pptpd +(%d+) +(%S.-%S)%:(%S.-%S) +.+ +(.+)")
local t = tonumber(a)
if t then
e["%02i.%s" % {t, "online"}] = {
['PID'] = a,
['PPID'] = n,
['SPEED'] = h,
['GATEWAY'] = s,
['VIP'] = o,
['CIP'] = i,
['BLACKLIST'] = 0
}
end
end
local a = luci.util.execi("sed = /etc/firewall.user | sed 'N;s/\\n/:/'")
for t in a do
local t, a = t:match("^ *(%d+)%:.+%#%# pptpd%-blacklist%-(.+)")
local t = tonumber(t)
if t then
e["%02i.%s" % {t, "blacklist"}] =
{
['PID'] = "-1",
['PPID'] = "-1",
['SPEED'] = "-1",
['GATEWAY'] = "-",
['VIP'] = "-",
['CIP'] = a,
['BLACKLIST'] = 1
}
end
end
f = SimpleForm("processes", translate("PPTP VPN Server"))
f.reset = false
f.submit = false
f.description = translate(
"Simple, quick and convenient PPTP VPN, universal across the platform")
t = f:section(Table, e, translate("Online Users"))
t:option(DummyValue, "GATEWAY", translate("Server IP"))
t:option(DummyValue, "VIP", translate("Client IP"))
t:option(DummyValue, "CIP", translate("IP address"))
blacklist = t:option(Button, "_blacklist", translate("Blacklist"))
function blacklist.render(e, t, a)
if e.map:get(t, "BLACKLIST") == 0 then
e.title = translate("Add to Blacklist")
e.inputstyle = "remove"
else
e.title = translate("Remove from Blacklist")
e.inputstyle = "apply"
end
Button.render(e, t, a)
end
function blacklist.write(t, a)
local e = t.map:get(a, "CIP")
if t.map:get(a, "BLACKLIST") == 0 then
luci.util.execi(
"echo 'iptables -A input_rule -s %s -p tcp --dport 1723 -j DROP ## pptpd-blacklist-%s' >> /etc/firewall.user" %
{e, e})
luci.util.execi(
"iptables -A input_rule -s %s -p tcp --dport 1723 -j DROP" % {e})
null, t.tag_error[a] = luci.sys.process.signal(t.map:get(a, "PID"), 9)
else
luci.util.execi(
"sed -i -e '/## pptpd-blacklist-%s/d' /etc/firewall.user" % {e})
luci.util.execi(
"iptables -D input_rule -s %s -p tcp --dport 1723 -j DROP" % {e})
end
luci.http.redirect(o.build_url("admin/vpn/pptpd/online"))
end
kill = t:option(Button, "_kill", translate("Forced offline"))
kill.inputstyle = "reset"
function kill.write(e, t)
null, e.tag_error[t] = luci.sys.process.signal(e.map:get(t, "PID"), 9)
luci.http.redirect(o.build_url("admin/vpn/pptpd/online"))
end
return f

View File

@ -0,0 +1,61 @@
local s = require "luci.sys"
local net = require"luci.model.network".init()
local ifaces = s.net:devices()
local m, s, o
m = Map("pptpd", translate("PPTP VPN Server"))
m.description = translate(
"Simple, quick and convenient PPTP VPN, universal across the platform")
m.template = "pptpd/index"
s = m:section(TypedSection, "service")
s.anonymous = true
o = s:option(DummyValue, "pptpd_status", translate("Current Condition"))
o.template = "pptpd/status"
o.value = translate("Collecting data...")
o = s:option(Flag, "enabled", translate("Enable VPN Server"))
o.rmempty = false
o = s:option(Value, "localip", translate("Server IP"),
translate("VPN Server IP address, it not required."))
o.datatype = "ipaddr"
o.placeholder = translate("192.168.1.2")
o.rmempty = true
o.default = "192.168.1.2"
o = s:option(Value, "remoteip", translate("Client IP"),
translate("VPN Client IP address, it not required."))
o.placeholder = translate("192.168.1.10-20")
o.rmempty = true
o.default = "192.168.1.10-20"
o = s:option(Value, "dns", translate("DNS IP address"),
translate("This will be sent to the client, it not required."))
o.placeholder = translate("192.168.1.1")
o.datatype = "ipaddr"
o.rmempty = true
o.default = "192.168.1.1"
o = s:option(Flag, "mppe", translate("Enable MPPE Encryption"),
translate("Allows 128-bit encrypted connection."))
o.rmempty = false
o = s:option(Flag, "is_nat", translate("is_nat"))
o.rmempty = false
o = s:option(ListValue, "export_interface", translate("Interface"),
translate("Specify interface forwarding traffic."))
o:value("default", translate("default"))
for _, iface in ipairs(ifaces) do
if (iface:match("^br*") or iface:match("^eth*") or iface:match("^pppoe*") or
iface:match("wlan*")) then
local nets = net:get_interface(iface)
nets = nets and nets:get_networks() or {}
for k, v in pairs(nets) do nets[k] = nets[k].sid end
nets = table.concat(nets, ",")
o:value(iface, ((#nets > 0) and "%s (%s)" % {iface, nets} or iface))
end
end
o:depends("is_nat", "1")
return m

View File

@ -0,0 +1,24 @@
m = Map("pptpd", translate("PPTP VPN Server"))
m.description = translate(
"Simple, quick and convenient PPTP VPN, universal across the platform")
s = m:section(TypedSection, "users", translate("Users Manager"))
s.addremove = true
s.anonymous = true
s.template = "cbi/tblsection"
o = s:option(Flag, "enabled", translate("Enabled"))
o.rmempty = false
o = s:option(Value, "username", translate("User name"))
o.placeholder = translate("User name")
o.rmempty = true
o = s:option(Value, "password", translate("Password"))
o.rmempty = true
o = s:option(Value, "ipaddress", translate("IP address"))
o.placeholder = translate("Automatically")
o.datatype = "ipaddr"
o.rmempty = true
function o.cfgvalue(e, t)
value = e.map:get(t, "ipaddress")
return value == "*" and "" or value
end
function o.remove(e, t) Value.write(e, t, "*") end
return m

View File

@ -0,0 +1,13 @@
<% include("cbi/map") %>
<script type="text/javascript">//<![CDATA[
XHR.poll(2, '<%=luci.dispatcher.build_url("admin", "vpn", "pptpd", "status")%>', null,
function(x, result)
{
var status = document.getElementsByClassName('pptpd_status')[0];
status.setAttribute("style","font-weight:bold;");
status.setAttribute("color",result.status ? "green":"red");
status.innerHTML = result.status?'<%=translate("RUNNING")%>':'<%=translate("NOT RUNNING")%>';
}
)
//]]>
</script>

View File

@ -0,0 +1,3 @@
<%+cbi/valueheader%>
<font class="pptpd_status"><%=pcdata(self:cfgvalue(section) or self.default or "")%></font>
<%+cbi/valuefooter%>

View File

@ -0,0 +1,89 @@
msgid "PPTP VPN Server"
msgstr "PPTP VPN 服务器"
msgid "Simple, quick and convenient PPTP VPN, universal across the platform"
msgstr "简单快捷方便的PPTP VPN全平台通用。"
msgid "PPTP VPN Server status"
msgstr "PPTP VPN 服务器运行状态"
msgid "Current Condition"
msgstr "当前状态"
msgid "General settings"
msgstr "基本设置"
msgid "Enable VPN Server"
msgstr "启用 VPN 服务器"
msgid "Server IP"
msgstr "服务器 IP 地址"
msgid "VPN Server IP address, it not required."
msgstr "VPN 服务器远程地址,留空将自动设置。"
msgid "Client IP"
msgstr "客户端 IP 地址"
msgid "VPN Client IP address, it not required."
msgstr "分配给客户端的 IP 地址范围,留空将自动设置。"
msgid "DNS IP address"
msgstr "DNS IP 地址"
msgid "This will be sent to the client, it not required."
msgstr "设置 VPN 服务器默认 DNS 服务器,该设置非必须。"
msgid "Enable MPPE Encryption"
msgstr "启用MPPE 加密"
msgid "Allows 128-bit encrypted connection."
msgstr "允许使用 128 位加密连接。"
msgid "is_nat"
msgstr "NAT转发"
msgid "Interface"
msgstr "接口"
msgid "Specify interface forwarding traffic."
msgstr "指定接口转发流量。"
msgid "Users Manager"
msgstr "用户管理"
msgid "Enabled"
msgstr "启用"
msgid "User name"
msgstr "用户名"
msgid "Password"
msgstr "密码"
msgid "IP address"
msgstr "IP 地址"
msgid "Automatically"
msgstr "自动分配"
msgid "Online Users""
msgstr "在线用户"
msgid "Blacklist"
msgstr "黑名单"
msgid "Add to Blacklist"
msgstr "加入黑名单"
msgid "Remove from Blacklist"
msgstr "移出黑名单"
msgid "Forced offline"
msgstr "强制下线"
msgid "NOT RUNNING"
msgstr "未运行"
msgid "RUNNING"
msgstr "运行中"

View File

@ -0,0 +1,16 @@
config service 'pptpd'
option mppe '1'
option localip '192.168.2.1'
option remoteip '192.168.2.10-20'
option dns '192.168.0.2'
option is_nat '1'
option export_interface 'default'
option enabled '0'
config users
option enabled '1'
option ipaddress '*'
option username 'guest'
option password '123456'

View File

@ -0,0 +1,95 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2018-2019 Lienol <lawlienol@gmail.com>
START=99
CONFIG=pptpd
CONFIG_FILE=/var/etc/$CONFIG.conf
BIN=/usr/sbin/$CONFIG
DEFAULT=/etc/default/$BIN
RUN_D=/var/run
PID_F=$RUN_D/$BIN.pid
CHAP_SECRETS=/var/etc/chap-secrets
SERVER_NAME="pptp-server"
TEMP=/tmp/pptpd.tmp
setup_dns() {
[ -n "$1" ] || return 0
echo ms-dns $1>>/etc/ppp/options.pptpd
}
setup_login() {
config_get enabled $1 enabled
[ "$enabled" -eq 0 ] && return 0
config_get ipaddress $1 ipaddress
[ -n "$ipaddress" ] || local ipaddress = "*"
config_get username $1 username
config_get password $1 password
[ -n "$username" ] || return 0
[ -n "$password" ] || return 0
echo "$username $SERVER_NAME $password $ipaddress" >> $CHAP_SECRETS
}
setup_config() {
config_get enabled $1 enabled
[ "$enabled" -eq 0 ] && return 1
mkdir -p /var/etc
cp /etc/pptpd.conf $CONFIG_FILE
config_get localip $1 localip
config_get remoteip $1 remoteip
config_get is_nat $1 is_nat
[ -z "$localip" ] && localip=$(ifconfig br-lan 2>/dev/null | grep "inet addr:" | grep -E -o "[0-9]+\.[0-9]+\.[0-9]+\."|head -1)2
[ -z "$localip" ] && remoteip=$(ifconfig br-lan 2>/dev/null | grep "inet addr:" | grep -E -o "[0-9]+\.[0-9]+\.[0-9]+\."|head -1)10-20
options="cat /etc/pptpd.conf |grep options"
[ -n "$localip" ] && echo "localip $localip" >> $CONFIG_FILE
[ -n "$remoteip" ] && echo "remoteip $remoteip" >> $CONFIG_FILE
[ -n "$options" ] && echo "option /etc/ppp/options.pptpd" >> $CONFIG_FILE
fw3 reload
config_get mppe $1 mppe
[ -n "$(cat "/etc/ppp/options.pptpd" |grep mppe)" ] && sed -i '/mppe/'d /etc/ppp/options.pptpd
[ -z "$(cat "/etc/ppp/options.pptpd" |grep mppe)" ] && echo "mppe required,no40,no56,stateless" >> /etc/ppp/options.pptpd
if [ "$mppe" -eq 0 ]; then
sed -i -e 's/mppe/#mppe/g' /etc/ppp/options.pptpd
fi
sed -i -e '/ms-dns/d' /etc/ppp/options.pptpd
config_get dns $1 dns
setup_dns $dns
echo ms-dns 223.5.5.5 >>/etc/ppp/options.pptpd
return 0
}
start_pptpd() {
[ -f $DEFAULT ] && . $DEFAULT
mkdir -p $RUN_D
for m in arc4 sha1_generic slhc crc-ccitt ppp_generic ppp_async ppp_mppe; do
insmod $m >/dev/null 2>&1
done
ln -sfn $CHAP_SECRETS /etc/ppp/chap-secrets
chmod 600 /etc/ppp/*-secrets
service_start $BIN $OPTIONS -c $CONFIG_FILE
}
del_user()
{
cat $CHAP_SECRETS | grep -v $SERVER_NAME > $TEMP
cat $TEMP > $CHAP_SECRETS
rm $TEMP
}
start() {
config_load $CONFIG
setup_config $CONFIG || return
del_user
config_foreach setup_login users
start_pptpd
}
stop() {
service_stop $BIN
ps | grep "pppd local" | grep -v "grep" | awk '{print $1}' | xargs kill -9
fw3 reload
del_user
}

View File

@ -0,0 +1,21 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete firewall.pptpd
set firewall.pptpd=include
set firewall.pptpd.type=script
set firewall.pptpd.path=/usr/share/pptpd/firewall.include
set firewall.pptpd.reload=1
EOF
uci -q batch <<-EOF >/dev/null
delete ucitrack.@pptpd[-1]
add ucitrack pptpd
set ucitrack.@pptpd[-1].init=pptpd
commit ucitrack
EOF
chmod a+x /usr/share/pptpd/* >/dev/null 2>&1
rm -f /tmp/luci-indexcache
exit 0

View File

@ -0,0 +1,45 @@
#!/bin/sh
iptables -D INPUT -p tcp --dport 1723 -m comment --comment "Rule For PPTP VPN Server" -j ACCEPT 2> /dev/null
pptp_nums=`iptables -t nat -L POSTROUTING 2> /dev/null|grep -c "Rule For PPTP VPN Server"`
if [ -n "$pptp_nums" ]; then
until [ "$pptp_nums" = 0 ]
do
pptp_rules=`iptables -t nat -L POSTROUTING --line-num 2> /dev/null|grep "Rule For PPTP VPN Server" |awk '{print $1}'`
for pptp_rule in $pptp_rules
do
iptables -t nat -D POSTROUTING $pptp_rule 2> /dev/null
break
done
pptp_nums=`expr $pptp_nums - 1`
done
fi
nums=`iptables -L forwarding_rule 2> /dev/null|grep -c "Rule For PPTP VPN Server"`
if [ -n "$nums" ]; then
until [ "$nums" = 0 ]
do
rules=`iptables -L forwarding_rule --line-num 2> /dev/null|grep "Rule For PPTP VPN Server" |awk '{print $1}'`
for rule in $rules
do
iptables -D forwarding_rule $rule 2> /dev/null
break
done
nums=`expr $nums - 1`
done
fi
enable=$(uci get pptpd.pptpd.enabled)
if [ $enable -eq 1 ]; then
is_nat=$(uci get pptpd.pptpd.is_nat)
if [ "$is_nat" -eq 1 ];then
localip=$(uci get pptpd.pptpd.localip)
export_interface=$(uci get pptpd.pptpd.export_interface)
if [ "$export_interface" != "default" ];then
iptables -t nat -I POSTROUTING -s ${localip%.*}.0/24 -o ${export_interface} -m comment --comment "Rule For PPTP VPN Server" -j MASQUERADE
else
iptables -t nat -I POSTROUTING -s ${localip%.*}.0/24 -m comment --comment "Rule For PPTP VPN Server" -j MASQUERADE
fi
iptables -I forwarding_rule -s ${localip%.*}.0/24 -m comment --comment "Rule For PPTP VPN Server" -j ACCEPT
fi
iptables -I INPUT -p tcp --dport 1723 -m comment --comment "Rule For PPTP VPN Server" -j ACCEPT 2>/dev/null
fi

View File

@ -0,0 +1,18 @@
# Copyright (C) 2018-2020 Lienol <lawlienol@gmail.com>
#
# This is free software, licensed under the GNU General Public License v3.
#
include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for SSR Python Pro Server
LUCI_DEPENDS:=+libsodium +luci-lib-jsonc +python
LUCI_PKGARCH:=all
PKG_VERSION:=1.0
PKG_RELEASE:=6-20190704
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -0,0 +1,96 @@
-- Copyright 2018-2019 Lienol <lawlienol@gmail.com>
module("luci.controller.ssr_python_pro_server", package.seeall)
local http = require "luci.http"
function index()
if not nixio.fs.access("/etc/config/ssr_python_pro_server") then return end
entry({"admin", "vpn"}, firstchild(), "VPN", 45).dependent = false
if nixio.fs.access("/usr/share/ssr_python_pro_server") then
entry({"admin", "vpn", "ssr_python_pro_server"},
cbi("ssr_python_pro_server/index"), _("SSR Python Server"), 2).dependent =
true
end
entry({"admin", "vpn", "ssr_python_pro_server", "config"},
cbi("ssr_python_pro_server/config")).leaf = true
entry({"admin", "vpn", "ssr_python_pro_server", "status"},
call("act_ssr_python_status")).leaf = true
entry({"admin", "vpn", "ssr_python_pro_server", "users_status"},
call("act_ssr_python_users_status")).leaf = true
entry({"admin", "vpn", "ssr_python_pro_server", "get_total_traffic"},
call("act_ssr_python_get_total_traffic")).leaf = true
entry({"admin", "vpn", "ssr_python_pro_server", "get_link"},
call("act_ssr_python_get_link")).leaf = true
entry({"admin", "vpn", "ssr_python_pro_server", "clear_traffic"},
call("act_ssr_python_clear_traffic")).leaf = true
entry({"admin", "vpn", "ssr_python_pro_server", "clear_traffic_all_users"},
call("act_ssr_python_clear_traffic_all_users")).leaf = true
end
local function http_write_json(content)
http.prepare_content("application/json")
http.write_json(content or {code = 1})
end
function act_ssr_python_status()
local e = {}
e.status = luci.sys.call(
"ps -w | grep -v grep | grep '/usr/share/ssr_python_pro_server/server.py' >/dev/null") ==
0
http_write_json(e)
end
function act_ssr_python_users_status()
local e = {}
e.index = luci.http.formvalue("index")
e.status = luci.sys.call("netstat -an | grep '" ..
luci.http.formvalue("port") .. "' >/dev/null") ==
0
http_write_json(e)
end
function act_ssr_python_get_total_traffic()
local e = {}
local result = nil
local total_traffic_str = luci.sys.exec(
"cd /usr/share/ssr_python_pro_server && ./mujson_mgr.py -l -I " ..
luci.http.formvalue("section") ..
" | sed -n 19p"):gsub("^%s*(.-)%s*$", "%1")
local total_traffic = luci.sys.exec("echo " .. total_traffic_str ..
" | awk '{print $3}'"):gsub(
"^%s*(.-)%s*$", "%1")
if total_traffic == "" then total_traffic = 0 end
local unit = luci.sys.exec("echo " .. total_traffic_str ..
" | awk '{print $4}'"):gsub("^%s*(.-)%s*$",
"%1")
result = string.format("%0.2f", total_traffic) .. unit
e.result = result
http_write_json(e)
end
function act_ssr_python_get_link()
local e = {}
local link = luci.sys.exec(
"cd /usr/share/ssr_python_pro_server && ./mujson_mgr.py -l -I " ..
luci.http.formvalue("section") .. " | sed -n 21p"):gsub(
"^%s*(.-)%s*$", "%1")
if link ~= "" then e.link = link end
http_write_json(e)
end
function act_ssr_python_clear_traffic()
local e = {}
e.status = luci.sys.call(
"cd /usr/share/ssr_python_pro_server && ./mujson_mgr.py -c -I '" ..
luci.http.formvalue("id") .. "' >/dev/null") == 0
http_write_json(e)
end
function act_ssr_python_clear_traffic_all_users()
local e = {}
e.status = luci.sys.call(
"/usr/share/ssr_python_pro_server/sh/clear_traffic_all_users.sh >/dev/null") ==
0
http_write_json(e)
end

View File

@ -0,0 +1,79 @@
local i = "ssr_python_pro_server"
local d = require "luci.dispatcher"
local a, t, e
local methods = {
"none", "table", "rc4", "rc4-md5", "aes-128-cfb", "aes-192-cfb",
"aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "bf-cfb",
"cast5-cfb", "des-cfb", "rc2-cfb", "salsa20", "chacha20", "chacha20-ietf"
}
local protocols = {
"origin", "verify_simple", "verify_deflate", "verify_sha1", "auth_simple",
"auth_sha1", "auth_sha1_v2", "auth_sha1_v4", "auth_aes128_md5",
"auth_aes128_sha1", "auth_chain_a", "auth_chain_b", "auth_chain_c",
"auth_chain_d"
}
local obfss = {
"plain", "http_simple", "http_post", "random_head", "tls_simple",
"tls1.0_session_auth", "tls1.2_ticket_auth"
}
a = Map(i, "ShadowsocksR Python " .. translate("Server Config"))
a.redirect = d.build_url("admin", "vpn", "ssr_python_pro_server")
t = a:section(NamedSection, arg[1], "user", "")
t.addremove = false
t.dynamic = false
e = t:option(Flag, "enable", translate("Enable"))
e.default = "1"
e.rmempty = false
e = t:option(Value, "remarks", translate("Remarks"))
e.default = translate("Remarks")
e.rmempty = false
e = t:option(Value, "port", translate("Port"))
e.datatype = "port"
e.rmempty = false
e = t:option(Value, "password", translate("Password"))
e.password = true
e.rmempty = false
e = t:option(ListValue, "method", translate("Encrypt Method"))
for a, t in ipairs(methods) do e:value(t) end
e = t:option(ListValue, "protocol", translate("Protocol"))
for a, t in ipairs(protocols) do e:value(t) end
e = t:option(ListValue, "obfs", translate("Obfs"))
for a, t in ipairs(obfss) do e:value(t) end
e = t:option(Value, "device_limit", translate("Device Limit"), translate(
"Number of clients that can be linked at the same time (multi-port mode, each port is calculated independently), a minimum of 2 is recommended."))
e.default = "2"
e.rmempty = false
e = t:option(Value, "speed_limit_per_con", translate("Speed Limit Per Con"),
translate(
"Single thread speed limit upper limit, multithreading is invalid. Zero means no speed limit. (unit: KB/S)"))
e.default = "0"
e.rmempty = false
e = t:option(Value, "speed_limit_per_user", translate("Speed Limit Per User"),
translate(
"Total speed limit upper limit, single port overall speed limit. Zero means no speed limit. (unit: KB/S)"))
e.default = "0"
e.rmempty = false
e = t:option(Value, "forbidden_port", translate("Forbidden Port"), translate(
"For example, if port 25 is not allowed, the user will not be able to access the mail port 25 through the SSR agent. If 80,443 is disabled, the user will not be able to access the HTTP/HTTPS website normally. <br>blocked single port format: 25<br>blocked multiple port format: 23,465<br>blocked port format: 233-266<br>blocked multiple port format: 25,465,233-666"))
e = t:option(Value, "transfer_enable", translate("Available Total Flow"),
translate(
"Maximum amount of total traffic available (GB, 1-838868), Zero means infinite."))
e.default = "0"
e.rmempty = false
return a

View File

@ -0,0 +1,71 @@
local uci = require"luci.model.uci".cursor()
local sys = require "luci.sys"
local jsonc = require "luci.jsonc"
local json = {}
function genconfig(i, section, d, u)
local server = uci:get_all("ssr_python_pro_server", section)
local enable = server.enable
local remarks = server.remarks
local port = server.port
local password = server.password
local method = server.method
local protocol = server.protocol
local obfs = server.obfs
local device_limit = server.device_limit
local speed_limit_per_con = server.speed_limit_per_con
local speed_limit_per_user = server.speed_limit_per_user
local forbidden_port = server.forbidden_port
local transfer_enable = server.transfer_enable
transfer_enable = transfer_enable and tonumber(transfer_enable) == 0 and
838868 or transfer_enable
json[i] = {
id = section,
enable = tonumber(enable),
user = remarks,
port = tonumber(port),
passwd = password,
method = method,
protocol = protocol,
obfs = obfs,
protocol_param = device_limit,
speed_limit_per_con = tonumber(speed_limit_per_con),
speed_limit_per_user = tonumber(speed_limit_per_user),
forbidden_port = forbidden_port and forbidden_port or "",
transfer_enable = transfer_enable and transfer_enable * 1024 * 1024 *
1024 or 1073741824,
d = d and tonumber(d) or 0,
u = u and tonumber(u) or 0
}
end
local mudbjson = luci.sys.exec("cat /usr/share/ssr_python_pro_server/mudb.json")
local mudbjson_object = jsonc.parse(mudbjson)
local i = 0
uci:foreach("ssr_python_pro_server", "user", function(s)
i = i + 1
local section = s[".name"]
if mudbjson_object then
local flag = true
for index = 1, table.maxn(mudbjson_object) do
local object = mudbjson_object[index]
if mudbjson_object[index] ~= nil then
if object.id == section then
flag = false
genconfig(i, section, object.d, object.u)
mudbjson_object[index] = nil
break
end
end
end
if flag == true then genconfig(i, section, 0, 0) end
else
genconfig(i, section, 0, 0)
end
end)
print(jsonc.stringify(json, 1))

View File

@ -0,0 +1,186 @@
local o = require "luci.dispatcher"
local fs = require "nixio.fs"
local sys = require "luci.sys"
local cursor = luci.model.uci.cursor()
local appname = "ssr_python_pro_server"
local a, t, e
a = Map(appname, translate("ShadowsocksR Python Server"))
t = a:section(TypedSection, "global", translate("Global Settings"))
t.anonymous = true
t.addremove = false
e = t:option(DummyValue, "status", translate("Current Condition"))
e.template = appname .. "/status"
e.value = translate("Collecting data...")
e = t:option(Flag, "enable", translate("Enable"))
e.rmempty = false
e =
t:option(Flag, "auto_clear_transfer", translate("Enable Auto Clear Traffic"))
e.default = 0
e.rmempty = false
e = t:option(Value, "auto_clear_transfer_time",
translate("Clear Traffic Time Interval"),
translate("*,*,*,*,* is Min Hour Day Mon Week"))
e.default = "0,2,1,*,*"
e:depends("auto_clear_transfer", 1)
e = t:option(Button, "clear_transfer", translate("Clear All Users Traffic"))
e.inputstyle = "remove"
function e.write(t, section)
local url = luci.dispatcher.build_url("admin", "vpn",
"ssr_python_pro_server",
"clear_traffic_all_users")
e.description = "<script>if (confirm('确认吗?')==true){XHR.get('" ..
url ..
"',null,function(x,result){window.location.replace(window.location.href)})}</script>"
end
t = a:section(TypedSection, "user", translate("Users Manager"))
t.anonymous = true
t.addremove = true
t.template = "cbi/tblsection"
t.extedit = o.build_url("admin", "vpn", appname, "config", "%s")
function t.create(e, t)
local e = TypedSection.create(e, t)
luci.http.redirect(o.build_url("admin", "vpn", appname, "config", e))
end
function t.remove(t, a)
t.map.proceed = true
t.map:del(a)
luci.http.redirect(o.build_url("admin", "vpn", appname))
end
e = t:option(Flag, "enable", translate("Enable"))
e.width = "5%"
e.rmempty = false
e = t:option(DummyValue, "status", translate("Status"))
e.template = "ssr_python_pro_server/users_status"
e.width = "5%"
e = t:option(DummyValue, "remarks", translate("Remarks"))
e.width = "10%"
e = t:option(DummyValue, "port", translate("Port"))
e.width = "10%"
e = t:option(DummyValue, "forbidden_port", translate("Forbidden Port"))
e.width = "10%"
e.cfgvalue = function(t, n)
local str = translate("Null")
local forbidden_port = a.uci:get(appname, n, "forbidden_port")
if forbidden_port then str = forbidden_port end
return str
end
e = t:option(DummyValue, "device_limit", translate("Device Limit"))
e.width = "10%"
e =
t:option(DummyValue, "speed_limit_per_con", translate("Speed Limit Per Con"))
e.width = "10%"
e.cfgvalue = function(t, section)
local str = translate("No Speed Limit")
local speed_limit_per_con = a.uci:get(appname, section,
"speed_limit_per_con")
if speed_limit_per_con and tonumber(speed_limit_per_con) > 0 then
speed_limit_per_con = tonumber(speed_limit_per_con)
if speed_limit_per_con < 1024 then
str = speed_limit_per_con .. "Kb/s"
elseif speed_limit_per_con < 1024 * 1024 then
str = string.format("%0.2f", speed_limit_per_con / 1024) .. "Mb/s"
end
end
return str
end
e = t:option(DummyValue, "speed_limit_per_user",
translate("Speed Limit Per User"))
e.width = "10%"
e.cfgvalue = function(t, section)
local str = translate("No Speed Limit")
local speed_limit_per_user = a.uci:get(appname, section,
"speed_limit_per_user")
if speed_limit_per_user and tonumber(speed_limit_per_user) > 0 then
speed_limit_per_user = tonumber(speed_limit_per_user)
if speed_limit_per_user < 1024 then
str = speed_limit_per_user .. "Kb/s"
elseif speed_limit_per_user < 1024 * 1024 then
str = string.format("%0.2f", speed_limit_per_user / 1024) .. "Mb/s"
end
end
return str
end
e = t:option(DummyValue, "transfer_enable", translate("Available Total Flow"))
e.width = "10%"
e.cfgvalue = function(t, section)
local str = translate("Infinite")
local transfer_enable = a.uci:get(appname, section, "transfer_enable")
if transfer_enable and tonumber(transfer_enable) > 0 then
str = transfer_enable .. "G"
end
return str
end
--[[e=t:option(DummyValue,"u",translate("Used Upload Traffic"))
e.width="10%"
e.cfgvalue=function(t,section)
local result = translate("Null")
local u_str = luci.sys.exec("cd /usr/share/ssr_python_pro_server && ./mujson_mgr.py -l -I "..section.." | sed -n 10p"):gsub("^%s*(.-)%s*$", "%1")
local u = luci.sys.exec("echo "..u_str.." | awk '{print $3}'"):gsub("^%s*(.-)%s*$", "%1")
if u == "" then u = 0 end
local unit = luci.sys.exec("echo "..u_str.." | awk '{print $4}'"):gsub("^%s*(.-)%s*$", "%1")
result = string.format("%0.2f",u)..unit
return result
end
e=t:option(DummyValue,"d",translate("Used Download Traffic"))
e.width="10%"
e.cfgvalue=function(t,section)
local result = translate("Null")
local d_str = luci.sys.exec("cd /usr/share/ssr_python_pro_server && ./mujson_mgr.py -l -I "..section.." | sed -n 11p"):gsub("^%s*(.-)%s*$", "%1")
local d = luci.sys.exec("echo "..d_str.." | awk '{print $3}'"):gsub("^%s*(.-)%s*$", "%1")
if d == "" then d = 0 end
local unit = luci.sys.exec("echo "..d_str.." | awk '{print $4}'"):gsub("^%s*(.-)%s*$", "%1")
result = string.format("%0.2f",d)..unit
return result
end]] --
e = t:option(DummyValue, "used_total_traffic", translate("Used Total Traffic"))
e.width = "10%"
e.template = appname .. "/users_total_traffic"
--[[e.cfgvalue=function(t,section)
local result = translate("Null")
local total_traffic_str = luci.sys.exec("cd /usr/share/ssr_python_pro_server && ./mujson_mgr.py -l -I "..section.." | sed -n 19p"):gsub("^%s*(.-)%s*$", "%1")
local total_traffic = luci.sys.exec("echo "..total_traffic_str.." | awk '{print $3}'"):gsub("^%s*(.-)%s*$", "%1")
if total_traffic == "" then total_traffic = 0 end
local unit = luci.sys.exec("echo "..total_traffic_str.." | awk '{print $4}'"):gsub("^%s*(.-)%s*$", "%1")
result = string.format("%0.2f",total_traffic)..unit
return result
end]] --
e = t:option(DummyValue, "ssr_link", translate("SSR Link"))
e.width = "10%"
e.template = appname .. "/users_link"
e = t:option(Button, "clear_transfer", translate("Clear Traffic"))
e.inputstyle = "remove"
function e.write(t, section)
local url = luci.dispatcher.build_url("admin", "vpn",
"ssr_python_pro_server",
"clear_traffic")
e.description = "<script>if (confirm('确认吗?')==true){XHR.get('" ..
url .. "',{id:'" .. section ..
"'},function(x,result){window.location.replace(window.location.href)})}</script>"
end
a:append(Template(appname .. "/ssr_python"))
return a

View File

@ -0,0 +1,58 @@
<%
local dsp = require "luci.dispatcher"
local ipkg = require "luci.model.ipkg"
-%>
<% include("cbi/map") %>
<script type="text/javascript">//<![CDATA[
var ssr_python_status = document.getElementsByClassName('ssr_python_status')[0];
ssr_python_status.setAttribute("style","font-weight:bold;");
<% if ipkg.installed("python") then %>
XHR.poll(2, '<%=luci.dispatcher.build_url("admin", "vpn", "ssr_python_pro_server", "status")%>', null,
function(x, result)
{
ssr_python_status.setAttribute("color",result.status ? "green":"red");
ssr_python_status.innerHTML = result.status?'<%=translate("RUNNING")%>':'<%=translate("NOT RUNNING")%>';
}
)
<% else %>
ssr_python_status.setAttribute("color","red");
ssr_python_status.innerHTML = '<%=translate("NOT INSTALLED")%><br><%=translate("please Install the python")%><br>opkg update && opkg install python</font></b>';
<% end %>
function ssr_python_get_total_traffic(section,dom) {
XHR.get('<%=luci.dispatcher.build_url("admin", "vpn", "ssr_python_pro_server", "get_total_traffic")%>', { "section" : section },
function(x, result)
{
if(x && x.status == 200)
dom.outerHTML = result.result;
}
)
}
function ssr_python_get_link(section) {
XHR.get('<%=luci.dispatcher.build_url("admin", "vpn", "ssr_python_pro_server", "get_link")%>', { "section" : section },
function(x, result)
{
if(x && x.status == 200)
alert(result.link);
}
)
}
/* var ssr_python_users_status = document.getElementsByClassName('ssr_python_users_status');
for(var i = 0; i < ssr_python_users_status.length; i++) {
var id_div = ssr_python_users_status[i].parentElement.parentElement.parentElement.id;
var port_div = document.getElementById(id_div+"-port");
var port = port_div.outerText;
XHR.poll(1,'<%=dsp.build_url("admin/vpn/ssr_python_pro_server/users_status")%>', {
index: i,
port: port
},
function(x, result) {
ssr_python_users_status[result.index].innerHTML = (result.status ? '<b><font color="green"></font></b>' : '<b><font color="red">X</font></b>');
}
);
} */
//]]>
</script>

View File

@ -0,0 +1,3 @@
<%+cbi/valueheader%>
<font class="ssr_python_status"><%=pcdata(self:cfgvalue(section) or self.default or "")%></font>
<%+cbi/valuefooter%>

View File

@ -0,0 +1,5 @@
<%+cbi/valueheader%>
<span class="ssr_python_link" hint="<%=self:cfgvalue(section)%>">
<a href="javascript:ssr_python_get_link('<%=section%>')"><%=translate("GET")%></a>
</span>
<%+cbi/valuefooter%>

View File

@ -0,0 +1,18 @@
<%
local uci = require "luci.model.uci".cursor()
function get_status(section)
local port = uci:get("ssr_python_pro_server",section,"port")
if port and port ~= "" then
local status = luci.sys.call("netstat -an | grep '" .. port .. "' >/dev/null")==0
if status then
return '<font color="green"></font>'
else
return '<font color="red"> X </font>'
end
end
end
%>
<%+cbi/valueheader%>
<span class="ssr_python_users_status" hint="<%=self:cfgvalue(section)%>"><%=get_status(section)%></span>
<%+cbi/valuefooter%>

View File

@ -0,0 +1,5 @@
<%+cbi/valueheader%>
<span class="ssr_python_total_traffic" hint="<%=self:cfgvalue(section)%>">
<a href="javascript:void(0)" onclick="javascript:ssr_python_get_total_traffic('<%=section%>',this)"><%=translate("GET")%></a>
</span>
<%+cbi/valuefooter%>

View File

@ -0,0 +1,152 @@
msgid "ShadowsocksR Python Server"
msgstr "ShadowsocksR Python 服务器"
msgid "SSR Python Server"
msgstr "SSR Python 服务器"
msgid "Global Settings"
msgstr "全局设置"
msgid "Server Config"
msgstr "服务器配置"
msgid "Users Manager"
msgstr "用户管理"
msgid "Remarks"
msgstr "备注"
msgid "Port"
msgstr "端口"
msgid "Password"
msgstr "密码"
msgid "Encrypt Method"
msgstr "加密"
msgid "Protocol"
msgstr "协议"
msgid "Protocol Param"
msgstr "协议参数"
msgid "Obfs"
msgstr "混淆"
msgid "Obfs Param"
msgstr "混淆参数"
msgid "Connection Timeout"
msgstr "连接超时时间"
msgid "redirect"
msgstr "重定向"
msgid "Fast Open"
msgstr "快速打开"
msgid "UDP Forward"
msgstr "UDP转发"
msgid "Null"
msgstr "无"
msgid "No Speed Limit"
msgstr "不限速"
msgid "Forbidden Port"
msgstr "禁止的端口"
msgid "Device Limit"
msgstr "设备数限制"
msgid "Speed Limit Per Con"
msgstr "单线程限速"
msgid "Speed Limit Per User"
msgstr "总限速"
msgid "Available Total Flow"
msgstr "可用总流量"
msgid "Infinite"
msgstr "无限"
msgid "Used Upload Traffic"
msgstr "已用上传流量"
msgid "Used Download Traffic"
msgstr "已用下载流量"
msgid "Used Total Traffic"
msgstr "已用总流量"
msgid "SSR Link"
msgstr "SSR链接"
msgid "GET"
msgstr "获取"
msgid "Clear Traffic"
msgstr "清空流量"
msgid "Clear All Users Traffic"
msgstr "清空所有用户流量"
msgid "Enable Auto Clear Traffic"
msgstr "启用自动清空流量"
msgid "Clear Traffic Time Interval"
msgstr "流量清空时间间隔"
msgid "*,*,*,*,* is Min Hour Day Mon Week"
msgstr "*,*,*,*,* 分别对应 分钟 小时 日份 月份 星期<br>0,2,1,*,* 代表 每月1日2点0分<br>0,2,15,*,* 代表 每月15日2点0分<br>0,2,*/7,*,* 代表 每7天2点0分<br>0,2,*,*,0 代表 每个星期日(7)<br>0,2,*,*,3 代表 每个星期三(3)"
msgid "Number of clients that can be linked at the same time (multi-port mode, each port is calculated independently), a minimum of 2 is recommended."
msgstr "同一时间能链接的客户端数量(多端口模式,每个端口都是独立计算),建议最少 2个。"
msgid "Single thread speed limit upper limit, multithreading is invalid. Zero means no speed limit. (unit: KB/S)"
msgstr "单线程的限速上限多线程即无效。0代表不限速。(单位KB/S)"
msgid "Total speed limit upper limit, single port overall speed limit. Zero means no speed limit. (unit: KB/S)"
msgstr "总速度限速上限单个端口整体限速。0代表不限速。(单位KB/S)"
msgid "For example, if port 25 is not allowed, the user will not be able to access the mail port 25 through the SSR agent. If 80,443 is disabled, the user will not be able to access the HTTP/HTTPS website normally. <br>blocked single port format: 25<br>blocked multiple port format: 23,465<br>blocked port format: 233-266<br>blocked multiple port format: 25,465,233-666"
msgstr "例如不允许访问 25端口用户就无法通过SSR代理访问邮件端口25了如果禁止了 80,443 那么用户将无法正常访问 http/https 网站。<br>封禁单个端口格式: 25<br>封禁多个端口格式: 23,465<br>封禁端口段格式: 233-266<br>封禁多种格式端口: 25,465,233-666"
msgid "Maximum amount of total traffic available (GB, 1-838868), Zero means infinite."
msgstr "可使用的总流量上限(单位: GB, 1-838868)0代表无限"
msgid "Alter ID"
msgstr "额外ID(AlterID)"
msgid "User Level"
msgstr "用户等级(Level)"
msgid "Transport"
msgstr "传输方式"
msgid "Camouflage Type"
msgstr "伪装类型"
msgid "Enabled"
msgstr "启用"
msgid "Status"
msgstr "状态"
msgid "Current Condition"
msgstr "当前状态"
msgid "please Install the python"
msgstr "请安装python环境"
msgid "NOT INSTALLED"
msgstr "未安装"
msgid "NOT RUNNING"
msgstr "未运行"
msgid "RUNNING"
msgstr "运行中"

View File

@ -0,0 +1,18 @@
config global
option enable '0'
option auto_clear_transfer '0'
config user
option enable '1'
option remarks 'test'
option port '50005'
option password '123456'
option method 'none'
option protocol 'auth_chain_a'
option obfs 'plain'
option device_limit '10'
option speed_limit_per_con '0'
option speed_limit_per_user '0'
option transfer_enable '0'

View File

@ -0,0 +1,100 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2018-2019 Lienol <lawlienol@gmail.com>
START=99
CONFIG=ssr_python_pro_server
ssr_python_path=/usr/share/$CONFIG
config_t_get() {
local index=0
[ -n "$4" ] && index=$4
local ret=$(uci get $CONFIG.@$1[$index].$2 2>/dev/null)
echo ${ret:=$3}
}
gen_ssr_python_config_file() {
cbi_ids="$cbi_ids $1"
config_get enable $1 enable
config_get remarks $1 remarks
config_get port $1 port
config_get password $1 password
config_get method $1 method
config_get protocol $1 protocol
config_get obfs $1 obfs
config_get device_limit $1 device_limit
config_get speed_limit_per_con $1 speed_limit_per_con
config_get speed_limit_per_user $1 speed_limit_per_user
config_get forbidden_port $1 forbidden_port
config_get transfer_enable $1 transfer_enable
[ "$transfer_enable" = "0" ] && transfer_enable="838868"
cd $ssr_python_path
exist=$(echo `./mujson_mgr.py -l -I $1`)
action="-e"
[ -z "$exist" ] && action="-a"
./mujson_mgr.py $action -I $1 -u "${remarks}" -p ${port} -k ${password} -m ${method} -O ${protocol} -G ${device_limit} -o ${obfs} -s ${speed_limit_per_con} -S ${speed_limit_per_user} -t ${transfer_enable} -f "${forbidden_port}"
}
set_ssr_python_crontab() {
if [ "$1" = "start" ];then
auto_clear_transfer=$(config_t_get global auto_clear_transfer 0)
if [ "$auto_clear_transfer" = "0" ];then
sed -i '/clear_traffic_all_users.sh/d' /etc/crontabs/root >/dev/null 2>&1 &
else
auto_clear_transfer_time=$(config_t_get global auto_clear_transfer_time)
[ -n "$auto_clear_transfer_time" ] && auto_clear_transfer_time=$(echo $auto_clear_transfer_time | tr ',' ' ')
echo "$auto_clear_transfer_time $ssr_python_path/sh/clear_traffic_all_users.sh >/dev/null 2>&1" >> /etc/crontabs/root
fi
else
sed -i '/clear_traffic_all_users.sh/d' /etc/crontabs/root >/dev/null 2>&1 &
fi
/etc/init.d/cron restart
}
start_ssr_python_server() {
python=/usr/bin/python
lua /usr/lib/lua/luci/model/cbi/ssr_python_pro_server/genssrmudbconfig.lua > /tmp/mudb.json
mv -f /tmp/mudb.json $ssr_python_path/mudb.json
$python $ssr_python_path/server.py >> /var/log/$CONFIG.log 2>&1 &
set_ssr_python_crontab "start"
fw3 reload
:<<EOF
cbi_ids=
config_foreach gen_ssr_python_config_file "ssr_python_users"
if [ -z "$cbi_ids" ];then
echo "[]" > $ssr_python_path/mudb.json
else
$python $ssr_python_path/server.py >> /var/log/$CONFIG.log 2>&1 &
fi
EOF
}
stop_ssr_python_server() {
fw3 reload
ps -w | grep "/usr/bin/python $ssr_python_path/server.py" | grep -v "grep" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &
rm -rf /var/log/$CONFIG.log
set_ssr_python_crontab "stop"
}
start() {
config_load $CONFIG
enable=$(config_t_get global enable 0)
if [ "$enable" = "0" ];then
stop_ssr_python_server
else
cp -rf $ssr_python_path/mudb.json $ssr_python_path/mudb_backup.json
start_ssr_python_server
fi
}
stop() {
stop_ssr_python_server
}
restart() {
stop
sleep 1
start
}

View File

@ -0,0 +1,21 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete firewall.ssr_python_pro_server
set firewall.ssr_python_pro_server=include
set firewall.ssr_python_pro_server.type=script
set firewall.ssr_python_pro_server.path=/usr/share/ssr_python_pro_server/firewall.include
set firewall.ssr_python_pro_server.reload=1
EOF
uci -q batch <<-EOF >/dev/null
delete ucitrack.@ssr_python_pro_server[-1]
add ucitrack ssr_python_pro_server
set ucitrack.@ssr_python_pro_server[-1].init=ssr_python_pro_server
commit ucitrack
EOF
chmod a+x /usr/share/ssr_python_pro_server/* >/dev/null 2>&1
rm -f /tmp/luci-indexcache
exit 0

View File

@ -0,0 +1,21 @@
language: python
python:
- 2.6
- 2.7
- 3.3
- 3.4
cache:
directories:
- dante-1.4.0
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq build-essential dnsutils iproute nginx bc
- sudo dd if=/dev/urandom of=/usr/share/nginx/www/file bs=1M count=10
- sudo sh -c "echo '127.0.0.1 localhost' > /etc/hosts"
- sudo service nginx restart
- pip install pep8 pyflakes nose coverage PySocks cymysql
- sudo tests/socksify/install.sh
- sudo tests/libsodium/install.sh
- sudo tests/setup_tc.sh
script:
- tests/jenkins.sh

View File

@ -0,0 +1,342 @@
3.4.0 2017-07-27
- add auth_chain_b
- add initmudbjson.sh
- allow set speed limit in runtime
- fix bugs & mem leak
3.3.3 2017-06-03
- add DNS cache
- add tls1.2_ticket_fastauth
- fix bugs
3.3.2 2017-05-20
- revert http reply
- refine tls1.2_ticket_auth error detector
3.3.1 2017-05-18
- fix stop script
- Async DNS query under UDP
- fix old version of OpenSSL
- http reply
3.3.0 2017-05-11
- connect_log include local addr & port
- fix auth_chain_a UDP bug
- add "additional_ports_only"
- add interface legendsockssr
- run with newest python version
- parse comment in hosts
- update mujson_mgr
- add cymysql setup script
- new speed tester
- fix leaks
- bugs fixed
3.2.0 2017-04-27
- add auth_chain_a
- remove auth_aes128, auth_sha1, auth_sha1_v2, verify_simple, auth_simple, verify_sha1
3.1.2 2017-04-07
- display UID
- auto adjust TCP MSS
3.1.1 2017-03-25
- add "New session ticket"
- ignore bind 10.0.0.0/8 and 192.168.0.0/16 by default
- improve rand size under auth_aes128_*
- fix bugs
3.1.0 2017-03-16
- add "glzjinmod" interface
- rate limit
- add additional_ports in config
3.0.4 2017-01-08
- multi-user in single port
3.0.1 2017-01-03
- remove auth_aes128_*_compatible
3.0.0 2016-12-23
- http_simple fix bugs
- tls1.2_ticket_auth fix bug & defaule time diff set to 86400s
2.9.7 2016-11-22
- manage client with LRUCache
- catch bind error
- fix import error of resource on windows
- print RLIMIT_NOFILE
- always close cymysql objects
- add init script
2.9.6 2016-10-17
- tls1.2_ticket_auth random packet size
2.9.5.1 2016-10-16
- UDP bind address
2.9.5 2016-10-13
- add auth_aes128_md5 and auth_aes128_sha1
2.9.4 2016-10-11
- sync client version
2.6.13 2015-11-02
- add protocol setting
2.6.12 2015-10-27
- IPv6 first
- Fix mem leaks
- auth_simple plugin
- remove FORCE_NEW_PROTOCOL
- optimize code
2.6.11 2015-10-20
- Obfs plugin
- Obfs parameters
- UDP over TCP
- TCP over UDP (experimental)
- Fix socket leaks
- Catch abnormal UDP package
2.6.10 2015-06-08
- Optimize LRU cache
- Refine logging
2.6.9 2015-05-19
- Fix a stability issue on Windows
2.6.8 2015-02-10
- Support multiple server ip on client side
- Support --version
- Minor fixes
2.6.7 2015-02-02
- Support --user
- Support CIDR format in --forbidden-ip
- Minor fixes
2.6.6 2015-01-23
- Fix a crash in forbidden list
2.6.5 2015-01-18
- Try both 32 bit and 64 bit dll on Windows
2.6.4 2015-01-14
- Also search lib* when searching libraries
2.6.3 2015-01-12
- Support --forbidden-ip to ban some IP, i.e. localhost
- Search OpenSSL and libsodium harder
- Now works on OpenWRT
2.6.2 2015-01-03
- Log client IP
2.6.1 2014-12-26
- Fix a problem with TCP Fast Open on local side
- Fix sometimes daemon_start returns wrong exit status
2.6 2014-12-21
- Add daemon support
2.5 2014-12-11
- Add salsa20 and chacha20
2.4.3 2014-11-10
- Fix an issue on Python 3
- Fix an issue with IPv6
2.4.2 2014-11-06
- Fix command line arguments on Python 3
- Support table on Python 3
- Fix TCP Fast Open on Python 3
2.4.1 2014-11-01
- Fix setup.py for non-utf8 locales on Python 3
2.4 2014-11-01
- Python 3 support
- Performance improvement
- Fix LRU cache behavior
2.3.2 2014-10-11
- Fix OpenSSL on Windows
2.3.1 2014-10-09
- Does not require M2Crypto any more
2.3 2014-09-23
- Support CFB1, CFB8 and CTR mode of AES
- Do not require password config when using port_password
- Use SIGTERM instead of SIGQUIT on Windows
2.2.2 2014-09-14
- Fix when multiple DNS set, IPv6 only sites are broken
2.2.1 2014-09-10
- Support graceful shutdown
- Fix some bugs
2.2.0 2014-09-09
- Add RC4-MD5 encryption
2.1.0 2014-08-10
- Use only IPv4 DNS server
- Does not ship config.json
- Better error message
2.0.12 2014-07-26
- Support -q quiet mode
- Exit 0 when showing help with -h
2.0.11 2014-07-12
- Prefers IP addresses over hostnames, more friendly with socksify and openvpn
2.0.10 2014-07-11
- Fix UDP on local
2.0.9 2014-07-06
- Fix EWOULDBLOCK on Windows
- Fix Unicode config problem on some platforms
2.0.8 2014-06-23
- Use multiple DNS to query hostnames
2.0.7 2014-06-21
- Fix fastopen on local
- Fallback when fastopen is not available
- Add verbose logging mode -vv
- Verify if hostname is valid
2.0.6 2014-06-19
- Fix CPU 100% on POLL_HUP
- More friendly logging
2.0.5 2014-06-18
- Support a simple config format for multiple ports
2.0.4 2014-06-12
- Fix worker master
2.0.3 2014-06-11
- Fix table encryption with UDP
2.0.2 2014-06-11
- Add asynchronous DNS in TCP relay
2.0.1 2014-06-05
- Better logging
- Maybe fix bad file descriptor
2.0 2014-06-05
- Use a new event model
- Remove gevent
- Refuse to use default password
- Fix a problem when using multiple passwords with table encryption
1.4.5 2014-05-24
- Add timeout in TCP server
- Close sockets in master process
1.4.4 2014-05-17
- Support multiple workers
1.4.3 2014-05-13
- Fix Windows
1.4.2 2014-05-10
- Add salsa20-ctr cipher
1.4.1 2014-05-03
- Fix error log
- Fix EINPROGESS with some version of gevent
1.4.0 2014-05-02
- Adds UDP relay
- TCP fast open support on Linux 3.7+
1.3.7 2014-04-10
- Fix a typo in help
1.3.6 2014-04-10
- Fix a typo in help
1.3.5 2014-04-07
- Add help
- Change default local binding address into 127.0.0.1
1.3.4 2014-02-17
- Fix a bug when no config file exists
- Client now support multiple server ports and multiple server/port pairs
- Better error message with bad config.json format and wrong password
1.3.3 2013-07-09
- Fix default key length of rc2
1.3.2 2013-07-04
- Server will listen at server IP specified in config
- Check config file and show some warning messages
1.3.1 2013-06-29
- Fix -c arg
1.3.0 2013-06-22
- Move to pypi
1.2.3 2013-06-14
- add bind address
1.2.2 2013-05-31
- local can listen at ::0 with -6 arg; bump 1.2.2
1.2.1 2013-05-23
- Fix an OpenSSL crash
1.2 2013-05-22
- Use random iv, we finally have strong encryption
1.1.1 2013-05-21
- Add encryption, AES, blowfish, etc.
1.1 2013-05-16
- Support IPv6 addresses (type 4)
- Drop Python 2.5 support
1.0 2013-04-03
- Fix -6 IPv6
0.9.4 2013-03-04
- Support Python 2.5
0.9.3 2013-01-14
- Fix conn termination null data
0.9.2 2013-01-05
- Change default timeout
0.9.1 2013-01-05
- Add Travis-CI test
0.9 2012-12-30
- Replace send with sendall, fix FreeBSD
0.6 2012-12-06
- Support args
0.5 2012-11-08
- Fix encryption with negative md5sum
0.4 2012-11-02
- Move config into a JSON file
- Auto-detect config path
0.3 2012-06-06
- Move socks5 negotiation to local
0.2 2012-05-11
- Add -6 arg for IPv6
- Fix socket.error
0.1 2012-04-20
- Initial version

View File

@ -0,0 +1,29 @@
How to Contribute
=================
Pull Requests
-------------
1. Pull requests are welcome. If you would like to add a large feature
or make a significant change, make sure to open an issue to discuss with
people first.
2. Follow PEP8.
3. Make sure to pass the unit tests. Write unit tests for new modules if
needed.
Issues
------
1. Only bugs and feature requests are accepted here.
2. We'll only work on important features. If the feature you're asking only
benefits a few people, you'd better implement the feature yourself and send us
a pull request, or ask some of your friends to do so.
3. We don't answer questions of any other types here. Since very few people
are watching the issue tracker here, you'll probably get no help from here.
Read [Troubleshooting] and get help from forums or [mailing lists].
4. Issues in languages other than English will be Google translated into English
later.
[Troubleshooting]: https://github.com/clowwindy/shadowsocks/wiki/Troubleshooting
[mailing lists]: https://groups.google.com/forum/#!forum/shadowsocks

View File

@ -0,0 +1,31 @@
FROM alpine:3.6
ENV SERVER_ADDR 0.0.0.0
ENV SERVER_PORT 51348
ENV PASSWORD psw
ENV METHOD aes-128-ctr
ENV PROTOCOL auth_aes128_md5
ENV PROTOCOLPARAM 32
ENV OBFS tls1.2_ticket_auth_compatible
ENV TIMEOUT 300
ENV DNS_ADDR 8.8.8.8
ENV DNS_ADDR_2 8.8.4.4
ARG BRANCH=manyuser
ARG WORK=~
RUN apk --no-cache add python \
libsodium \
wget
RUN mkdir -p $WORK && \
wget -qO- --no-check-certificate https://github.com/shadowsocksr/shadowsocksr/archive/$BRANCH.tar.gz | tar -xzf - -C $WORK
WORKDIR $WORK/shadowsocksr-$BRANCH/shadowsocks
EXPOSE $SERVER_PORT
CMD python server.py -p $SERVER_PORT -k $PASSWORD -m $METHOD -O $PROTOCOL -o $OBFS -G $PROTOCOLPARAM

View File

@ -0,0 +1,3 @@
recursive-include shadowsocks *.py
include README.rst
include LICENSE

View File

@ -0,0 +1,15 @@
# Config
API_INTERFACE = 'sspanelv2' #mudbjson, sspanelv2, sspanelv3, sspanelv3ssr, glzjinmod, legendsockssr, muapiv2(not support)
UPDATE_TIME = 60
SERVER_PUB_ADDR = '127.0.0.1' # mujson_mgr need this to generate ssr link
#mudb
MUDB_FILE = 'mudb.json'
# Mysql
MYSQL_CONFIG = 'usermysql.json'
# API
MUAPI_CONFIG = 'usermuapi.json'

View File

@ -0,0 +1,99 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2014 clowwindy
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import time
import os
import socket
import struct
import re
import logging
from shadowsocks import common
from shadowsocks import lru_cache
from shadowsocks import eventloop
import server_pool
import Config
class ServerMgr(object):
def __init__(self):
self._loop = None
self._request_id = 1
self._hosts = {}
self._hostname_status = {}
self._hostname_to_cb = {}
self._cb_to_hostname = {}
self._last_time = time.time()
self._sock = None
self._servers = None
def add_to_loop(self, loop):
if self._loop:
raise Exception('already add to loop')
self._loop = loop
# TODO when dns server is IPv6
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
socket.SOL_UDP)
self._sock.bind((Config.MANAGE_BIND_IP, Config.MANAGE_PORT))
self._sock.setblocking(False)
loop.add(self._sock, eventloop.POLL_IN, self)
def _handle_data(self, sock):
data, addr = sock.recvfrom(128)
#manage pwd:port:passwd:action
args = data.split(':')
if len(args) < 4:
return
if args[0] == Config.MANAGE_PASS:
if args[3] == '0':
server_pool.ServerPool.get_instance().cb_del_server(args[1])
elif args[3] == '1':
server_pool.ServerPool.get_instance().new_server(args[1], args[2])
def handle_event(self, sock, fd, event):
if sock != self._sock:
return
if event & eventloop.POLL_ERR:
logging.error('mgr socket err')
self._loop.remove(self._sock)
self._sock.close()
# TODO when dns server is IPv6
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
socket.SOL_UDP)
self._sock.setblocking(False)
self._loop.add(self._sock, eventloop.POLL_IN, self)
else:
self._handle_data(sock)
def close(self):
if self._sock:
if self._loop:
self._loop.remove(self._sock)
self._sock.close()
self._sock = None
def test():
pass
if __name__ == '__main__':
test()

View File

@ -0,0 +1,25 @@
{
"server": "0.0.0.0",
"server_ipv6": "::",
"server_port": 8388,
"local_address": "127.0.0.1",
"local_port": 1080,
"password": "m",
"method": "aes-128-ctr",
"protocol": "auth_aes128_md5",
"protocol_param": "",
"obfs": "tls1.2_ticket_auth_compatible",
"obfs_param": "",
"speed_limit_per_con": 0,
"speed_limit_per_user": 0,
"additional_ports" : {}, // only works under multi-user mode
"additional_ports_only" : false, // only works under multi-user mode
"timeout": 120,
"udp_timeout": 60,
"dns_ipv6": false,
"connect_verbose_info": 0,
"redirect": "",
"fast_open": false
}

View File

@ -0,0 +1,15 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import importloader
g_config = None
def load_config():
global g_config
g_config = importloader.loads(['userapiconfig', 'apiconfig'])
def get_config():
return g_config
load_config()

View File

@ -0,0 +1,631 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import logging
import time
import sys
from server_pool import ServerPool
import traceback
from shadowsocks import common, shell, lru_cache, obfs
from configloader import load_config, get_config
import importloader
switchrule = None
db_instance = None
class TransferBase(object):
def __init__(self):
import threading
self.event = threading.Event()
self.key_list = ['port', 'u', 'd', 'transfer_enable', 'passwd', 'enable']
self.last_get_transfer = {} #上一次的实际流量
self.last_update_transfer = {} #上一次更新到的流量(小于等于实际流量)
self.force_update_transfer = set() #强制推入数据库的ID
self.port_uid_table = {} #端口到uid的映射仅v3以上有用
self.onlineuser_cache = lru_cache.LRUCache(timeout=60*30) #用户在线状态记录
self.pull_ok = False #记录是否已经拉出过数据
self.mu_ports = {}
def load_cfg(self):
pass
def push_db_all_user(self):
if self.pull_ok is False:
return
#更新用户流量到数据库
last_transfer = self.last_update_transfer
curr_transfer = ServerPool.get_instance().get_servers_transfer()
#上次和本次的增量
dt_transfer = {}
for id in self.force_update_transfer: #此表中的用户统计上次未计入的流量
if id in self.last_get_transfer and id in last_transfer:
dt_transfer[id] = [self.last_get_transfer[id][0] - last_transfer[id][0], self.last_get_transfer[id][1] - last_transfer[id][1]]
for id in curr_transfer.keys():
if id in self.force_update_transfer or id in self.mu_ports:
continue
#算出与上次记录的流量差值保存于dt_transfer表
if id in last_transfer:
if curr_transfer[id][0] + curr_transfer[id][1] - last_transfer[id][0] - last_transfer[id][1] <= 0:
continue
dt_transfer[id] = [curr_transfer[id][0] - last_transfer[id][0],
curr_transfer[id][1] - last_transfer[id][1]]
else:
if curr_transfer[id][0] + curr_transfer[id][1] <= 0:
continue
dt_transfer[id] = [curr_transfer[id][0], curr_transfer[id][1]]
#有流量的,先记录在线状态
if id in self.last_get_transfer:
if curr_transfer[id][0] + curr_transfer[id][1] > self.last_get_transfer[id][0] + self.last_get_transfer[id][1]:
self.onlineuser_cache[id] = curr_transfer[id][0] + curr_transfer[id][1]
else:
self.onlineuser_cache[id] = curr_transfer[id][0] + curr_transfer[id][1]
self.onlineuser_cache.sweep()
update_transfer = self.update_all_user(dt_transfer) #返回有更新的表
for id in update_transfer.keys(): #其增量加在此表
if id not in self.force_update_transfer: #但排除在force_update_transfer内的
last = self.last_update_transfer.get(id, [0,0])
self.last_update_transfer[id] = [last[0] + update_transfer[id][0], last[1] + update_transfer[id][1]]
self.last_get_transfer = curr_transfer
for id in self.force_update_transfer:
if id in self.last_update_transfer:
del self.last_update_transfer[id]
if id in self.last_get_transfer:
del self.last_get_transfer[id]
self.force_update_transfer = set()
def del_server_out_of_bound_safe(self, last_rows, rows):
#停止超流量的服务
#启动没超流量的服务
try:
switchrule = importloader.load('switchrule')
except Exception as e:
logging.error('load switchrule.py fail')
cur_servers = {}
new_servers = {}
allow_users = {}
mu_servers = {}
config = shell.get_config(False)
for row in rows:
try:
allow = switchrule.isTurnOn(row) and row['enable'] == 1 and row['u'] + row['d'] < row['transfer_enable']
except Exception as e:
allow = False
port = row['port']
passwd = common.to_bytes(row['passwd'])
if hasattr(passwd, 'encode'):
passwd = passwd.encode('utf-8')
cfg = {'password': passwd}
if 'id' in row:
self.port_uid_table[row['port']] = row['id']
read_config_keys = ['method', 'obfs', 'obfs_param', 'protocol', 'protocol_param', 'forbidden_ip', 'forbidden_port', 'speed_limit_per_con', 'speed_limit_per_user']
for name in read_config_keys:
if name in row and row[name]:
cfg[name] = row[name]
merge_config_keys = ['password'] + read_config_keys
for name in cfg.keys():
if hasattr(cfg[name], 'encode'):
try:
cfg[name] = cfg[name].encode('utf-8')
except Exception as e:
logging.warning('encode cfg key "%s" fail, val "%s"' % (name, cfg[name]))
if port not in cur_servers:
cur_servers[port] = passwd
else:
logging.error('more than one user use the same port [%s]' % (port,))
continue
if 'protocol' in cfg and 'protocol_param' in cfg and common.to_str(cfg['protocol']) in obfs.mu_protocol():
if '#' in common.to_str(cfg['protocol_param']):
mu_servers[port] = passwd
allow = True
if allow:
if port not in mu_servers:
allow_users[port] = cfg
cfgchange = False
if port in ServerPool.get_instance().tcp_servers_pool:
relay = ServerPool.get_instance().tcp_servers_pool[port]
for name in merge_config_keys:
if name in cfg and not self.cmp(cfg[name], relay._config[name]):
cfgchange = True
break
if not cfgchange and port in ServerPool.get_instance().tcp_ipv6_servers_pool:
relay = ServerPool.get_instance().tcp_ipv6_servers_pool[port]
for name in merge_config_keys:
if (name in cfg) and ((name not in relay._config) or not self.cmp(cfg[name], relay._config[name])):
cfgchange = True
break
if port in mu_servers:
if ServerPool.get_instance().server_is_run(port) > 0:
if cfgchange:
logging.info('db stop server at port [%s] reason: config changed: %s' % (port, cfg))
ServerPool.get_instance().cb_del_server(port)
self.force_update_transfer.add(port)
new_servers[port] = (passwd, cfg)
else:
self.new_server(port, passwd, cfg)
else:
if ServerPool.get_instance().server_is_run(port) > 0:
if config['additional_ports_only'] or not allow:
logging.info('db stop server at port [%s]' % (port,))
ServerPool.get_instance().cb_del_server(port)
self.force_update_transfer.add(port)
else:
if cfgchange:
logging.info('db stop server at port [%s] reason: config changed: %s' % (port, cfg))
ServerPool.get_instance().cb_del_server(port)
self.force_update_transfer.add(port)
new_servers[port] = (passwd, cfg)
elif not config['additional_ports_only'] and allow and port > 0 and port < 65536 and ServerPool.get_instance().server_run_status(port) is False:
self.new_server(port, passwd, cfg)
for row in last_rows:
if row['port'] in cur_servers:
pass
else:
logging.info('db stop server at port [%s] reason: port not exist' % (row['port']))
ServerPool.get_instance().cb_del_server(row['port'])
self.clear_cache(row['port'])
if row['port'] in self.port_uid_table:
del self.port_uid_table[row['port']]
if len(new_servers) > 0:
from shadowsocks import eventloop
self.event.wait(eventloop.TIMEOUT_PRECISION + eventloop.TIMEOUT_PRECISION / 2)
for port in new_servers.keys():
passwd, cfg = new_servers[port]
self.new_server(port, passwd, cfg)
logging.debug('db allow users %s \nmu_servers %s' % (allow_users, mu_servers))
for port in mu_servers:
ServerPool.get_instance().update_mu_users(port, allow_users)
self.mu_ports = mu_servers
def clear_cache(self, port):
if port in self.force_update_transfer: del self.force_update_transfer[port]
if port in self.last_get_transfer: del self.last_get_transfer[port]
if port in self.last_update_transfer: del self.last_update_transfer[port]
def new_server(self, port, passwd, cfg):
protocol = cfg.get('protocol', ServerPool.get_instance().config.get('protocol', 'origin'))
method = cfg.get('method', ServerPool.get_instance().config.get('method', 'None'))
obfs = cfg.get('obfs', ServerPool.get_instance().config.get('obfs', 'plain'))
logging.info('db start server at port [%s] pass [%s] protocol [%s] method [%s] obfs [%s]' % (port, passwd, protocol, method, obfs))
ServerPool.get_instance().new_server(port, cfg)
def cmp(self, val1, val2):
if type(val1) is bytes:
val1 = common.to_str(val1)
if type(val2) is bytes:
val2 = common.to_str(val2)
return val1 == val2
@staticmethod
def del_servers():
for port in [v for v in ServerPool.get_instance().tcp_servers_pool.keys()]:
if ServerPool.get_instance().server_is_run(port) > 0:
ServerPool.get_instance().cb_del_server(port)
for port in [v for v in ServerPool.get_instance().tcp_ipv6_servers_pool.keys()]:
if ServerPool.get_instance().server_is_run(port) > 0:
ServerPool.get_instance().cb_del_server(port)
@staticmethod
def thread_db(obj):
import socket
import time
global db_instance
timeout = 60
socket.setdefaulttimeout(timeout)
last_rows = []
db_instance = obj()
ServerPool.get_instance()
shell.log_shadowsocks_version()
try:
import resource
logging.info('current process RLIMIT_NOFILE resource: soft %d hard %d' % resource.getrlimit(resource.RLIMIT_NOFILE))
except:
pass
try:
while True:
load_config()
db_instance.load_cfg()
try:
db_instance.push_db_all_user()
rows = db_instance.pull_db_all_user()
if rows:
db_instance.pull_ok = True
config = shell.get_config(False)
for port in config['additional_ports']:
val = config['additional_ports'][port]
val['port'] = int(port)
val['enable'] = 1
val['transfer_enable'] = 1024 ** 7
val['u'] = 0
val['d'] = 0
if "password" in val:
val["passwd"] = val["password"]
rows.append(val)
db_instance.del_server_out_of_bound_safe(last_rows, rows)
last_rows = rows
except Exception as e:
trace = traceback.format_exc()
logging.error(trace)
#logging.warn('db thread except:%s' % e)
if db_instance.event.wait(get_config().UPDATE_TIME) or not ServerPool.get_instance().thread.is_alive():
break
except KeyboardInterrupt as e:
pass
db_instance.del_servers()
ServerPool.get_instance().stop()
db_instance = None
@staticmethod
def thread_db_stop():
global db_instance
db_instance.event.set()
class DbTransfer(TransferBase):
def __init__(self):
super(DbTransfer, self).__init__()
self.user_pass = {} #记录更新此用户流量时被跳过多少次
self.cfg = {
"host": "127.0.0.1",
"port": 3306,
"user": "ss",
"password": "pass",
"db": "shadowsocks",
"node_id": 0,
"transfer_mul": 1.0,
"ssl_enable": 0,
"ssl_ca": "",
"ssl_cert": "",
"ssl_key": ""}
self.load_cfg()
def load_cfg(self):
import json
config_path = get_config().MYSQL_CONFIG
cfg = None
with open(config_path, 'rb+') as f:
cfg = json.loads(f.read().decode('utf8'))
if cfg:
self.cfg.update(cfg)
def update_all_user(self, dt_transfer):
import cymysql
update_transfer = {}
query_head = 'UPDATE user'
query_sub_when = ''
query_sub_when2 = ''
query_sub_in = None
last_time = time.time()
for id in dt_transfer.keys():
transfer = dt_transfer[id]
#小于最低更新流量的先不更新
update_trs = 1024 * (2048 - self.user_pass.get(id, 0) * 64)
if transfer[0] + transfer[1] < update_trs and id not in self.force_update_transfer:
self.user_pass[id] = self.user_pass.get(id, 0) + 1
continue
if id in self.user_pass:
del self.user_pass[id]
query_sub_when += ' WHEN %s THEN u+%s' % (id, int(transfer[0] * self.cfg["transfer_mul"]))
query_sub_when2 += ' WHEN %s THEN d+%s' % (id, int(transfer[1] * self.cfg["transfer_mul"]))
update_transfer[id] = transfer
if query_sub_in is not None:
query_sub_in += ',%s' % id
else:
query_sub_in = '%s' % id
if query_sub_when == '':
return update_transfer
query_sql = query_head + ' SET u = CASE port' + query_sub_when + \
' END, d = CASE port' + query_sub_when2 + \
' END, t = ' + str(int(last_time)) + \
' WHERE port IN (%s)' % query_sub_in
if self.cfg["ssl_enable"] == 1:
conn = cymysql.connect(host=self.cfg["host"], port=self.cfg["port"],
user=self.cfg["user"], passwd=self.cfg["password"],
db=self.cfg["db"], charset='utf8',
ssl={'ca':self.cfg["ssl_ca"],'cert':self.cfg["ssl_cert"],'key':self.cfg["ssl_key"]})
else:
conn = cymysql.connect(host=self.cfg["host"], port=self.cfg["port"],
user=self.cfg["user"], passwd=self.cfg["password"],
db=self.cfg["db"], charset='utf8')
try:
cur = conn.cursor()
try:
cur.execute(query_sql)
except Exception as e:
logging.error(e)
update_transfer = {}
cur.close()
conn.commit()
except Exception as e:
logging.error(e)
update_transfer = {}
finally:
conn.close()
return update_transfer
def pull_db_all_user(self):
import cymysql
#数据库所有用户信息
if self.cfg["ssl_enable"] == 1:
conn = cymysql.connect(host=self.cfg["host"], port=self.cfg["port"],
user=self.cfg["user"], passwd=self.cfg["password"],
db=self.cfg["db"], charset='utf8',
ssl={'ca':self.cfg["ssl_ca"],'cert':self.cfg["ssl_cert"],'key':self.cfg["ssl_key"]})
else:
conn = cymysql.connect(host=self.cfg["host"], port=self.cfg["port"],
user=self.cfg["user"], passwd=self.cfg["password"],
db=self.cfg["db"], charset='utf8')
try:
rows = self.pull_db_users(conn)
finally:
conn.close()
if not rows:
logging.warn('no user in db')
return rows
def pull_db_users(self, conn):
try:
switchrule = importloader.load('switchrule')
keys = switchrule.getKeys(self.key_list)
except Exception as e:
keys = self.key_list
cur = conn.cursor()
cur.execute("SELECT " + ','.join(keys) + " FROM user")
rows = []
for r in cur.fetchall():
d = {}
for column in range(len(keys)):
d[keys[column]] = r[column]
rows.append(d)
cur.close()
return rows
class Dbv3Transfer(DbTransfer):
def __init__(self):
super(Dbv3Transfer, self).__init__()
self.update_node_state = True if get_config().API_INTERFACE != 'legendsockssr' else False
if self.update_node_state:
self.key_list += ['id']
self.key_list += ['method']
if self.update_node_state:
self.ss_node_info_name = 'ss_node_info_log'
if get_config().API_INTERFACE == 'sspanelv3ssr':
self.key_list += ['obfs', 'protocol']
if get_config().API_INTERFACE == 'glzjinmod':
self.key_list += ['obfs', 'protocol']
self.ss_node_info_name = 'ss_node_info'
else:
self.key_list += ['obfs', 'protocol']
self.start_time = time.time()
def update_all_user(self, dt_transfer):
import cymysql
update_transfer = {}
query_head = 'UPDATE user'
query_sub_when = ''
query_sub_when2 = ''
query_sub_in = None
last_time = time.time()
alive_user_count = len(self.onlineuser_cache)
bandwidth_thistime = 0
if self.cfg["ssl_enable"] == 1:
conn = cymysql.connect(host=self.cfg["host"], port=self.cfg["port"],
user=self.cfg["user"], passwd=self.cfg["password"],
db=self.cfg["db"], charset='utf8',
ssl={'ca':self.cfg["ssl_ca"],'cert':self.cfg["ssl_cert"],'key':self.cfg["ssl_key"]})
else:
conn = cymysql.connect(host=self.cfg["host"], port=self.cfg["port"],
user=self.cfg["user"], passwd=self.cfg["password"],
db=self.cfg["db"], charset='utf8')
conn.autocommit(True)
for id in dt_transfer.keys():
transfer = dt_transfer[id]
bandwidth_thistime = bandwidth_thistime + transfer[0] + transfer[1]
update_trs = 1024 * (2048 - self.user_pass.get(id, 0) * 64)
if transfer[0] + transfer[1] < update_trs:
self.user_pass[id] = self.user_pass.get(id, 0) + 1
continue
if id in self.user_pass:
del self.user_pass[id]
query_sub_when += ' WHEN %s THEN u+%s' % (id, int(transfer[0] * self.cfg["transfer_mul"]))
query_sub_when2 += ' WHEN %s THEN d+%s' % (id, int(transfer[1] * self.cfg["transfer_mul"]))
update_transfer[id] = transfer
if self.update_node_state:
cur = conn.cursor()
try:
if id in self.port_uid_table:
cur.execute("INSERT INTO `user_traffic_log` (`id`, `user_id`, `u`, `d`, `node_id`, `rate`, `traffic`, `log_time`) VALUES (NULL, '" + \
str(self.port_uid_table[id]) + "', '" + str(transfer[0]) + "', '" + str(transfer[1]) + "', '" + \
str(self.cfg["node_id"]) + "', '" + str(self.cfg["transfer_mul"]) + "', '" + \
self.traffic_format((transfer[0] + transfer[1]) * self.cfg["transfer_mul"]) + "', unix_timestamp()); ")
except:
logging.warn('no `user_traffic_log` in db')
cur.close()
if query_sub_in is not None:
query_sub_in += ',%s' % id
else:
query_sub_in = '%s' % id
if query_sub_when != '':
query_sql = query_head + ' SET u = CASE port' + query_sub_when + \
' END, d = CASE port' + query_sub_when2 + \
' END, t = ' + str(int(last_time)) + \
' WHERE port IN (%s)' % query_sub_in
cur = conn.cursor()
try:
cur.execute(query_sql)
except Exception as e:
logging.error(e)
cur.close()
if self.update_node_state:
try:
cur = conn.cursor()
try:
cur.execute("INSERT INTO `ss_node_online_log` (`id`, `node_id`, `online_user`, `log_time`) VALUES (NULL, '" + \
str(self.cfg["node_id"]) + "', '" + str(alive_user_count) + "', unix_timestamp()); ")
except Exception as e:
logging.error(e)
cur.close()
cur = conn.cursor()
try:
cur.execute("INSERT INTO `" + self.ss_node_info_name + "` (`id`, `node_id`, `uptime`, `load`, `log_time`) VALUES (NULL, '" + \
str(self.cfg["node_id"]) + "', '" + str(self.uptime()) + "', '" + \
str(self.load()) + "', unix_timestamp()); ")
except Exception as e:
logging.error(e)
cur.close()
except:
logging.warn('no `ss_node_online_log` or `" + self.ss_node_info_name + "` in db')
conn.close()
return update_transfer
def pull_db_users(self, conn):
try:
switchrule = importloader.load('switchrule')
keys = switchrule.getKeys(self.key_list)
except Exception as e:
keys = self.key_list
cur = conn.cursor()
if self.update_node_state:
node_info_keys = ['traffic_rate']
try:
cur.execute("SELECT " + ','.join(node_info_keys) +" FROM ss_node where `id`='" + str(self.cfg["node_id"]) + "'")
nodeinfo = cur.fetchone()
except Exception as e:
logging.error(e)
nodeinfo = None
if nodeinfo == None:
rows = []
cur.close()
conn.commit()
logging.warn('None result when select node info from ss_node in db, maybe you set the incorrect node id')
return rows
cur.close()
node_info_dict = {}
for column in range(len(nodeinfo)):
node_info_dict[node_info_keys[column]] = nodeinfo[column]
self.cfg['transfer_mul'] = float(node_info_dict['traffic_rate'])
cur = conn.cursor()
try:
rows = []
cur.execute("SELECT " + ','.join(keys) + " FROM user")
for r in cur.fetchall():
d = {}
for column in range(len(keys)):
d[keys[column]] = r[column]
rows.append(d)
except Exception as e:
logging.error(e)
cur.close()
return rows
def load(self):
import os
return os.popen("cat /proc/loadavg | awk '{ print $1\" \"$2\" \"$3 }'").readlines()[0]
def uptime(self):
return time.time() - self.start_time
def traffic_format(self, traffic):
if traffic < 1024 * 8:
return str(int(traffic)) + "B";
if traffic < 1024 * 1024 * 2:
return str(round((traffic / 1024.0), 2)) + "KB";
return str(round((traffic / 1048576.0), 2)) + "MB";
class MuJsonTransfer(TransferBase):
def __init__(self):
super(MuJsonTransfer, self).__init__()
def update_all_user(self, dt_transfer):
import json
rows = None
config_path = get_config().MUDB_FILE
with open(config_path, 'rb+') as f:
rows = json.loads(f.read().decode('utf8'))
for row in rows:
if "port" in row:
port = row["port"]
if port in dt_transfer:
row["u"] += dt_transfer[port][0]
row["d"] += dt_transfer[port][1]
if rows:
output = json.dumps(rows, sort_keys=True, indent=4, separators=(',', ': '))
with open(config_path, 'r+') as f:
f.write(output)
f.truncate()
return dt_transfer
def pull_db_all_user(self):
import json
rows = None
config_path = get_config().MUDB_FILE
with open(config_path, 'rb+') as f:
rows = json.loads(f.read().decode('utf8'))
for row in rows:
try:
if 'forbidden_ip' in row:
row['forbidden_ip'] = common.IPNetwork(row['forbidden_ip'])
except Exception as e:
logging.error(e)
try:
if 'forbidden_port' in row:
row['forbidden_port'] = common.PortRange(row['forbidden_port'])
except Exception as e:
logging.error(e)
if not rows:
logging.warn('no user in json file')
return rows

View File

@ -0,0 +1,5 @@
shadowsocks (2.1.0-1) unstable; urgency=low
* Initial release (Closes: #758900)
-- Shell.Xu <shell909090@gmail.com> Sat, 23 Aug 2014 00:56:04 +0800

View File

@ -0,0 +1,11 @@
{
"server":"my_server_ip",
"server_port":8388,
"local_address": "127.0.0.1",
"local_port":1080,
"password":"mypassword",
"timeout":300,
"method":"aes-256-cfb",
"fast_open": false,
"workers": 1
}

View File

@ -0,0 +1,19 @@
Source: shadowsocks
Section: python
Priority: extra
Maintainer: Shell.Xu <shell909090@gmail.com>
Build-Depends: debhelper (>= 8), python-all (>= 2.6.6-3~), python-setuptools
Standards-Version: 3.9.5
Homepage: https://github.com/clowwindy/shadowsocks
Vcs-Git: git://github.com/shell909090/shadowsocks.git
Vcs-Browser: http://github.com/shell909090/shadowsocks
Package: shadowsocks
Architecture: all
Pre-Depends: dpkg (>= 1.15.6~)
Depends: ${misc:Depends}, ${python:Depends}, python-pkg-resources, python-m2crypto
Description: Fast tunnel proxy that helps you bypass firewalls
A secure socks5 proxy, designed to protect your Internet traffic.
.
This package contain local and server part of shadowsocks, a fast,
powerful tunnel proxy to bypass firewalls.

View File

@ -0,0 +1,30 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: shadowsocks
Source: https://github.com/clowwindy/shadowsocks
Files: debian/*
Copyright: 2014 Shell.Xu <shell909090@gmail.com>
License: Expat
Files: *
Copyright: 2014 clowwindy <clowwindy42@gmail.com>
License: Expat
License: Expat
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,2 @@
README.md
README.rst

View File

@ -0,0 +1,149 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: shadowsocks
# Required-Start: $network $local_fs $remote_fs
# Required-Stop: $network $local_fs $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Fast tunnel proxy that helps you bypass firewalls
# Description: A secure socks5 proxy, designed to protect your Internet traffic.
# This package contain local and server part of shadowsocks, a fast,
# powerful tunnel proxy to bypass firewalls.
### END INIT INFO
# Author: Shell.Xu <shell909090@gmail.com>
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC=shadowsocks # Introduce a short description here
NAME=shadowsocks # Introduce the short server's name here
DAEMON=/usr/bin/ssserver # Introduce the server's location here
DAEMON_ARGS="" # Arguments to run the daemon with
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
LOGFILE=/var/log/$NAME.log
# Exit if the package is not installed
[ -x $DAEMON ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \
--background --make-pidfile --chdir / --chuid $USERID --no-close --test > /dev/null \
|| return 1
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \
--background --make-pidfile --chdir / --chuid $USERID --no-close -- \
$DAEMON_ARGS $DAEMON_OPTS >> $LOGFILE 2>&1 \
|| return 2
# Add code here, if necessary, that waits for the process to be ready
# to handle requests from services started subsequently which depend
# on this one. As a last resort, sleep for some time.
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE
return "$RETVAL"
}
#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
#
# If the daemon can reload its configuration without
# restarting (for example, when it is sent a SIGHUP),
# then implement that here.
#
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
return 0
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
#reload|force-reload)
#
# If do_reload() is not implemented then leave this commented out
# and leave 'force-reload' as an alias for 'restart'.
#
#log_daemon_msg "Reloading $DESC" "$NAME"
#do_reload
#log_end_msg $?
#;;
restart|force-reload)
#
# If the "reload" option is implemented then remove the
# 'force-reload' alias
#
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 3
;;
esac
:

View File

@ -0,0 +1 @@
debian/config.json etc/shadowsocks/

View File

@ -0,0 +1,5 @@
#!/usr/bin/make -f
# -*- makefile -*-
%:
dh $@ --with python2 --buildsystem=python_distutils

View File

@ -0,0 +1,12 @@
# Defaults for shadowsocks initscript
# sourced by /etc/init.d/shadowsocks
# installed at /etc/default/shadowsocks by the maintainer scripts
USERID="nobody"
#
# This is a POSIX shell fragment
#
# Additional options that are passed to the Daemon.
DAEMON_OPTS="-q -c /etc/shadowsocks/config.json"

View File

@ -0,0 +1,2 @@
debian/sslocal.1
debian/ssserver.1

View File

@ -0,0 +1,59 @@
.\" Hey, EMACS: -*- nroff -*-
.\" (C) Copyright 2014 Shell.Xu <shell909090@gmail.com>,
.\"
.TH SHADOWSOCKS 1 "August 23, 2014"
.SH NAME
shadowsocks \- Fast tunnel proxy that helps you bypass firewalls
.SH SYNOPSIS
.B ssserver
.RI [ options ]
.br
.B sslocal
.RI [ options ]
.SH DESCRIPTION
shadowsocks is a tunnel proxy helps you bypass firewall.
.B ssserver
is the server part, and
.B sslocal
is the local part.
.SH OPTIONS
.TP
.B \-h, \-\-help
Show this help message and exit.
.TP
.B \-s SERVER_ADDR
Server address, default: 0.0.0.0.
.TP
.B \-p SERVER_PORT
Server port, default: 8388.
.TP
.B \-k PASSWORD
Password.
.TP
.B \-m METHOD
Encryption method, default: aes-256-cfb.
.TP
.B \-t TIMEOUT
Timeout in seconds, default: 300.
.TP
.B \-c CONFIG
Path to config file.
.TP
.B \-\-fast-open
Use TCP_FASTOPEN, requires Linux 3.7+.
.TP
.B \-\-workers WORKERS
Number of workers, available on Unix/Linux.
.TP
.B \-v, \-vv
Verbose mode.
.TP
.B \-q, \-qq
Quiet mode, only show warnings/errors.
.SH SEE ALSO
.br
The programs are documented fully by
.IR "Shell Xu <shell909090@gmail.com>"
and
.IR "Clowwindy <clowwindy42@gmail.com>",
available via the Info system.

View File

@ -0,0 +1,59 @@
.\" Hey, EMACS: -*- nroff -*-
.\" (C) Copyright 2014 Shell.Xu <shell909090@gmail.com>,
.\"
.TH SHADOWSOCKS 1 "August 23, 2014"
.SH NAME
shadowsocks \- Fast tunnel proxy that helps you bypass firewalls
.SH SYNOPSIS
.B ssserver
.RI [ options ]
.br
.B sslocal
.RI [ options ]
.SH DESCRIPTION
shadowsocks is a tunnel proxy helps you bypass firewall.
.B ssserver
is the server part, and
.B sslocal
is the local part.
.SH OPTIONS
.TP
.B \-h, \-\-help
Show this help message and exit.
.TP
.B \-s SERVER_ADDR
Server address, default: 0.0.0.0.
.TP
.B \-p SERVER_PORT
Server port, default: 8388.
.TP
.B \-k PASSWORD
Password.
.TP
.B \-m METHOD
Encryption method, default: aes-256-cfb.
.TP
.B \-t TIMEOUT
Timeout in seconds, default: 300.
.TP
.B \-c CONFIG
Path to config file.
.TP
.B \-\-fast-open
Use TCP_FASTOPEN, requires Linux 3.7+.
.TP
.B \-\-workers WORKERS
Number of workers, available on Unix/Linux.
.TP
.B \-v, \-vv
Verbose mode.
.TP
.B \-q, \-qq
Quiet mode, only show warnings/errors.
.SH SEE ALSO
.br
The programs are documented fully by
.IR "Shell Xu <shell909090@gmail.com>"
and
.IR "Clowwindy <clowwindy42@gmail.com>",
available via the Info system.

View File

@ -0,0 +1,25 @@
#!/bin/sh
. $IPKG_INSTROOT/lib/functions.sh
. $IPKG_INSTROOT/lib/functions/service.sh
gen_user_iptables() {
config_get enable $1 enable
[ "$enable" = "0" ] && return 0
config_get remarks $1 remarks
config_get port $1 port
iptables -A SSR_PYTHON-SERVER -p tcp --dport $port -m comment --comment "$remarks" -j ACCEPT
iptables -A SSR_PYTHON-SERVER -p udp --dport $port -m comment --comment "$remarks" -j ACCEPT
}
iptables -F SSR_PYTHON-SERVER 2>/dev/null
iptables -D INPUT -j SSR_PYTHON-SERVER 2>/dev/null
iptables -X SSR_PYTHON-SERVER 2>/dev/null
enable=$(uci get ssr_python_pro_server.@global[0].enable)
if [ $enable -eq 1 ]; then
iptables -N SSR_PYTHON-SERVER
iptables -I INPUT -j SSR_PYTHON-SERVER
config_load ssr_python_pro_server
config_foreach gen_user_iptables "user"
fi

View File

@ -0,0 +1,24 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
def load(name):
try:
obj = __import__(name)
reload(obj)
return obj
except:
pass
try:
import importlib
obj = importlib.__import__(name)
importlib.reload(obj)
return obj
except:
pass
def loads(namelist):
for name in namelist:
obj = load(name)
if obj is not None:
return obj

View File

@ -0,0 +1,4 @@
@echo off
If Not Exist "userapiconfig.py" Copy "apiconfig.py" "userapiconfig.py"
If Not Exist "user-config.json" Copy "config.json" "user-config.json"
If Not Exist "usermysql.json" Copy "mysql.json" "usermysql.json"

View File

@ -0,0 +1,8 @@
#!/bin/bash
chmod +x *.sh
chmod +x shadowsocks/*.sh
cp -n apiconfig.py userapiconfig.py
cp -n config.json user-config.json
cp -n mysql.json usermysql.json

View File

@ -0,0 +1,24 @@
#!/bin/bash
bash initcfg.sh
sed -i "s/API_INTERFACE = .\+\?\#/API_INTERFACE = \'mudbjson\' \#/g" userapiconfig.py
ip_addr=`ifconfig -a|grep inet|grep -v inet6|grep -v "127.0.0."|grep -v -e "192\.168\..[0-9]\+\.[0-9]\+"|grep -v -e "10\.[0-9]\+\.[0-9]\+\.[0-9]\+"|awk '{print $2}'|tr -d "addr:"`
ip_count=`echo $ip_addr|grep -e "^[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+$" -c`
if [[ $ip_count == 1 ]]; then
ip_addr=`ip a|grep inet|grep -v inet6|grep -v "127.0.0."|grep -v -e "192\.168\..[0-9]\+\.[0-9]\+"|grep -v -e "10\.[0-9]\+\.[0-9]\+\.[0-9]\+"|awk '{print $2}'`
ip_addr=${ip_addr%/*}
ip_count=`echo $ip_addr|grep -e "^[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+$" -c`
fi
if [[ $ip_count == 1 ]]; then
echo "server IP is "${ip_addr}
sed -i "s/SERVER_PUB_ADDR = .\+/SERVER_PUB_ADDR = \'"${ip_addr}"\'/g" userapiconfig.py
user_count=`python mujson_mgr.py -l|grep -c -e "[0-9]"`
if [[ $user_count == 0 ]]; then
port=`python -c 'import random;print(random.randint(10000, 65536))'`
python mujson_mgr.py -a -p ${port}
fi
else
echo "unable to detect server IP"
fi

View File

@ -0,0 +1,7 @@
#!/bin/bash
cd `dirname $0`
#python_ver=$(ls /usr/bin|grep -e "^python[23]\.[1-9]\+$"|tail -1)
eval $(ps -ef | grep "[0-9] python server\\.py m" | awk '{print "kill "$2}')
ulimit -n 512000
nohup python server.py m >> ssserver.log 2>&1 &

View File

@ -0,0 +1,19 @@
[
{
"d": 276776,
"enable": 1,
"forbidden_port": "",
"id": "cfg05ae6f",
"method": "none",
"obfs": "tls1.2_ticket_auth",
"passwd": "123456",
"port": 11111,
"protocol": "auth_chain_a",
"protocol_param": "2",
"speed_limit_per_con": 100,
"speed_limit_per_user": 200,
"transfer_enable": 10737418240,
"u": 60612,
"user": "\u563b\u563b"
}
]

View File

@ -0,0 +1,372 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import traceback
from shadowsocks import shell, common
from configloader import load_config, get_config
import random
import getopt
import sys
import json
import base64
class MuJsonLoader(object):
def __init__(self):
self.json = None
def load(self, path):
l = "[]"
try:
with open(path, 'rb+') as f:
l = f.read().decode('utf8')
except:
pass
self.json = json.loads(l)
def save(self, path):
if self.json is not None:
output = json.dumps(self.json, sort_keys=True, indent=4, separators=(',', ': '))
with open(path, 'a'):
pass
with open(path, 'rb+') as f:
f.write(output.encode('utf8'))
f.truncate()
class MuMgr(object):
def __init__(self):
self.config_path = get_config().MUDB_FILE
try:
self.server_addr = get_config().SERVER_PUB_ADDR
except:
self.server_addr = '127.0.0.1'
self.data = MuJsonLoader()
if self.server_addr == '127.0.0.1':
self.server_addr = self.getipaddr()
def getipaddr(self, ifname='eth0'):
import socket
import struct
ret = '127.0.0.1'
try:
ret = socket.gethostbyname(socket.getfqdn(socket.gethostname()))
except:
pass
if ret == '127.0.0.1':
try:
import fcntl
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ret = socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24])
except:
pass
return ret
def ssrlink(self, user, encode, muid):
protocol = user.get('protocol', '')
obfs = user.get('obfs', '')
protocol = protocol.replace("_compatible", "")
obfs = obfs.replace("_compatible", "")
protocol_param = ''
if muid is not None:
protocol_param_ = user.get('protocol_param', '')
param = protocol_param_.split('#')
if len(param) == 2:
for row in self.data.json:
if int(row['port']) == muid:
param = str(muid) + ':' + row['passwd']
protocol_param = '/?protoparam=' + common.to_str(base64.urlsafe_b64encode(common.to_bytes(param))).replace("=", "")
break
link = ("%s:%s:%s:%s:%s:%s" % (self.server_addr, user['port'], protocol, user['method'], obfs, common.to_str(base64.urlsafe_b64encode(common.to_bytes(user['passwd']))).replace("=", ""))) + protocol_param
return "ssr://" + (encode and common.to_str(base64.urlsafe_b64encode(common.to_bytes(link))).replace("=", "") or link)
def userinfo(self, user, muid = None):
ret = ""
key_list = ['user', 'port', 'method', 'passwd', 'protocol', 'protocol_param', 'obfs', 'obfs_param', 'transfer_enable', 'u', 'd']
for key in sorted(user):
if key not in key_list:
key_list.append(key)
for key in key_list:
if key in ['enable'] or key not in user:
continue
ret += '\n'
if (muid is not None) and (key in ['protocol_param']):
for row in self.data.json:
if int(row['port']) == muid:
ret += " %s : %s" % (key, str(muid) + ':' + row['passwd'])
break
elif key in ['transfer_enable', 'u', 'd']:
if muid is not None:
for row in self.data.json:
if int(row['port']) == muid:
val = row[key]
break
else:
val = user[key]
if val < 1024:
ret += " %s : %s" % (key, val)
elif val < 1024 ** 2:
val /= float(1024)
ret += " %s : %s K Bytes" % (key, val)
elif val < 1024 ** 3:
val /= float(1024 ** 2)
ret += " %s : %s M Bytes" % (key, val)
else:
val /= float(1024 ** 3)
ret += " %s : %s G Bytes" % (key, val)
else:
ret += " %s : %s" % (key, user[key])
u_b = user['u']
d_b = user['d']
total_traffic_b = u_b + d_b
total_traffic = total_traffic_b
ret += "\n %s : %s" % ("u_b", u_b)
ret += "\n %s : %s" % ("d_b", d_b)
ret += "\n %s : %s" % ("total_traffic_b", total_traffic_b)
ret += "\n"
if total_traffic < 1024:
ret += " %s : %s" % ("total_traffic", total_traffic)
elif total_traffic < 1024 ** 2:
total_traffic /= float(1024)
ret += " %s : %s K Bytes" % ("total_traffic", total_traffic)
elif total_traffic < 1024 ** 3:
total_traffic /= float(1024 ** 2)
ret += " %s : %s M Bytes" % ("total_traffic", total_traffic)
else:
total_traffic /= float(1024 ** 3)
ret += " %s : %s G Bytes" % ("total_traffic", total_traffic)
ret += "\n " + self.ssrlink(user, False, muid)
ret += "\n " + self.ssrlink(user, True, muid)
return ret
def rand_pass(self):
return ''.join([random.choice('''ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~-_=+(){}[]^&%$@''') for i in range(8)])
def add(self, user):
up = {'enable': 1, 'u': 0, 'd': 0, 'method': "aes-128-ctr",
'protocol': "auth_aes128_md5",
'obfs': "tls1.2_ticket_auth_compatible",
'transfer_enable': 9007199254740992}
up['passwd'] = self.rand_pass()
up.update(user)
self.data.load(self.config_path)
for row in self.data.json:
match = False
if 'id' in user and row['id'] != user['id']:
match = False
if match:
print("id %s user [%s] port [%s] already exist" % (row['id'], row['user'], row['port']))
return
self.data.json.append(up)
print("### add user info %s" % self.userinfo(up))
self.data.save(self.config_path)
def edit(self, user):
self.data.load(self.config_path)
for row in self.data.json:
match = True
if 'id' in user and row['id'] != user['id']:
match = False
if match:
print("edit user [%s]" % (row['user'],))
row.update(user)
print("### new id %s user info %s" % (row["id"], self.userinfo(row)))
break
self.data.save(self.config_path)
def delete(self, user):
self.data.load(self.config_path)
index = 0
for row in self.data.json:
match = True
if 'id' in user and row['id'] != user['id']:
match = False
if match:
print("delete id %s user [%s]" % (row["id"], row['user']))
del self.data.json[index]
break
index += 1
self.data.save(self.config_path)
def clear_ud(self, user):
up = {'u': 0, 'd': 0}
self.data.load(self.config_path)
for row in self.data.json:
match = True
if 'id' in user and row['id'] != user['id']:
match = False
if match:
row.update(up)
print("clear id %s user [%s]" % (row["id"], row['user']))
self.data.save(self.config_path)
def list_user(self, user):
self.data.load(self.config_path)
if not user:
for row in self.data.json:
print("id %s user [%s] port %s" % (row["id"], row['user'], row['port']))
return
for row in self.data.json:
match = True
if 'id' in user and row['id'] != user['id']:
match = False
if match:
muid = None
if 'muid' in user:
muid = user['muid']
print("### id %s user [%s] info %s" % (row["id"], row['user'], self.userinfo(row, muid)))
def print_server_help():
print('''usage: python mujson_manage.py -a|-d|-e|-c|-l [OPTION]...
Actions:
-a add/edit a user
-d delete a user
-e edit a user
-c set u&d to zero
-l display a user infomation or all users infomation
Options:
-I ID the ID
-u USER the user name
-p PORT server port (only this option must be set if add a user)
-k PASSWORD password
-m METHOD encryption method, default: aes-128-ctr
-O PROTOCOL protocol plugin, default: auth_aes128_md5
-o OBFS obfs plugin, default: tls1.2_ticket_auth_compatible
-G PROTOCOL_PARAM protocol plugin param
-g OBFS_PARAM obfs plugin param
-t TRANSFER max transfer for G bytes, default: 8388608 (8 PB or 8192 TB)
-f FORBID set forbidden ports. Example (ban 1~79 and 81~100): -f "1-79,81-100"
-i MUID set sub id to display (only work with -l)
-s SPEED set speed_limit_per_con
-S SPEED set speed_limit_per_user
General options:
-h, --help show this help message and exit
''')
def main():
reload(sys)
sys.setdefaultencoding('utf-8')
shortopts = 'adeclI:u:i:p:k:O:o:G:g:m:t:f:hs:S:'
longopts = ['help']
action = None
user = {}
fast_set_obfs = {'0': 'plain',
'+1': 'http_simple_compatible',
'1': 'http_simple',
'+2': 'tls1.2_ticket_auth_compatible',
'2': 'tls1.2_ticket_auth'}
fast_set_protocol = {'0': 'origin',
's4': 'auth_sha1_v4',
'+s4': 'auth_sha1_v4_compatible',
'am': 'auth_aes128_md5',
'as': 'auth_aes128_sha1',
'ca': 'auth_chain_a',
}
fast_set_method = {'0': 'none',
'a1c': 'aes-128-cfb',
'a2c': 'aes-192-cfb',
'a3c': 'aes-256-cfb',
'r': 'rc4-md5',
'r6': 'rc4-md5-6',
'c': 'chacha20',
'ci': 'chacha20-ietf',
's': 'salsa20',
'a1': 'aes-128-ctr',
'a2': 'aes-192-ctr',
'a3': 'aes-256-ctr'}
try:
optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
for key, value in optlist:
if key == '-a':
action = 1
elif key == '-d':
action = 2
elif key == '-e':
action = 3
elif key == '-l':
action = 4
elif key == '-c':
action = 0
elif key == '-I':
user['id'] = value
elif key == '-u':
user['user'] = value
elif key == '-i':
user['muid'] = int(value)
elif key == '-p':
user['port'] = int(value)
elif key == '-k':
user['passwd'] = value
elif key == '-o':
if value in fast_set_obfs:
user['obfs'] = fast_set_obfs[value]
else:
user['obfs'] = value
elif key == '-O':
if value in fast_set_protocol:
user['protocol'] = fast_set_protocol[value]
else:
user['protocol'] = value
elif key == '-g':
user['obfs_param'] = value
elif key == '-G':
user['protocol_param'] = value
elif key == '-s':
user['speed_limit_per_con'] = int(value)
elif key == '-S':
user['speed_limit_per_user'] = int(value)
elif key == '-m':
if value in fast_set_method:
user['method'] = fast_set_method[value]
else:
user['method'] = value
elif key == '-f':
user['forbidden_port'] = value
elif key == '-t':
val = float(value)
try:
val = int(value)
except:
pass
user['transfer_enable'] = int(val * 1024) * (1024 ** 2)
elif key in ('-h', '--help'):
print_server_help()
sys.exit(0)
except getopt.GetoptError as e:
print(e)
sys.exit(2)
manage = MuMgr()
if action == 0:
manage.clear_ud(user)
elif action == 1:
if 'user' not in user and 'id' in user:
user['user'] = str(user['id'])
if 'user' in user and 'port' in user and 'id' in user:
manage.add(user)
else:
print("You have to set the port with -p")
elif action == 2:
if 'user' in user or 'port' in user or 'id' in user:
manage.delete(user)
else:
print("You have to set the user name or port with -u/-p")
elif action == 3:
if 'user' in user or 'port' in user or 'id' in user:
manage.edit(user)
else:
print("You have to set the user name or port with -u/-p")
elif action == 4:
manage.list_user(user)
elif action is None:
print_server_help()
if __name__ == '__main__':
main()

View File

@ -0,0 +1,358 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import traceback
from shadowsocks import shell, common
from configloader import load_config, get_config
import random
import getopt
import sys
import json
import base64
class MuJsonLoader(object):
def __init__(self):
self.json = None
def load(self, path):
l = "[]"
try:
with open(path, 'rb+') as f:
l = f.read().decode('utf8')
except:
pass
self.json = json.loads(l)
def save(self, path):
if self.json is not None:
output = json.dumps(self.json, sort_keys=True, indent=4, separators=(',', ': '))
with open(path, 'a'):
pass
with open(path, 'rb+') as f:
f.write(output.encode('utf8'))
f.truncate()
class MuMgr(object):
def __init__(self):
self.config_path = get_config().MUDB_FILE
try:
self.server_addr = get_config().SERVER_PUB_ADDR
except:
self.server_addr = '127.0.0.1'
self.data = MuJsonLoader()
if self.server_addr == '127.0.0.1':
self.server_addr = self.getipaddr()
def getipaddr(self, ifname='eth0'):
import socket
import struct
ret = '127.0.0.1'
try:
ret = socket.gethostbyname(socket.getfqdn(socket.gethostname()))
except:
pass
if ret == '127.0.0.1':
try:
import fcntl
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ret = socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24])
except:
pass
return ret
def ssrlink(self, user, encode, muid):
protocol = user.get('protocol', '')
obfs = user.get('obfs', '')
protocol = protocol.replace("_compatible", "")
obfs = obfs.replace("_compatible", "")
protocol_param = ''
if muid is not None:
protocol_param_ = user.get('protocol_param', '')
param = protocol_param_.split('#')
if len(param) == 2:
for row in self.data.json:
if int(row['port']) == muid:
param = str(muid) + ':' + row['passwd']
protocol_param = '/?protoparam=' + common.to_str(base64.urlsafe_b64encode(common.to_bytes(param))).replace("=", "")
break
link = ("%s:%s:%s:%s:%s:%s" % (self.server_addr, user['port'], protocol, user['method'], obfs, common.to_str(base64.urlsafe_b64encode(common.to_bytes(user['passwd']))).replace("=", ""))) + protocol_param
return "ssr://" + (encode and common.to_str(base64.urlsafe_b64encode(common.to_bytes(link))).replace("=", "") or link)
def userinfo(self, user, muid = None):
ret = ""
key_list = ['user', 'port', 'method', 'passwd', 'protocol', 'protocol_param', 'obfs', 'obfs_param', 'transfer_enable', 'u', 'd']
for key in sorted(user):
if key not in key_list:
key_list.append(key)
for key in key_list:
if key in ['enable'] or key not in user:
continue
ret += '\n'
if (muid is not None) and (key in ['protocol_param']):
for row in self.data.json:
if int(row['port']) == muid:
ret += " %s : %s" % (key, str(muid) + ':' + row['passwd'])
break
elif key in ['transfer_enable', 'u', 'd']:
if muid is not None:
for row in self.data.json:
if int(row['port']) == muid:
val = row[key]
break
else:
val = user[key]
if val / 1024 < 4:
ret += " %s : %s" % (key, val)
elif val / 1024 ** 2 < 4:
val /= float(1024)
ret += " %s : %s K Bytes" % (key, val)
elif val / 1024 ** 3 < 4:
val /= float(1024 ** 2)
ret += " %s : %s M Bytes" % (key, val)
else:
val /= float(1024 ** 3)
ret += " %s : %s G Bytes" % (key, val)
else:
ret += " %s : %s" % (key, user[key])
ret += "\n " + self.ssrlink(user, False, muid)
ret += "\n " + self.ssrlink(user, True, muid)
return ret
def rand_pass(self):
return ''.join([random.choice('''ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~-_=+(){}[]^&%$@''') for i in range(8)])
def add(self, user):
up = {'enable': 1, 'u': 0, 'd': 0, 'method': "aes-128-ctr",
'protocol': "auth_aes128_md5",
'obfs': "tls1.2_ticket_auth_compatible",
'transfer_enable': 9007199254740992}
up['passwd'] = self.rand_pass()
up.update(user)
self.data.load(self.config_path)
for row in self.data.json:
match = False
if 'user' in user and row['user'] == user['user']:
match = True
if 'port' in user and row['port'] == user['port']:
match = True
if match:
print("user [%s] port [%s] already exist" % (row['user'], row['port']))
return
self.data.json.append(up)
print("### add user info %s" % self.userinfo(up))
self.data.save(self.config_path)
def edit(self, user):
self.data.load(self.config_path)
for row in self.data.json:
match = True
if 'user' in user and row['user'] != user['user']:
match = False
if 'port' in user and row['port'] != user['port']:
match = False
if match:
print("edit user [%s]" % (row['user'],))
row.update(user)
print("### new user info %s" % self.userinfo(row))
break
self.data.save(self.config_path)
def delete(self, user):
self.data.load(self.config_path)
index = 0
for row in self.data.json:
match = True
if 'user' in user and row['user'] != user['user']:
match = False
if 'port' in user and row['port'] != user['port']:
match = False
if match:
print("delete user [%s]" % row['user'])
del self.data.json[index]
break
index += 1
self.data.save(self.config_path)
def clear_ud(self, user):
up = {'u': 0, 'd': 0}
self.data.load(self.config_path)
for row in self.data.json:
match = True
if 'user' in user and row['user'] != user['user']:
match = False
if 'port' in user and row['port'] != user['port']:
match = False
if match:
row.update(up)
print("clear user [%s]" % row['user'])
self.data.save(self.config_path)
def list_user(self, user):
self.data.load(self.config_path)
if not user:
for row in self.data.json:
print("user [%s] port %s" % (row['user'], row['port']))
return
for row in self.data.json:
match = True
if 'user' in user and row['user'] != user['user']:
match = False
if 'port' in user and row['port'] != user['port']:
match = False
if match:
muid = None
if 'muid' in user:
muid = user['muid']
print("### user [%s] info %s" % (row['user'], self.userinfo(row, muid)))
def print_server_help():
print('''usage: python mujson_manage.py -a|-d|-e|-c|-l [OPTION]...
Actions:
-a add/edit a user
-d delete a user
-e edit a user
-c set u&d to zero
-l display a user infomation or all users infomation
Options:
-u USER the user name
-p PORT server port (only this option must be set if add a user)
-k PASSWORD password
-m METHOD encryption method, default: aes-128-ctr
-O PROTOCOL protocol plugin, default: auth_aes128_md5
-o OBFS obfs plugin, default: tls1.2_ticket_auth_compatible
-G PROTOCOL_PARAM protocol plugin param
-g OBFS_PARAM obfs plugin param
-t TRANSFER max transfer for G bytes, default: 8388608 (8 PB or 8192 TB)
-f FORBID set forbidden ports. Example (ban 1~79 and 81~100): -f "1-79,81-100"
-i MUID set sub id to display (only work with -l)
-s SPEED set speed_limit_per_con
-S SPEED set speed_limit_per_user
General options:
-h, --help show this help message and exit
''')
def main():
shortopts = 'adeclu:i:p:k:O:o:G:g:m:t:f:hs:S:'
longopts = ['help']
action = None
user = {}
fast_set_obfs = {'0': 'plain',
'+1': 'http_simple_compatible',
'1': 'http_simple',
'+2': 'tls1.2_ticket_auth_compatible',
'2': 'tls1.2_ticket_auth'}
fast_set_protocol = {'0': 'origin',
's4': 'auth_sha1_v4',
'+s4': 'auth_sha1_v4_compatible',
'am': 'auth_aes128_md5',
'as': 'auth_aes128_sha1',
'ca': 'auth_chain_a',
}
fast_set_method = {'0': 'none',
'a1c': 'aes-128-cfb',
'a2c': 'aes-192-cfb',
'a3c': 'aes-256-cfb',
'r': 'rc4-md5',
'r6': 'rc4-md5-6',
'c': 'chacha20',
'ci': 'chacha20-ietf',
's': 'salsa20',
'a1': 'aes-128-ctr',
'a2': 'aes-192-ctr',
'a3': 'aes-256-ctr'}
try:
optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
for key, value in optlist:
if key == '-a':
action = 1
elif key == '-d':
action = 2
elif key == '-e':
action = 3
elif key == '-l':
action = 4
elif key == '-c':
action = 0
elif key == '-u':
user['user'] = value
elif key == '-i':
user['muid'] = int(value)
elif key == '-p':
user['port'] = int(value)
elif key == '-k':
user['passwd'] = value
elif key == '-o':
if value in fast_set_obfs:
user['obfs'] = fast_set_obfs[value]
else:
user['obfs'] = value
elif key == '-O':
if value in fast_set_protocol:
user['protocol'] = fast_set_protocol[value]
else:
user['protocol'] = value
elif key == '-g':
user['obfs_param'] = value
elif key == '-G':
user['protocol_param'] = value
elif key == '-s':
user['speed_limit_per_con'] = int(value)
elif key == '-S':
user['speed_limit_per_user'] = int(value)
elif key == '-m':
if value in fast_set_method:
user['method'] = fast_set_method[value]
else:
user['method'] = value
elif key == '-f':
user['forbidden_port'] = value
elif key == '-t':
val = float(value)
try:
val = int(value)
except:
pass
user['transfer_enable'] = int(val * 1024) * (1024 ** 2)
elif key in ('-h', '--help'):
print_server_help()
sys.exit(0)
except getopt.GetoptError as e:
print(e)
sys.exit(2)
manage = MuMgr()
if action == 0:
manage.clear_ud(user)
elif action == 1:
if 'user' not in user and 'port' in user:
user['user'] = str(user['port'])
if 'user' in user and 'port' in user:
manage.add(user)
else:
print("You have to set the port with -p")
elif action == 2:
if 'user' in user or 'port' in user:
manage.delete(user)
else:
print("You have to set the user name or port with -u/-p")
elif action == 3:
if 'user' in user or 'port' in user:
manage.edit(user)
else:
print("You have to set the user name or port with -u/-p")
elif action == 4:
manage.list_user(user)
elif action is None:
print_server_help()
if __name__ == '__main__':
main()

View File

@ -0,0 +1,13 @@
{
"host": "127.0.0.1",
"port": 3306,
"user": "ss",
"password": "pass",
"db": "sspanel",
"node_id": 0,
"transfer_mul": 1.0,
"ssl_enable": 0,
"ssl_ca": "",
"ssl_cert": "",
"ssl_key": ""
}

View File

@ -0,0 +1,7 @@
#!/bin/bash
cd `dirname $0`
#python_ver=$(ls /usr/bin|grep -e "^python[23]\.[1-9]\+$"|tail -1)
eval $(ps -ef | grep "[0-9] python server\\.py m" | awk '{print "kill "$2}')
ulimit -n 512000
nohup python server.py m>> /dev/null 2>&1 &

View File

@ -0,0 +1,66 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright 2015 breakwall
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import time
import sys
import threading
import os
if __name__ == '__main__':
import inspect
os.chdir(os.path.dirname(os.path.realpath(inspect.getfile(inspect.currentframe()))))
import server_pool
import db_transfer
from shadowsocks import shell
from configloader import load_config, get_config
class MainThread(threading.Thread):
def __init__(self, obj):
super(MainThread, self).__init__()
self.daemon = True
self.obj = obj
def run(self):
self.obj.thread_db(self.obj)
def stop(self):
self.obj.thread_db_stop()
def main():
shell.check_python()
if False:
db_transfer.DbTransfer.thread_db()
else:
if get_config().API_INTERFACE == 'mudbjson':
thread = MainThread(db_transfer.MuJsonTransfer)
elif get_config().API_INTERFACE == 'sspanelv2':
thread = MainThread(db_transfer.DbTransfer)
else:
thread = MainThread(db_transfer.Dbv3Transfer)
thread.start()
try:
while thread.is_alive():
thread.join(10.0)
except (KeyboardInterrupt, IOError, OSError) as e:
import traceback
traceback.print_exc()
thread.stop()
if __name__ == '__main__':
main()

View File

@ -0,0 +1,293 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2014 clowwindy
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
import logging
import struct
import time
from shadowsocks import shell, eventloop, tcprelay, udprelay, asyncdns, common
import threading
import sys
import traceback
from socket import *
from configloader import load_config, get_config
class MainThread(threading.Thread):
def __init__(self, params):
super(MainThread, self).__init__()
self.params = params
def run(self):
ServerPool._loop(*self.params)
class ServerPool(object):
instance = None
def __init__(self):
shell.check_python()
self.config = shell.get_config(False)
self.dns_resolver = asyncdns.DNSResolver()
if not self.config.get('dns_ipv6', False):
asyncdns.IPV6_CONNECTION_SUPPORT = False
self.mgr = None #asyncmgr.ServerMgr()
self.tcp_servers_pool = {}
self.tcp_ipv6_servers_pool = {}
self.udp_servers_pool = {}
self.udp_ipv6_servers_pool = {}
self.stat_counter = {}
self.loop = eventloop.EventLoop()
self.thread = MainThread( (self.loop, self.dns_resolver, self.mgr) )
self.thread.start()
@staticmethod
def get_instance():
if ServerPool.instance is None:
ServerPool.instance = ServerPool()
return ServerPool.instance
def stop(self):
self.loop.stop()
@staticmethod
def _loop(loop, dns_resolver, mgr):
try:
if mgr is not None:
mgr.add_to_loop(loop)
dns_resolver.add_to_loop(loop)
loop.run()
except (KeyboardInterrupt, IOError, OSError) as e:
logging.error(e)
traceback.print_exc()
os.exit(0)
except Exception as e:
logging.error(e)
traceback.print_exc()
def server_is_run(self, port):
port = int(port)
ret = 0
if port in self.tcp_servers_pool:
ret = 1
if port in self.tcp_ipv6_servers_pool:
ret |= 2
return ret
def server_run_status(self, port):
if 'server' in self.config:
if port not in self.tcp_servers_pool:
return False
if 'server_ipv6' in self.config:
if port not in self.tcp_ipv6_servers_pool:
return False
return True
def new_server(self, port, user_config):
ret = True
port = int(port)
ipv6_ok = False
if 'server_ipv6' in self.config:
if port in self.tcp_ipv6_servers_pool:
logging.info("server already at %s:%d" % (self.config['server_ipv6'], port))
return 'this port server is already running'
else:
a_config = self.config.copy()
a_config.update(user_config)
if len(a_config['server_ipv6']) > 2 and a_config['server_ipv6'][0] == "[" and a_config['server_ipv6'][-1] == "]":
a_config['server_ipv6'] = a_config['server_ipv6'][1:-1]
a_config['server'] = a_config['server_ipv6']
a_config['server_port'] = port
a_config['max_connect'] = 128
a_config['method'] = common.to_str(a_config['method'])
try:
logging.info("starting server at [%s]:%d" % (common.to_str(a_config['server']), port))
tcp_server = tcprelay.TCPRelay(a_config, self.dns_resolver, False, stat_counter=self.stat_counter)
tcp_server.add_to_loop(self.loop)
self.tcp_ipv6_servers_pool.update({port: tcp_server})
udp_server = udprelay.UDPRelay(a_config, self.dns_resolver, False, stat_counter=self.stat_counter)
udp_server.add_to_loop(self.loop)
self.udp_ipv6_servers_pool.update({port: udp_server})
if common.to_str(a_config['server_ipv6']) == "::":
ipv6_ok = True
except Exception as e:
logging.warn("IPV6 %s " % (e,))
if 'server' in self.config:
if port in self.tcp_servers_pool:
logging.info("server already at %s:%d" % (common.to_str(self.config['server']), port))
return 'this port server is already running'
else:
a_config = self.config.copy()
a_config.update(user_config)
a_config['server_port'] = port
a_config['max_connect'] = 128
a_config['method'] = common.to_str(a_config['method'])
try:
logging.info("starting server at %s:%d" % (common.to_str(a_config['server']), port))
tcp_server = tcprelay.TCPRelay(a_config, self.dns_resolver, False)
tcp_server.add_to_loop(self.loop)
self.tcp_servers_pool.update({port: tcp_server})
udp_server = udprelay.UDPRelay(a_config, self.dns_resolver, False)
udp_server.add_to_loop(self.loop)
self.udp_servers_pool.update({port: udp_server})
except Exception as e:
if not ipv6_ok:
logging.warn("IPV4 %s " % (e,))
return True
def del_server(self, port):
port = int(port)
logging.info("del server at %d" % port)
try:
udpsock = socket(AF_INET, SOCK_DGRAM)
udpsock.sendto('%s:%s:0:0' % (get_config().MANAGE_PASS, port), (get_config().MANAGE_BIND_IP, get_config().MANAGE_PORT))
udpsock.close()
except Exception as e:
logging.warn(e)
return True
def cb_del_server(self, port):
port = int(port)
if port not in self.tcp_servers_pool:
logging.info("stopped server at %s:%d already stop" % (self.config['server'], port))
else:
logging.info("stopped server at %s:%d" % (self.config['server'], port))
try:
self.tcp_servers_pool[port].close(True)
del self.tcp_servers_pool[port]
except Exception as e:
logging.warn(e)
try:
self.udp_servers_pool[port].close(True)
del self.udp_servers_pool[port]
except Exception as e:
logging.warn(e)
if 'server_ipv6' in self.config:
if port not in self.tcp_ipv6_servers_pool:
logging.info("stopped server at [%s]:%d already stop" % (self.config['server_ipv6'], port))
else:
logging.info("stopped server at [%s]:%d" % (self.config['server_ipv6'], port))
try:
self.tcp_ipv6_servers_pool[port].close(True)
del self.tcp_ipv6_servers_pool[port]
except Exception as e:
logging.warn(e)
try:
self.udp_ipv6_servers_pool[port].close(True)
del self.udp_ipv6_servers_pool[port]
except Exception as e:
logging.warn(e)
return True
def update_mu_users(self, port, users):
port = int(port)
if port in self.tcp_servers_pool:
try:
self.tcp_servers_pool[port].update_users(users)
except Exception as e:
logging.warn(e)
try:
self.udp_servers_pool[port].update_users(users)
except Exception as e:
logging.warn(e)
if port in self.tcp_ipv6_servers_pool:
try:
self.tcp_ipv6_servers_pool[port].update_users(users)
except Exception as e:
logging.warn(e)
try:
self.udp_ipv6_servers_pool[port].update_users(users)
except Exception as e:
logging.warn(e)
def get_server_transfer(self, port):
port = int(port)
uid = struct.pack('<I', port)
ret = [0, 0]
if port in self.tcp_servers_pool:
ret[0], ret[1] = self.tcp_servers_pool[port].get_ud()
if port in self.udp_servers_pool:
u, d = self.udp_servers_pool[port].get_ud()
ret[0] += u
ret[1] += d
if port in self.tcp_ipv6_servers_pool:
u, d = self.tcp_ipv6_servers_pool[port].get_ud()
ret[0] += u
ret[1] += d
if port in self.udp_ipv6_servers_pool:
u, d = self.udp_ipv6_servers_pool[port].get_ud()
ret[0] += u
ret[1] += d
return ret
def get_server_mu_transfer(self, server):
return server.get_users_ud()
def update_mu_transfer(self, user_dict, u, d):
for uid in u:
port = struct.unpack('<I', uid)[0]
if port not in user_dict:
user_dict[port] = [0, 0]
user_dict[port][0] += u[uid]
for uid in d:
port = struct.unpack('<I', uid)[0]
if port not in user_dict:
user_dict[port] = [0, 0]
user_dict[port][1] += d[uid]
def get_servers_transfer(self):
servers = self.tcp_servers_pool.copy()
servers.update(self.tcp_ipv6_servers_pool)
servers.update(self.udp_servers_pool)
servers.update(self.udp_ipv6_servers_pool)
ret = {}
for port in servers.keys():
ret[port] = self.get_server_transfer(port)
for port in self.tcp_servers_pool:
u, d = self.get_server_mu_transfer(self.tcp_servers_pool[port])
self.update_mu_transfer(ret, u, d)
for port in self.tcp_ipv6_servers_pool:
u, d = self.get_server_mu_transfer(self.tcp_ipv6_servers_pool[port])
self.update_mu_transfer(ret, u, d)
for port in self.udp_servers_pool:
u, d = self.get_server_mu_transfer(self.udp_servers_pool[port])
self.update_mu_transfer(ret, u, d)
for port in self.udp_ipv6_servers_pool:
u, d = self.get_server_mu_transfer(self.udp_ipv6_servers_pool[port])
self.update_mu_transfer(ret, u, d)
return ret

View File

@ -0,0 +1,39 @@
import codecs
from setuptools import setup
with codecs.open('README.rst', encoding='utf-8') as f:
long_description = f.read()
setup(
name="shadowsocks",
version="2.6.12",
license='http://www.apache.org/licenses/LICENSE-2.0',
description="A fast tunnel proxy that help you get through firewalls",
author='clowwindy',
author_email='clowwindy42@gmail.com',
url='https://github.com/shadowsocks/shadowsocks',
packages=['shadowsocks', 'shadowsocks.crypto', 'shadowsocks.obfsplugin'],
package_data={
'shadowsocks': ['README.rst', 'LICENSE']
},
install_requires=[],
entry_points="""
[console_scripts]
sslocal = shadowsocks.local:main
ssserver = shadowsocks.server:main
""",
classifiers=[
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Internet :: Proxy Servers',
],
long_description=long_description,
)

View File

@ -0,0 +1,6 @@
#!/bin/bash
rm -rf CyMySQL
rm -rf cymysql
git clone https://github.com/nakagami/CyMySQL.git
mv CyMySQL/cymysql ./
rm -rf CyMySQL

View File

@ -0,0 +1,16 @@
#!/bin/sh
cd /usr/share/ssr_python_pro_server
user_total=$(./mujson_mgr.py -l | wc -l)
[ $user_total -eq 0 ] && echo -e "没有发现用户,请检查 !" && exit 1
for i in `seq 1 $user_total`
do
user_id=$(./mujson_mgr.py -l | sed -n ${i}p | awk '{print $2}')
match_clear=$(./mujson_mgr.py -c -I "${user_id}" | grep 'clear')
if [ -z "$match_clear" ]; then
echo -e "$user_id已使用流量清零失败"
else
echo -e "$user_id已使用流量清零成功"
fi
done
exit

View File

@ -0,0 +1,18 @@
#!/usr/bin/python
#
# Copyright 2012-2015 clowwindy
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import, division, print_function, \
with_statement

View File

@ -0,0 +1,555 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2014-2015 clowwindy
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import, division, print_function, \
with_statement
import os
import socket
import struct
import re
import logging
if __name__ == '__main__':
import sys
import inspect
file_path = os.path.dirname(os.path.realpath(inspect.getfile(inspect.currentframe())))
sys.path.insert(0, os.path.join(file_path, '../'))
from shadowsocks import common, lru_cache, eventloop, shell
CACHE_SWEEP_INTERVAL = 30
VALID_HOSTNAME = re.compile(br"(?!-)[A-Z\d_-]{1,63}(?<!-)$", re.IGNORECASE)
common.patch_socket()
# rfc1035
# format
# +---------------------+
# | Header |
# +---------------------+
# | Question | the question for the name server
# +---------------------+
# | Answer | RRs answering the question
# +---------------------+
# | Authority | RRs pointing toward an authority
# +---------------------+
# | Additional | RRs holding additional information
# +---------------------+
#
# header
# 1 1 1 1 1 1
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# | ID |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# | QDCOUNT |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# | ANCOUNT |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# | NSCOUNT |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# | ARCOUNT |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
QTYPE_ANY = 255
QTYPE_A = 1
QTYPE_AAAA = 28
QTYPE_CNAME = 5
QTYPE_NS = 2
QCLASS_IN = 1
def detect_ipv6_supprot():
if 'has_ipv6' in dir(socket):
try:
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
s.connect(('::1', 0))
print('IPv6 support')
return True
except:
pass
print('IPv6 not support')
return False
IPV6_CONNECTION_SUPPORT = detect_ipv6_supprot()
def build_address(address):
address = address.strip(b'.')
labels = address.split(b'.')
results = []
for label in labels:
l = len(label)
if l > 63:
return None
results.append(common.chr(l))
results.append(label)
results.append(b'\0')
return b''.join(results)
def build_request(address, qtype):
request_id = os.urandom(2)
header = struct.pack('!BBHHHH', 1, 0, 1, 0, 0, 0)
addr = build_address(address)
qtype_qclass = struct.pack('!HH', qtype, QCLASS_IN)
return request_id + header + addr + qtype_qclass
def parse_ip(addrtype, data, length, offset):
if addrtype == QTYPE_A:
return socket.inet_ntop(socket.AF_INET, data[offset:offset + length])
elif addrtype == QTYPE_AAAA:
return socket.inet_ntop(socket.AF_INET6, data[offset:offset + length])
elif addrtype in [QTYPE_CNAME, QTYPE_NS]:
return parse_name(data, offset)[1]
else:
return data[offset:offset + length]
def parse_name(data, offset):
p = offset
labels = []
l = common.ord(data[p])
while l > 0:
if (l & (128 + 64)) == (128 + 64):
# pointer
pointer = struct.unpack('!H', data[p:p + 2])[0]
pointer &= 0x3FFF
r = parse_name(data, pointer)
labels.append(r[1])
p += 2
# pointer is the end
return p - offset, b'.'.join(labels)
else:
labels.append(data[p + 1:p + 1 + l])
p += 1 + l
l = common.ord(data[p])
return p - offset + 1, b'.'.join(labels)
# rfc1035
# record
# 1 1 1 1 1 1
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# | |
# / /
# / NAME /
# | |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# | TYPE |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# | CLASS |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# | TTL |
# | |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# | RDLENGTH |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
# / RDATA /
# / /
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
def parse_record(data, offset, question=False):
nlen, name = parse_name(data, offset)
if not question:
record_type, record_class, record_ttl, record_rdlength = struct.unpack(
'!HHiH', data[offset + nlen:offset + nlen + 10]
)
ip = parse_ip(record_type, data, record_rdlength, offset + nlen + 10)
return nlen + 10 + record_rdlength, \
(name, ip, record_type, record_class, record_ttl)
else:
record_type, record_class = struct.unpack(
'!HH', data[offset + nlen:offset + nlen + 4]
)
return nlen + 4, (name, None, record_type, record_class, None, None)
def parse_header(data):
if len(data) >= 12:
header = struct.unpack('!HBBHHHH', data[:12])
res_id = header[0]
res_qr = header[1] & 128
res_tc = header[1] & 2
res_ra = header[2] & 128
res_rcode = header[2] & 15
# assert res_tc == 0
# assert res_rcode in [0, 3]
res_qdcount = header[3]
res_ancount = header[4]
res_nscount = header[5]
res_arcount = header[6]
return (res_id, res_qr, res_tc, res_ra, res_rcode, res_qdcount,
res_ancount, res_nscount, res_arcount)
return None
def parse_response(data):
try:
if len(data) >= 12:
header = parse_header(data)
if not header:
return None
res_id, res_qr, res_tc, res_ra, res_rcode, res_qdcount, \
res_ancount, res_nscount, res_arcount = header
qds = []
ans = []
offset = 12
for i in range(0, res_qdcount):
l, r = parse_record(data, offset, True)
offset += l
if r:
qds.append(r)
for i in range(0, res_ancount):
l, r = parse_record(data, offset)
offset += l
if r:
ans.append(r)
for i in range(0, res_nscount):
l, r = parse_record(data, offset)
offset += l
for i in range(0, res_arcount):
l, r = parse_record(data, offset)
offset += l
response = DNSResponse()
if qds:
response.hostname = qds[0][0]
for an in qds:
response.questions.append((an[1], an[2], an[3]))
for an in ans:
response.answers.append((an[1], an[2], an[3]))
return response
except Exception as e:
shell.print_exception(e)
return None
def is_valid_hostname(hostname):
if len(hostname) > 255:
return False
if hostname[-1] == b'.':
hostname = hostname[:-1]
return all(VALID_HOSTNAME.match(x) for x in hostname.split(b'.'))
class DNSResponse(object):
def __init__(self):
self.hostname = None
self.questions = [] # each: (addr, type, class)
self.answers = [] # each: (addr, type, class)
def __str__(self):
return '%s: %s' % (self.hostname, str(self.answers))
STATUS_IPV4 = 0
STATUS_IPV6 = 1
class DNSResolver(object):
def __init__(self):
self._loop = None
self._hosts = {}
self._hostname_status = {}
self._hostname_to_cb = {}
self._cb_to_hostname = {}
self._cache = lru_cache.LRUCache(timeout=300)
self._sock = None
self._servers = None
self._parse_resolv()
self._parse_hosts()
# TODO monitor hosts change and reload hosts
# TODO parse /etc/gai.conf and follow its rules
def _parse_resolv(self):
self._servers = []
try:
with open('dns.conf', 'rb') as f:
content = f.readlines()
for line in content:
line = line.strip()
if line:
parts = line.split(b' ', 1)
if len(parts) >= 2:
server = parts[0]
port = int(parts[1])
else:
server = parts[0]
port = 53
if common.is_ip(server) == socket.AF_INET:
if type(server) != str:
server = server.decode('utf8')
self._servers.append((server, port))
except IOError:
pass
if not self._servers:
try:
with open('/etc/resolv.conf', 'rb') as f:
content = f.readlines()
for line in content:
line = line.strip()
if line:
if line.startswith(b'nameserver'):
parts = line.split()
if len(parts) >= 2:
server = parts[1]
if common.is_ip(server) == socket.AF_INET:
if type(server) != str:
server = server.decode('utf8')
self._servers.append((server, 53))
except IOError:
pass
if not self._servers:
self._servers = [('8.8.4.4', 53), ('8.8.8.8', 53)]
logging.info('dns server: %s' % (self._servers,))
def _parse_hosts(self):
etc_path = '/etc/hosts'
if 'WINDIR' in os.environ:
etc_path = os.environ['WINDIR'] + '/system32/drivers/etc/hosts'
try:
with open(etc_path, 'rb') as f:
for line in f.readlines():
line = line.strip()
if b"#" in line:
line = line[:line.find(b'#')]
parts = line.split()
if len(parts) >= 2:
ip = parts[0]
if common.is_ip(ip):
for i in range(1, len(parts)):
hostname = parts[i]
if hostname:
self._hosts[hostname] = ip
except IOError:
self._hosts['localhost'] = '127.0.0.1'
def add_to_loop(self, loop):
if self._loop:
raise Exception('already add to loop')
self._loop = loop
# TODO when dns server is IPv6
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
socket.SOL_UDP)
self._sock.setblocking(False)
loop.add(self._sock, eventloop.POLL_IN, self)
loop.add_periodic(self.handle_periodic)
def _call_callback(self, hostname, ip, error=None):
callbacks = self._hostname_to_cb.get(hostname, [])
for callback in callbacks:
if callback in self._cb_to_hostname:
del self._cb_to_hostname[callback]
if ip or error:
callback((hostname, ip), error)
else:
callback((hostname, None),
Exception('unable to parse hostname %s' % hostname))
if hostname in self._hostname_to_cb:
del self._hostname_to_cb[hostname]
if hostname in self._hostname_status:
del self._hostname_status[hostname]
def _handle_data(self, data):
response = parse_response(data)
if response and response.hostname:
hostname = response.hostname
ip = None
for answer in response.answers:
if answer[1] in (QTYPE_A, QTYPE_AAAA) and \
answer[2] == QCLASS_IN:
ip = answer[0]
break
if IPV6_CONNECTION_SUPPORT:
if not ip and self._hostname_status.get(hostname, STATUS_IPV4) \
== STATUS_IPV6:
self._hostname_status[hostname] = STATUS_IPV4
self._send_req(hostname, QTYPE_A)
else:
if ip:
self._cache[hostname] = ip
self._call_callback(hostname, ip)
elif self._hostname_status.get(hostname, None) == STATUS_IPV4:
for question in response.questions:
if question[1] == QTYPE_A:
self._call_callback(hostname, None)
break
else:
if not ip and self._hostname_status.get(hostname, STATUS_IPV6) \
== STATUS_IPV4:
self._hostname_status[hostname] = STATUS_IPV6
self._send_req(hostname, QTYPE_AAAA)
else:
if ip:
self._cache[hostname] = ip
self._call_callback(hostname, ip)
elif self._hostname_status.get(hostname, None) == STATUS_IPV6:
for question in response.questions:
if question[1] == QTYPE_AAAA:
self._call_callback(hostname, None)
break
def handle_event(self, sock, fd, event):
if sock != self._sock:
return
if event & eventloop.POLL_ERR:
logging.error('dns socket err')
self._loop.remove(self._sock)
self._sock.close()
# TODO when dns server is IPv6
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
socket.SOL_UDP)
self._sock.setblocking(False)
self._loop.add(self._sock, eventloop.POLL_IN, self)
else:
data, addr = sock.recvfrom(1024)
if addr not in self._servers:
logging.warn('received a packet other than our dns')
return
self._handle_data(data)
def handle_periodic(self):
self._cache.sweep()
def remove_callback(self, callback):
hostname = self._cb_to_hostname.get(callback)
if hostname:
del self._cb_to_hostname[callback]
arr = self._hostname_to_cb.get(hostname, None)
if arr:
arr.remove(callback)
if not arr:
del self._hostname_to_cb[hostname]
if hostname in self._hostname_status:
del self._hostname_status[hostname]
def _send_req(self, hostname, qtype):
req = build_request(hostname, qtype)
for server in self._servers:
logging.debug('resolving %s with type %d using server %s',
hostname, qtype, server)
self._sock.sendto(req, server)
def resolve(self, hostname, callback):
if type(hostname) != bytes:
hostname = hostname.encode('utf8')
if not hostname:
callback(None, Exception('empty hostname'))
elif common.is_ip(hostname):
callback((hostname, hostname), None)
elif hostname in self._hosts:
logging.debug('hit hosts: %s', hostname)
ip = self._hosts[hostname]
callback((hostname, ip), None)
elif hostname in self._cache:
logging.debug('hit cache: %s', hostname)
ip = self._cache[hostname]
callback((hostname, ip), None)
else:
if not is_valid_hostname(hostname):
callback(None, Exception('invalid hostname: %s' % hostname))
return
if False:
addrs = socket.getaddrinfo(hostname, 0, 0,
socket.SOCK_DGRAM, socket.SOL_UDP)
if addrs:
af, socktype, proto, canonname, sa = addrs[0]
logging.debug('DNS resolve %s %s' % (hostname, sa[0]) )
self._cache[hostname] = sa[0]
callback((hostname, sa[0]), None)
return
arr = self._hostname_to_cb.get(hostname, None)
if not arr:
if IPV6_CONNECTION_SUPPORT:
self._hostname_status[hostname] = STATUS_IPV6
self._send_req(hostname, QTYPE_AAAA)
else:
self._hostname_status[hostname] = STATUS_IPV4
self._send_req(hostname, QTYPE_A)
self._hostname_to_cb[hostname] = [callback]
self._cb_to_hostname[callback] = hostname
else:
arr.append(callback)
# TODO send again only if waited too long
if IPV6_CONNECTION_SUPPORT:
self._send_req(hostname, QTYPE_AAAA)
else:
self._send_req(hostname, QTYPE_A)
def close(self):
if self._sock:
if self._loop:
self._loop.remove_periodic(self.handle_periodic)
self._loop.remove(self._sock)
self._sock.close()
self._sock = None
def test():
dns_resolver = DNSResolver()
loop = eventloop.EventLoop()
dns_resolver.add_to_loop(loop)
global counter
counter = 0
def make_callback():
global counter
def callback(result, error):
global counter
# TODO: what can we assert?
print(result, error)
counter += 1
if counter == 9:
dns_resolver.close()
loop.stop()
a_callback = callback
return a_callback
assert(make_callback() != make_callback())
dns_resolver.resolve(b'google.com', make_callback())
dns_resolver.resolve('google.com', make_callback())
dns_resolver.resolve('example.com', make_callback())
dns_resolver.resolve('ipv6.google.com', make_callback())
dns_resolver.resolve('www.facebook.com', make_callback())
dns_resolver.resolve('ns2.google.com', make_callback())
dns_resolver.resolve('invalid.@!#$%^&$@.hostname', make_callback())
dns_resolver.resolve('toooooooooooooooooooooooooooooooooooooooooooooooooo'
'ooooooooooooooooooooooooooooooooooooooooooooooooooo'
'long.hostname', make_callback())
dns_resolver.resolve('toooooooooooooooooooooooooooooooooooooooooooooooooo'
'ooooooooooooooooooooooooooooooooooooooooooooooooooo'
'ooooooooooooooooooooooooooooooooooooooooooooooooooo'
'ooooooooooooooooooooooooooooooooooooooooooooooooooo'
'ooooooooooooooooooooooooooooooooooooooooooooooooooo'
'ooooooooooooooooooooooooooooooooooooooooooooooooooo'
'long.hostname', make_callback())
loop.run()
if __name__ == '__main__':
test()

View File

@ -0,0 +1,418 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright 2013-2015 clowwindy
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import, division, print_function, \
with_statement
import socket
import struct
import logging
import binascii
import re
from shadowsocks import lru_cache
def compat_ord(s):
if type(s) == int:
return s
return _ord(s)
def compat_chr(d):
if bytes == str:
return _chr(d)
return bytes([d])
_ord = ord
_chr = chr
ord = compat_ord
chr = compat_chr
connect_log = logging.debug
def to_bytes(s):
if bytes != str:
if type(s) == str:
return s.encode('utf-8')
return s
def to_str(s):
if bytes != str:
if type(s) == bytes:
return s.decode('utf-8')
return s
def int32(x):
if x > 0xFFFFFFFF or x < 0:
x &= 0xFFFFFFFF
if x > 0x7FFFFFFF:
x = int(0x100000000 - x)
if x < 0x80000000:
return -x
else:
return -2147483648
return x
def inet_ntop(family, ipstr):
if family == socket.AF_INET:
return to_bytes(socket.inet_ntoa(ipstr))
elif family == socket.AF_INET6:
import re
v6addr = ':'.join(('%02X%02X' % (ord(i), ord(j))).lstrip('0')
for i, j in zip(ipstr[::2], ipstr[1::2]))
v6addr = re.sub('::+', '::', v6addr, count=1)
return to_bytes(v6addr)
def inet_pton(family, addr):
addr = to_str(addr)
if family == socket.AF_INET:
return socket.inet_aton(addr)
elif family == socket.AF_INET6:
if '.' in addr: # a v4 addr
v4addr = addr[addr.rindex(':') + 1:]
v4addr = socket.inet_aton(v4addr)
v4addr = ['%02X' % ord(x) for x in v4addr]
v4addr.insert(2, ':')
newaddr = addr[:addr.rindex(':') + 1] + ''.join(v4addr)
return inet_pton(family, newaddr)
dbyts = [0] * 8 # 8 groups
grps = addr.split(':')
for i, v in enumerate(grps):
if v:
dbyts[i] = int(v, 16)
else:
for j, w in enumerate(grps[::-1]):
if w:
dbyts[7 - j] = int(w, 16)
else:
break
break
return b''.join((chr(i // 256) + chr(i % 256)) for i in dbyts)
else:
raise RuntimeError("What family?")
def is_ip(address):
for family in (socket.AF_INET, socket.AF_INET6):
try:
if type(address) != str:
address = address.decode('utf8')
inet_pton(family, address)
return family
except (TypeError, ValueError, OSError, IOError):
pass
return False
def match_regex(regex, text):
regex = re.compile(regex)
for item in regex.findall(text):
return True
return False
def patch_socket():
if not hasattr(socket, 'inet_pton'):
socket.inet_pton = inet_pton
if not hasattr(socket, 'inet_ntop'):
socket.inet_ntop = inet_ntop
patch_socket()
ADDRTYPE_IPV4 = 1
ADDRTYPE_IPV6 = 4
ADDRTYPE_HOST = 3
def pack_addr(address):
address_str = to_str(address)
for family in (socket.AF_INET, socket.AF_INET6):
try:
r = socket.inet_pton(family, address_str)
if family == socket.AF_INET6:
return b'\x04' + r
else:
return b'\x01' + r
except (TypeError, ValueError, OSError, IOError):
pass
if len(address) > 255:
address = address[:255] # TODO
return b'\x03' + chr(len(address)) + address
def pre_parse_header(data):
if not data:
return None
datatype = ord(data[0])
if datatype == 0x80:
if len(data) <= 2:
return None
rand_data_size = ord(data[1])
if rand_data_size + 2 >= len(data):
logging.warn('header too short, maybe wrong password or '
'encryption method')
return None
data = data[rand_data_size + 2:]
elif datatype == 0x81:
data = data[1:]
elif datatype == 0x82:
if len(data) <= 3:
return None
rand_data_size = struct.unpack('>H', data[1:3])[0]
if rand_data_size + 3 >= len(data):
logging.warn('header too short, maybe wrong password or '
'encryption method')
return None
data = data[rand_data_size + 3:]
elif datatype == 0x88 or (~datatype & 0xff) == 0x88:
if len(data) <= 7 + 7:
return None
data_size = struct.unpack('>H', data[1:3])[0]
ogn_data = data
data = data[:data_size]
crc = binascii.crc32(data) & 0xffffffff
if crc != 0xffffffff:
logging.warn('uncorrect CRC32, maybe wrong password or '
'encryption method')
return None
start_pos = 3 + ord(data[3])
data = data[start_pos:-4]
if data_size < len(ogn_data):
data += ogn_data[data_size:]
return data
def parse_header(data):
addrtype = ord(data[0])
dest_addr = None
dest_port = None
header_length = 0
connecttype = (addrtype & 0x8) and 1 or 0
addrtype &= ~0x8
if addrtype == ADDRTYPE_IPV4:
if len(data) >= 7:
dest_addr = socket.inet_ntoa(data[1:5])
dest_port = struct.unpack('>H', data[5:7])[0]
header_length = 7
else:
logging.warn('header is too short')
elif addrtype == ADDRTYPE_HOST:
if len(data) > 2:
addrlen = ord(data[1])
if len(data) >= 4 + addrlen:
dest_addr = data[2:2 + addrlen]
dest_port = struct.unpack('>H', data[2 + addrlen:4 +
addrlen])[0]
header_length = 4 + addrlen
else:
logging.warn('header is too short')
else:
logging.warn('header is too short')
elif addrtype == ADDRTYPE_IPV6:
if len(data) >= 19:
dest_addr = socket.inet_ntop(socket.AF_INET6, data[1:17])
dest_port = struct.unpack('>H', data[17:19])[0]
header_length = 19
else:
logging.warn('header is too short')
else:
logging.warn('unsupported addrtype %d, maybe wrong password or '
'encryption method' % addrtype)
if dest_addr is None:
return None
return connecttype, addrtype, to_bytes(dest_addr), dest_port, header_length
class IPNetwork(object):
ADDRLENGTH = {socket.AF_INET: 32, socket.AF_INET6: 128, False: 0}
def __init__(self, addrs):
self.addrs_str = addrs
self._network_list_v4 = []
self._network_list_v6 = []
if type(addrs) == str:
addrs = addrs.split(',')
list(map(self.add_network, addrs))
def add_network(self, addr):
if addr is "":
return
block = addr.split('/')
addr_family = is_ip(block[0])
addr_len = IPNetwork.ADDRLENGTH[addr_family]
if addr_family is socket.AF_INET:
ip, = struct.unpack("!I", socket.inet_aton(block[0]))
elif addr_family is socket.AF_INET6:
hi, lo = struct.unpack("!QQ", inet_pton(addr_family, block[0]))
ip = (hi << 64) | lo
else:
raise Exception("Not a valid CIDR notation: %s" % addr)
if len(block) is 1:
prefix_size = 0
while (ip & 1) == 0 and ip is not 0:
ip >>= 1
prefix_size += 1
logging.warn("You did't specify CIDR routing prefix size for %s, "
"implicit treated as %s/%d" % (addr, addr, addr_len))
elif block[1].isdigit() and int(block[1]) <= addr_len:
prefix_size = addr_len - int(block[1])
ip >>= prefix_size
else:
raise Exception("Not a valid CIDR notation: %s" % addr)
if addr_family is socket.AF_INET:
self._network_list_v4.append((ip, prefix_size))
else:
self._network_list_v6.append((ip, prefix_size))
def __contains__(self, addr):
addr_family = is_ip(addr)
if addr_family is socket.AF_INET:
ip, = struct.unpack("!I", socket.inet_aton(addr))
return any(map(lambda n_ps: n_ps[0] == ip >> n_ps[1],
self._network_list_v4))
elif addr_family is socket.AF_INET6:
hi, lo = struct.unpack("!QQ", inet_pton(addr_family, addr))
ip = (hi << 64) | lo
return any(map(lambda n_ps: n_ps[0] == ip >> n_ps[1],
self._network_list_v6))
else:
return False
def __cmp__(self, other):
return cmp(self.addrs_str, other.addrs_str)
def __eq__(self, other):
return self.addrs_str == other.addrs_str
def __ne__(self, other):
return self.addrs_str != other.addrs_str
class PortRange(object):
def __init__(self, range_str):
self.range_str = to_str(range_str)
self.range = set()
range_str = to_str(range_str).split(',')
for item in range_str:
try:
int_range = item.split('-')
if len(int_range) == 1:
if item:
self.range.add(int(item))
elif len(int_range) == 2:
int_range[0] = int(int_range[0])
int_range[1] = int(int_range[1])
if int_range[0] < 0:
int_range[0] = 0
if int_range[1] > 65535:
int_range[1] = 65535
i = int_range[0]
while i <= int_range[1]:
self.range.add(i)
i += 1
except Exception as e:
logging.error(e)
def __contains__(self, val):
return val in self.range
def __cmp__(self, other):
return cmp(self.range_str, other.range_str)
def __eq__(self, other):
return self.range_str == other.range_str
def __ne__(self, other):
return self.range_str != other.range_str
class UDPAsyncDNSHandler(object):
dns_cache = lru_cache.LRUCache(timeout=1800)
def __init__(self, params):
self.params = params
self.remote_addr = None
self.call_back = None
def resolve(self, dns_resolver, remote_addr, call_back):
if remote_addr in UDPAsyncDNSHandler.dns_cache:
if call_back:
call_back("", remote_addr, UDPAsyncDNSHandler.dns_cache[remote_addr], self.params)
else:
self.call_back = call_back
self.remote_addr = remote_addr
dns_resolver.resolve(remote_addr[0], self._handle_dns_resolved)
UDPAsyncDNSHandler.dns_cache.sweep()
def _handle_dns_resolved(self, result, error):
if error:
logging.error("%s when resolve DNS" % (error,)) #drop
return self.call_back(error, self.remote_addr, None, self.params)
if result:
ip = result[1]
if ip:
return self.call_back("", self.remote_addr, ip, self.params)
logging.warning("can't resolve %s" % (self.remote_addr,))
return self.call_back("fail to resolve", self.remote_addr, None, self.params)
def test_inet_conv():
ipv4 = b'8.8.4.4'
b = inet_pton(socket.AF_INET, ipv4)
assert inet_ntop(socket.AF_INET, b) == ipv4
ipv6 = b'2404:6800:4005:805::1011'
b = inet_pton(socket.AF_INET6, ipv6)
assert inet_ntop(socket.AF_INET6, b) == ipv6
def test_parse_header():
assert parse_header(b'\x03\x0ewww.google.com\x00\x50') == \
(0, b'www.google.com', 80, 18)
assert parse_header(b'\x01\x08\x08\x08\x08\x00\x35') == \
(0, b'8.8.8.8', 53, 7)
assert parse_header((b'\x04$\x04h\x00@\x05\x08\x05\x00\x00\x00\x00\x00'
b'\x00\x10\x11\x00\x50')) == \
(0, b'2404:6800:4005:805::1011', 80, 19)
def test_pack_header():
assert pack_addr(b'8.8.8.8') == b'\x01\x08\x08\x08\x08'
assert pack_addr(b'2404:6800:4005:805::1011') == \
b'\x04$\x04h\x00@\x05\x08\x05\x00\x00\x00\x00\x00\x00\x10\x11'
assert pack_addr(b'www.google.com') == b'\x03\x0ewww.google.com'
def test_ip_network():
ip_network = IPNetwork('127.0.0.0/24,::ff:1/112,::1,192.168.1.1,192.0.2.0')
assert '127.0.0.1' in ip_network
assert '127.0.1.1' not in ip_network
assert ':ff:ffff' in ip_network
assert '::ffff:1' not in ip_network
assert '::1' in ip_network
assert '::2' not in ip_network
assert '192.168.1.1' in ip_network
assert '192.168.1.2' not in ip_network
assert '192.0.2.1' in ip_network
assert '192.0.3.1' in ip_network # 192.0.2.0 is treated as 192.0.2.0/23
assert 'www.google.com' not in ip_network
if __name__ == '__main__':
test_inet_conv()
test_parse_header()
test_pack_header()
test_ip_network()

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python
#
# Copyright 2015 clowwindy
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import, division, print_function, \
with_statement

View File

@ -0,0 +1,135 @@
#!/usr/bin/env python
# Copyright (c) 2014 clowwindy
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from __future__ import absolute_import, division, print_function, \
with_statement
import logging
from ctypes import CDLL, c_char_p, c_int, c_ulonglong, byref, \
create_string_buffer, c_void_p
__all__ = ['ciphers']
libsodium = None
loaded = False
buf_size = 2048
# for salsa20 and chacha20
BLOCK_SIZE = 64
def load_libsodium():
global loaded, libsodium, buf
from ctypes.util import find_library
for p in ('sodium',):
libsodium_path = find_library(p)
if libsodium_path:
break
else:
raise Exception('libsodium not found')
logging.info('loading libsodium from %s', libsodium_path)
libsodium = CDLL(libsodium_path)
libsodium.sodium_init.restype = c_int
libsodium.crypto_stream_salsa20_xor_ic.restype = c_int
libsodium.crypto_stream_salsa20_xor_ic.argtypes = (c_void_p, c_char_p,
c_ulonglong,
c_char_p, c_ulonglong,
c_char_p)
libsodium.crypto_stream_chacha20_xor_ic.restype = c_int
libsodium.crypto_stream_chacha20_xor_ic.argtypes = (c_void_p, c_char_p,
c_ulonglong,
c_char_p, c_ulonglong,
c_char_p)
libsodium.sodium_init()
buf = create_string_buffer(buf_size)
loaded = True
class Salsa20Crypto(object):
def __init__(self, cipher_name, key, iv, op):
if not loaded:
load_libsodium()
self.key = key
self.iv = iv
self.key_ptr = c_char_p(key)
self.iv_ptr = c_char_p(iv)
if cipher_name == b'salsa20':
self.cipher = libsodium.crypto_stream_salsa20_xor_ic
elif cipher_name == b'chacha20':
self.cipher = libsodium.crypto_stream_chacha20_xor_ic
else:
raise Exception('Unknown cipher')
# byte counter, not block counter
self.counter = 0
def update(self, data):
global buf_size, buf
l = len(data)
# we can only prepend some padding to make the encryption align to
# blocks
padding = self.counter % BLOCK_SIZE
if buf_size < padding + l:
buf_size = (padding + l) * 2
buf = create_string_buffer(buf_size)
if padding:
data = (b'\0' * padding) + data
self.cipher(byref(buf), c_char_p(data), padding + l,
self.iv_ptr, int(self.counter / BLOCK_SIZE), self.key_ptr)
self.counter += l
# buf is copied to a str object when we access buf.raw
# strip off the padding
return buf.raw[padding:padding + l]
ciphers = {
b'salsa20': (32, 8, Salsa20Crypto),
b'chacha20': (32, 8, Salsa20Crypto),
}
def test_salsa20():
from shadowsocks.crypto import util
cipher = Salsa20Crypto(b'salsa20', b'k' * 32, b'i' * 16, 1)
decipher = Salsa20Crypto(b'salsa20', b'k' * 32, b'i' * 16, 0)
util.run_cipher(cipher, decipher)
def test_chacha20():
from shadowsocks.crypto import util
cipher = Salsa20Crypto(b'chacha20', b'k' * 32, b'i' * 16, 1)
decipher = Salsa20Crypto(b'chacha20', b'k' * 32, b'i' * 16, 0)
util.run_cipher(cipher, decipher)
if __name__ == '__main__':
test_chacha20()
test_salsa20()

View File

@ -0,0 +1,188 @@
#!/usr/bin/env python
# Copyright (c) 2014 clowwindy
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from __future__ import absolute_import, division, print_function, \
with_statement
import logging
from ctypes import CDLL, c_char_p, c_int, c_long, byref,\
create_string_buffer, c_void_p
__all__ = ['ciphers']
libcrypto = None
loaded = False
buf_size = 2048
def load_openssl():
global loaded, libcrypto, buf
from ctypes.util import find_library
for p in ('crypto', 'eay32', 'libeay32'):
libcrypto_path = find_library(p)
if libcrypto_path:
break
else:
raise Exception('libcrypto(OpenSSL) not found')
logging.info('loading libcrypto from %s', libcrypto_path)
libcrypto = CDLL(libcrypto_path)
libcrypto.EVP_get_cipherbyname.restype = c_void_p
libcrypto.EVP_CIPHER_CTX_new.restype = c_void_p
libcrypto.EVP_CipherInit_ex.argtypes = (c_void_p, c_void_p, c_char_p,
c_char_p, c_char_p, c_int)
libcrypto.EVP_CipherUpdate.argtypes = (c_void_p, c_void_p, c_void_p,
c_char_p, c_int)
libcrypto.EVP_CIPHER_CTX_cleanup.argtypes = (c_void_p,)
libcrypto.EVP_CIPHER_CTX_free.argtypes = (c_void_p,)
if hasattr(libcrypto, 'OpenSSL_add_all_ciphers'):
libcrypto.OpenSSL_add_all_ciphers()
buf = create_string_buffer(buf_size)
loaded = True
def load_cipher(cipher_name):
func_name = b'EVP_' + cipher_name.replace(b'-', b'_')
if bytes != str:
func_name = str(func_name, 'utf-8')
cipher = getattr(libcrypto, func_name, None)
if cipher:
cipher.restype = c_void_p
return cipher()
return None
class CtypesCrypto(object):
def __init__(self, cipher_name, key, iv, op):
if not loaded:
load_openssl()
self._ctx = None
cipher = libcrypto.EVP_get_cipherbyname(cipher_name)
if not cipher:
cipher = load_cipher(cipher_name)
if not cipher:
raise Exception('cipher %s not found in libcrypto' % cipher_name)
key_ptr = c_char_p(key)
iv_ptr = c_char_p(iv)
self._ctx = libcrypto.EVP_CIPHER_CTX_new()
if not self._ctx:
raise Exception('can not create cipher context')
r = libcrypto.EVP_CipherInit_ex(self._ctx, cipher, None,
key_ptr, iv_ptr, c_int(op))
if not r:
self.clean()
raise Exception('can not initialize cipher context')
def update(self, data):
global buf_size, buf
cipher_out_len = c_long(0)
l = len(data)
if buf_size < l:
buf_size = l * 2
buf = create_string_buffer(buf_size)
libcrypto.EVP_CipherUpdate(self._ctx, byref(buf),
byref(cipher_out_len), c_char_p(data), l)
# buf is copied to a str object when we access buf.raw
return buf.raw[:cipher_out_len.value]
def __del__(self):
self.clean()
def clean(self):
if self._ctx:
libcrypto.EVP_CIPHER_CTX_cleanup(self._ctx)
libcrypto.EVP_CIPHER_CTX_free(self._ctx)
ciphers = {
b'aes-128-cfb': (16, 16, CtypesCrypto),
b'aes-192-cfb': (24, 16, CtypesCrypto),
b'aes-256-cfb': (32, 16, CtypesCrypto),
b'aes-128-ofb': (16, 16, CtypesCrypto),
b'aes-192-ofb': (24, 16, CtypesCrypto),
b'aes-256-ofb': (32, 16, CtypesCrypto),
b'aes-128-ctr': (16, 16, CtypesCrypto),
b'aes-192-ctr': (24, 16, CtypesCrypto),
b'aes-256-ctr': (32, 16, CtypesCrypto),
b'aes-128-cfb8': (16, 16, CtypesCrypto),
b'aes-192-cfb8': (24, 16, CtypesCrypto),
b'aes-256-cfb8': (32, 16, CtypesCrypto),
b'aes-128-cfb1': (16, 16, CtypesCrypto),
b'aes-192-cfb1': (24, 16, CtypesCrypto),
b'aes-256-cfb1': (32, 16, CtypesCrypto),
b'bf-cfb': (16, 8, CtypesCrypto),
b'camellia-128-cfb': (16, 16, CtypesCrypto),
b'camellia-192-cfb': (24, 16, CtypesCrypto),
b'camellia-256-cfb': (32, 16, CtypesCrypto),
b'cast5-cfb': (16, 8, CtypesCrypto),
b'des-cfb': (8, 8, CtypesCrypto),
b'idea-cfb': (16, 8, CtypesCrypto),
b'rc2-cfb': (16, 8, CtypesCrypto),
b'rc4': (16, 0, CtypesCrypto),
b'seed-cfb': (16, 16, CtypesCrypto),
}
def run_method(method):
from shadowsocks.crypto import util
cipher = CtypesCrypto(method, b'k' * 32, b'i' * 16, 1)
decipher = CtypesCrypto(method, b'k' * 32, b'i' * 16, 0)
util.run_cipher(cipher, decipher)
def test_aes_128_cfb():
run_method(b'aes-128-cfb')
def test_aes_256_cfb():
run_method(b'aes-256-cfb')
def test_aes_128_cfb8():
run_method(b'aes-128-cfb8')
def test_aes_256_ofb():
run_method(b'aes-256-ofb')
def test_aes_256_ctr():
run_method(b'aes-256-ctr')
def test_bf_cfb():
run_method(b'bf-cfb')
def test_rc4():
run_method(b'rc4')
if __name__ == '__main__':
test_aes_128_cfb()

View File

@ -0,0 +1,199 @@
#!/usr/bin/env python
#
# Copyright 2015 clowwindy
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import, division, print_function, \
with_statement
from ctypes import c_char_p, c_int, c_long, byref,\
create_string_buffer, c_void_p
from shadowsocks import common
from shadowsocks.crypto import util
__all__ = ['ciphers']
libcrypto = None
loaded = False
buf_size = 2048
def load_openssl():
global loaded, libcrypto, buf
libcrypto = util.find_library(('crypto', 'eay32'),
'EVP_get_cipherbyname',
'libcrypto')
if libcrypto is None:
raise Exception('libcrypto(OpenSSL) not found')
libcrypto.EVP_get_cipherbyname.restype = c_void_p
libcrypto.EVP_CIPHER_CTX_new.restype = c_void_p
libcrypto.EVP_CipherInit_ex.argtypes = (c_void_p, c_void_p, c_char_p,
c_char_p, c_char_p, c_int)
libcrypto.EVP_CipherUpdate.argtypes = (c_void_p, c_void_p, c_void_p,
c_char_p, c_int)
if hasattr(libcrypto, "EVP_CIPHER_CTX_cleanup"):
libcrypto.EVP_CIPHER_CTX_cleanup.argtypes = (c_void_p,)
else:
libcrypto.EVP_CIPHER_CTX_reset.argtypes = (c_void_p,)
libcrypto.EVP_CIPHER_CTX_free.argtypes = (c_void_p,)
libcrypto.RAND_bytes.restype = c_int
libcrypto.RAND_bytes.argtypes = (c_void_p, c_int)
if hasattr(libcrypto, 'OpenSSL_add_all_ciphers'):
libcrypto.OpenSSL_add_all_ciphers()
buf = create_string_buffer(buf_size)
loaded = True
def load_cipher(cipher_name):
func_name = 'EVP_' + cipher_name.replace('-', '_')
cipher = getattr(libcrypto, func_name, None)
if cipher:
cipher.restype = c_void_p
return cipher()
return None
def rand_bytes(length):
if not loaded:
load_openssl()
buf = create_string_buffer(length)
r = libcrypto.RAND_bytes(buf, length)
if r <= 0:
raise Exception('RAND_bytes return error')
return buf.raw
class OpenSSLCrypto(object):
def __init__(self, cipher_name, key, iv, op):
self._ctx = None
if not loaded:
load_openssl()
cipher = libcrypto.EVP_get_cipherbyname(common.to_bytes(cipher_name))
if not cipher:
cipher = load_cipher(cipher_name)
if not cipher:
raise Exception('cipher %s not found in libcrypto' % cipher_name)
key_ptr = c_char_p(key)
iv_ptr = c_char_p(iv)
self._ctx = libcrypto.EVP_CIPHER_CTX_new()
if not self._ctx:
raise Exception('can not create cipher context')
r = libcrypto.EVP_CipherInit_ex(self._ctx, cipher, None,
key_ptr, iv_ptr, c_int(op))
if not r:
self.clean()
raise Exception('can not initialize cipher context')
def update(self, data):
global buf_size, buf
cipher_out_len = c_long(0)
l = len(data)
if buf_size < l:
buf_size = l * 2
buf = create_string_buffer(buf_size)
libcrypto.EVP_CipherUpdate(self._ctx, byref(buf),
byref(cipher_out_len), c_char_p(data), l)
# buf is copied to a str object when we access buf.raw
return buf.raw[:cipher_out_len.value]
def __del__(self):
self.clean()
def clean(self):
if self._ctx:
if hasattr(libcrypto, "EVP_CIPHER_CTX_cleanup"):
libcrypto.EVP_CIPHER_CTX_cleanup(self._ctx)
else:
libcrypto.EVP_CIPHER_CTX_reset(self._ctx)
libcrypto.EVP_CIPHER_CTX_free(self._ctx)
ciphers = {
'aes-128-cbc': (16, 16, OpenSSLCrypto),
'aes-192-cbc': (24, 16, OpenSSLCrypto),
'aes-256-cbc': (32, 16, OpenSSLCrypto),
'aes-128-cfb': (16, 16, OpenSSLCrypto),
'aes-192-cfb': (24, 16, OpenSSLCrypto),
'aes-256-cfb': (32, 16, OpenSSLCrypto),
'aes-128-ofb': (16, 16, OpenSSLCrypto),
'aes-192-ofb': (24, 16, OpenSSLCrypto),
'aes-256-ofb': (32, 16, OpenSSLCrypto),
'aes-128-ctr': (16, 16, OpenSSLCrypto),
'aes-192-ctr': (24, 16, OpenSSLCrypto),
'aes-256-ctr': (32, 16, OpenSSLCrypto),
'aes-128-cfb8': (16, 16, OpenSSLCrypto),
'aes-192-cfb8': (24, 16, OpenSSLCrypto),
'aes-256-cfb8': (32, 16, OpenSSLCrypto),
'aes-128-cfb1': (16, 16, OpenSSLCrypto),
'aes-192-cfb1': (24, 16, OpenSSLCrypto),
'aes-256-cfb1': (32, 16, OpenSSLCrypto),
'bf-cfb': (16, 8, OpenSSLCrypto),
'camellia-128-cfb': (16, 16, OpenSSLCrypto),
'camellia-192-cfb': (24, 16, OpenSSLCrypto),
'camellia-256-cfb': (32, 16, OpenSSLCrypto),
'cast5-cfb': (16, 8, OpenSSLCrypto),
'des-cfb': (8, 8, OpenSSLCrypto),
'idea-cfb': (16, 8, OpenSSLCrypto),
'rc2-cfb': (16, 8, OpenSSLCrypto),
'rc4': (16, 0, OpenSSLCrypto),
'seed-cfb': (16, 16, OpenSSLCrypto),
}
def run_method(method):
cipher = OpenSSLCrypto(method, b'k' * 32, b'i' * 16, 1)
decipher = OpenSSLCrypto(method, b'k' * 32, b'i' * 16, 0)
util.run_cipher(cipher, decipher)
def test_aes_128_cfb():
run_method('aes-128-cfb')
def test_aes_256_cfb():
run_method('aes-256-cfb')
def test_aes_128_cfb8():
run_method('aes-128-cfb8')
def test_aes_256_ofb():
run_method('aes-256-ofb')
def test_aes_256_ctr():
run_method('aes-256-ctr')
def test_bf_cfb():
run_method('bf-cfb')
def test_rc4():
run_method('rc4')
if __name__ == '__main__':
test_aes_128_cfb()

Some files were not shown because too many files have changed in this diff Show More