├── README.md
├── local
├── .gitignore
├── GeoIP.dat
├── Microsoft.VC90.CRT.manifest
├── SwitchyOptions.bak
├── addto-startup.js
├── addto-startup.py
├── cacert.pem
├── dnsproxy.py
├── goagent-gtk.py
├── goagent-osx.command
├── goagent-uv.exe
├── goagent.exe
├── msvcr90.dll
├── ntlmaps.bat
├── packages.egg
├── proxy.bat
├── proxy.ini
├── proxy.pac
├── proxy.py
├── proxy.sh
├── proxylib.py
├── python27.dll
├── python27.exe
└── python27.zip
└── server
├── .gitignore
├── gae
├── app.template.yaml
├── gae.py
└── legacy.py
├── google_appengine.zip
├── php
├── index.js
├── index.php
├── index.py
└── relay.php
├── uploader.bat
├── uploader.py
├── uploadvps.bat
├── uploadvps.py
└── vps
├── goagentvps.py
├── goagentvps.sh
├── limits.conf
├── supervisor-3.1.3.egg
├── supervisord-goagentvps.conf
└── sysctl.conf
/README.md:
--------------------------------------------------------------------------------
1 | goagent 3.2.3 [http://git.io/goa](https://nodeload.github.com/goagent/goagent/legacy.zip/3.0)
2 |
3 | ## Issues
4 | * https://code.google.com/p/goagent/issues/list
5 |
6 | ## Documents
7 | | | |
8 | | -------- | :---- |
9 | | Simple Guide | https://github.com/goagent/goagent/blob/wiki/SimpleGuide.md |
10 | | Graphic Guide | https://github.com/goagent/goagent/blob/wiki/InstallGuide.md |
11 | | FAQ | https://github.com/goagent/goagent/blob/wiki/FAQ.md |
12 | | Configurations | https://github.com/goagent/goagent/blob/wiki/ConfigIntroduce.md.ini |
13 | | Spam List | https://github.com/goagent/goagent/blob/wiki/SpamList.md |
14 | | History | https://github.com/goagent/goagent/blob/wiki/History.md |
15 |
16 | ## Code
17 | | | |
18 | | -------- | :---- |
19 | | proxy.py | https://github.com/goagent/goagent |
20 | | python27.exe | https://github.com/goagent/pybuild |
21 | | goagent.exe | https://github.com/goagent/taskbar |
22 |
23 | ## License
24 | * [GNU GPL v2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html)
25 |
--------------------------------------------------------------------------------
/local/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *~
3 | *.pyc
4 | *.pac
5 | *.key
6 | *.crt
7 | *.pid
8 | *.user.ini
9 |
--------------------------------------------------------------------------------
/local/GeoIP.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Long-live-shadowsocks/goagent/985cbd55e232833a859e7f2940b54f7dcb6cdb14/local/GeoIP.dat
--------------------------------------------------------------------------------
/local/Microsoft.VC90.CRT.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/local/SwitchyOptions.bak:
--------------------------------------------------------------------------------
1 | eyJjb25maWciOiJ7XCJmaXJzdFRpbWVcIjpcIjpdXCIsXCJwcm94eU1vZGVcIjpcImF1dG9cIixcImF1dG9QYWNTY3JpcHRQYXRoXCI6XCI6bWVtb3J5OlwiLFwicnVsZUxpc3RVcmxcIjpcImh0dHBzOi8vYXV0b3Byb3h5LWdmd2xpc3QuZ29vZ2xlY29kZS5jb20vc3ZuL3RydW5rL2dmd2xpc3QudHh0XCIsXCJydWxlTGlzdFJlbG9hZFwiOlwiNzIwXCIsXCJydWxlTGlzdFByb2ZpbGVJZFwiOlwiR29BZ2VudFwiLFwicnVsZUxpc3RBdXRvUHJveHlcIjp0cnVlLFwic3dpdGNoUnVsZXNcIjp0cnVlLFwicnVsZUxpc3RFbmFibGVkXCI6dHJ1ZSxcInBhY1NjcmlwdERhdGFcIjpcIlwiLFwicHJveHlTZXJ2ZXJcIjpcIlwiLFwicXVpY2tTd2l0Y2hcIjpmYWxzZSxcInF1aWNrU3dpdGNoVHlwZVwiOlwiYmluYXJ5XCIsXCJyZWFwcGx5U2VsZWN0ZWRQcm9maWxlXCI6dHJ1ZSxcImNvbmZpcm1EZWxldGlvblwiOmZhbHNlLFwicnVsZXNGaXJzdFRpbWVcIjpcIjtdXCIsXCJtb25pdG9yUHJveHlDaGFuZ2VzXCI6ZmFsc2UsXCJwcmV2ZW50UHJveHlDaGFuZ2VzXCI6ZmFsc2UsXCJsYXN0TGlzdFVwZGF0ZVwiOlwiVGh1IFNlcCAxMSAyMDE0IDE2OjE2OjMxIEdNVCswODAwIChDaGluYSBTdGFuZGFyZCBUaW1lKVwiLFwicmVmcmVzaFRhYlwiOmZhbHNlLFwic3RhcnR1cFByb2ZpbGVJZFwiOlwiXCIsXCJxdWlja1J1bGVQcm9maWxlSWRcIjpcIkdvQWdlbnRcIixcInF1aWNrUnVsZVBhdHRlcm5UeXBlXCI6XCJ3aWxkY2FyZFwiLFwicHJveHlDb25maWdVcmxcIjpcIjptZW1vcnk6XCJ9IiwiZGVmYXVsdFJ1bGUiOiJ7XCJpZFwiOlwiZGVmYXVsdFJ1bGVcIixcIm5hbWVcIjpcIkRlZmF1bHQgUnVsZVwiLFwidXJsUGF0dGVyblwiOlwiXCIsXCJwYXR0ZXJuVHlwZVwiOlwid2lsZGNhcmRcIixcInByb2ZpbGVJZFwiOlwiZGlyZWN0XCJ9IiwicHJvZmlsZXMiOiJ7XCJHb0FnZW50XCI6e1wibmFtZVwiOlwiR29BZ2VudFwiLFwicHJveHlNb2RlXCI6XCJtYW51YWxcIixcInByb3h5SHR0cFwiOlwiMTI3LjAuMC4xOjgwODdcIixcInVzZVNhbWVQcm94eVwiOnRydWUsXCJwcm94eUh0dHBzXCI6XCJcIixcInByb3h5RnRwXCI6XCJcIixcInByb3h5U29ja3NcIjpcIlwiLFwic29ja3NWZXJzaW9uXCI6NCxcInByb3h5RXhjZXB0aW9uc1wiOlwibG9jYWxob3N0OyAxMjcuMC4wLjE7IDxsb2NhbD5cIixcInByb3h5Q29uZmlnVXJsXCI6XCJcIixcImNvbG9yXCI6XCJibHVlXCIsXCJpZFwiOlwiR29BZ2VudFwifSxcIkdvQWdlbnQgUEhQXCI6e1wibmFtZVwiOlwiR29BZ2VudCBQQUNcIixcInByb3h5TW9kZVwiOlwiYXV0b1wiLFwicHJveHlIdHRwXCI6XCJcIixcInVzZVNhbWVQcm94eVwiOnRydWUsXCJwcm94eUh0dHBzXCI6XCJcIixcInByb3h5RnRwXCI6XCJcIixcInByb3h5U29ja3NcIjpcIlwiLFwic29ja3NWZXJzaW9uXCI6NSxcInByb3h5RXhjZXB0aW9uc1wiOlwibG9jYWxob3N0OyAxMjcuMC4wLjE7IDxsb2NhbD5cIixcInByb3h5Q29uZmlnVXJsXCI6XCJodHRwOi8vMTI3LjAuMC4xOjgwODYvcHJveHkucGFjXCIsXCJjb2xvclwiOlwiYmx1ZVwiLFwiaWRcIjpcIkdvQWdlbnQgUEhQXCJ9LFwiR29BZ2VudCBQSFAyXCI6e1wibmFtZVwiOlwiR29BZ2VudCBQSFBcIixcInByb3h5TW9kZVwiOlwibWFudWFsXCIsXCJwcm94eUh0dHBcIjpcIjEyNy4wLjAuMTo4MDg4XCIsXCJ1c2VTYW1lUHJveHlcIjp0cnVlLFwicHJveHlIdHRwc1wiOlwiXCIsXCJwcm94eUZ0cFwiOlwiXCIsXCJwcm94eVNvY2tzXCI6XCJcIixcInNvY2tzVmVyc2lvblwiOjQsXCJwcm94eUV4Y2VwdGlvbnNcIjpcImxvY2FsaG9zdDsgMTI3LjAuMC4xOyA8bG9jYWw+XCIsXCJwcm94eUNvbmZpZ1VybFwiOlwiXCIsXCJjb2xvclwiOlwiYmx1ZVwiLFwiaWRcIjpcIkdvQWdlbnQgUEhQMlwifX0iLCJxdWlja1N3aXRjaFByb2ZpbGVzIjoiW1wiZGlyZWN0XCJdIiwicnVsZXMiOiJ7XCJnZ3BodFwiOntcIm5hbWVcIjpcImdncGh0XCIsXCJ1cmxQYXR0ZXJuXCI6XCIqOi8vKi5nZ3BodC5jb20vKlwiLFwicGF0dGVyblR5cGVcIjpcIndpbGRjYXJkXCIsXCJwcm9maWxlSWRcIjpcIkdvQWdlbnRcIixcImlkXCI6XCJnZ3BodFwifSxcIk5ldyBSdWxlM1wiOntcIm5hbWVcIjpcImdvb2dsZVwiLFwidXJsUGF0dGVyblwiOlwiKjovLyouZ29vZ2xlLmNvbS4qLypcIixcInBhdHRlcm5UeXBlXCI6XCJ3aWxkY2FyZFwiLFwicHJvZmlsZUlkXCI6XCJHb0FnZW50XCIsXCJpZFwiOlwiTmV3IFJ1bGUzXCJ9LFwiTmV3IFJ1bGUyXCI6e1wibmFtZVwiOlwiZ29vZ2xlXCIsXCJ1cmxQYXR0ZXJuXCI6XCIqOi8vKi5nb29nbGUqLmNvbS8qXCIsXCJwYXR0ZXJuVHlwZVwiOlwid2lsZGNhcmRcIixcInByb2ZpbGVJZFwiOlwiR29BZ2VudFwiLFwiaWRcIjpcIk5ldyBSdWxlMlwifSxcIk5ldyBSdWxlOFwiOntcIm5hbWVcIjpcIndpa2lwZWRpYVwiLFwidXJsUGF0dGVyblwiOlwiKjovLyoud2lraXBlZGlhLm9yZy8qXCIsXCJwYXR0ZXJuVHlwZVwiOlwid2lsZGNhcmRcIixcInByb2ZpbGVJZFwiOlwiR29BZ2VudFwiLFwiaWRcIjpcIk5ldyBSdWxlOFwifX0iLCJzZWxlY3RlZFByb2ZpbGUiOiJ7XCJpZFwiOlwiYXV0b1wiLFwibmFtZVwiOlwiW0F1dG8gU3dpdGNoXVwiLFwicHJveHlNb2RlXCI6XCJhdXRvXCIsXCJjb2xvclwiOlwiYXV0by1ibHVlXCIsXCJpc0F1dG9tYXRpY01vZGVQcm9maWxlXCI6dHJ1ZSxcInByb3h5Q29uZmlnVXJsXCI6XCI6bWVtb3J5OlwifSJ9
--------------------------------------------------------------------------------
/local/addto-startup.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Long-live-shadowsocks/goagent/985cbd55e232833a859e7f2940b54f7dcb6cdb14/local/addto-startup.js
--------------------------------------------------------------------------------
/local/addto-startup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding:utf-8
3 |
4 | from __future__ import with_statement
5 |
6 | __version__ = '1.0'
7 |
8 | import sys
9 | import os
10 | import re
11 | import time
12 | import ctypes
13 | import platform
14 |
15 | python2 = os.popen('python2 -V 2>&1').read().startswith('Python 2.') and 'python2' or 'python'
16 |
17 | def addto_startup_linux():
18 | filename = os.path.abspath(__file__)
19 | dirname = os.path.dirname(filename)
20 | #you can change it to 'proxy.py' if you like :)
21 | scriptname = 'goagent-gtk.py'
22 | DESKTOP_FILE = '''\
23 | [Desktop Entry]
24 | Type=Application
25 | Categories=Network;Proxy;
26 | Exec=/usr/bin/env %s "%s/%s"
27 | Icon=%s/goagent-logo.png
28 | Hidden=false
29 | NoDisplay=false
30 | X-GNOME-Autostart-enabled=true
31 | Name=GoAgent GTK
32 | Comment=GoAgent GTK Launcher
33 | ''' % (python2, dirname , scriptname , dirname)
34 | #sometimes maybe /etc/xdg/autostart , ~/.kde/Autostart/ , ~/.config/openbox/autostart
35 | for dirname in map(os.path.expanduser, ['~/.config/autostart']):
36 | if not os.path.exists(dirname):
37 | os.makedirs(dirname)
38 | if os.path.isdir(dirname):
39 | filename = os.path.join(dirname, 'goagent-gtk.desktop')
40 | with open(filename, 'w') as fp:
41 | fp.write(DESKTOP_FILE)
42 | # os.chmod(filename, 0755)
43 |
44 |
45 | def addto_startup_osx():
46 | if os.getuid() != 0:
47 | print 'please use sudo run this script'
48 | sys.exit()
49 | import plistlib
50 | plist = dict(
51 | GroupName = 'wheel',
52 | Label = 'org.goagent.macos',
53 | ProgramArguments = list([
54 | '/usr/bin/%s' % python2,
55 | os.path.join(os.path.abspath(os.path.dirname(__file__)), 'proxy.py')
56 | ]),
57 | RunAtLoad = True,
58 | UserName = 'root',
59 | WorkingDirectory = os.path.abspath(os.path.dirname(__file__)),
60 | StandardOutPath = '/var/log/goagent.log',
61 | StandardErrorPath = '/var/log/goagent.log',
62 | KeepAlive = dict(
63 | SuccessfulExit = False,
64 | )
65 | )
66 | filename = '/Library/LaunchDaemons/org.goagent.macos.plist'
67 | print 'write plist to %s' % filename
68 | plistlib.writePlist(plist, filename)
69 | print 'write plist to %s done' % filename
70 | print 'Adding CA.crt to system keychain, You may need to input your password...'
71 | cmd = 'sudo security add-trusted-cert -d -r trustRoot -k "/Library/Keychains/System.keychain" "%s/CA.crt"' % os.path.abspath(os.path.dirname(__file__))
72 | if os.system(cmd) != 0:
73 | print 'Adding CA.crt to system keychain Failed!'
74 | sys.exit(0)
75 | print 'Adding CA.crt to system keychain Done'
76 | print 'To start goagent right now, try this command: sudo launchctl load /Library/LaunchDaemons/org.goagent.macos.plist'
77 | print 'To checkout log file: using Console.app to locate /var/log/goagent.log'
78 |
79 | install_sharp_osx()
80 |
81 |
82 | def install_sharp_osx():
83 | # extracted from SwitchySharp.crx
84 | extension_id = 'dpplabbmogkhghncfbfdeeokoefdjegm'
85 | extension_version = '1.10.2'
86 | extension_path = '%s/SwitchySharp.crx' % os.path.abspath(os.path.dirname(__file__))
87 |
88 | dest_path = '/Library/Application Support/Google/Chrome/External Extensions'
89 | dest_file = '%s/%s.json' % (dest_path, extension_id)
90 | print 'Installing SwitchySharp for Chrome...'
91 | cmd = 'mkdir -p "%s"' % dest_path
92 | if os.system(cmd) != 0:
93 | print 'Create Chrome External Extensions folder Failed!'
94 | sys.exit(0)
95 |
96 | json_dict = {'external_crx': extension_path,
97 | 'external_version': extension_version}
98 | with open(dest_file, 'w') as fp:
99 | import json
100 | json.dump(json_dict, fp)
101 | print 'Installing SwitchySharp done.'
102 |
103 |
104 | def addto_startup_windows():
105 | if 1 == ctypes.windll.user32.MessageBoxW(None, u'是否将goagent.exe加入到启动项?', u'GoAgent 对话框', 1):
106 | if 1 == ctypes.windll.user32.MessageBoxW(None, u'是否显示托盘区图标?', u'GoAgent 对话框', 1):
107 | pass
108 |
109 |
110 | def addto_startup_unknown():
111 | print '*** error: Unknown system'
112 |
113 |
114 | def main():
115 | addto_startup_funcs = {
116 | 'Darwin' : addto_startup_osx,
117 | 'Windows' : addto_startup_windows,
118 | 'Linux' : addto_startup_linux,
119 | }
120 | addto_startup_funcs.get(platform.system(), addto_startup_unknown)()
121 |
122 |
123 | if __name__ == '__main__':
124 | try:
125 | main()
126 | except KeyboardInterrupt:
127 | pass
128 |
--------------------------------------------------------------------------------
/local/dnsproxy.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding:utf-8
3 |
4 | __version__ = '1.0'
5 |
6 | import sys
7 | import os
8 | import sysconfig
9 |
10 | sys.path += [os.path.abspath(os.path.join(__file__, '../packages.egg/%s' % x)) for x in ('noarch', sysconfig.get_platform().split('-')[0])]
11 |
12 | import gevent
13 | import gevent.server
14 | import gevent.timeout
15 | import gevent.monkey
16 | gevent.monkey.patch_all(subprocess=True)
17 |
18 | import re
19 | import time
20 | import logging
21 | import heapq
22 | import socket
23 | import select
24 | import struct
25 | import errno
26 | import thread
27 | import dnslib
28 | import Queue
29 | import pygeoip
30 |
31 |
32 | is_local_addr = re.compile(r'(?i)(?:[0-9a-f:]+0:5efe:)?(?:127(?:\.\d+){3}|10(?:\.\d+){3}|192\.168(?:\.\d+){2}|172\.(?:1[6-9]|2\d|3[01])(?:\.\d+){2})').match
33 |
34 |
35 | def get_dnsserver_list():
36 | if os.name == 'nt':
37 | import ctypes, ctypes.wintypes, struct, socket
38 | DNS_CONFIG_DNS_SERVER_LIST = 6
39 | buf = ctypes.create_string_buffer(2048)
40 | ctypes.windll.dnsapi.DnsQueryConfig(DNS_CONFIG_DNS_SERVER_LIST, 0, None, None, ctypes.byref(buf), ctypes.byref(ctypes.wintypes.DWORD(len(buf))))
41 | ipcount = struct.unpack('I', buf[0:4])[0]
42 | iplist = [socket.inet_ntoa(buf[i:i+4]) for i in xrange(4, ipcount*4+4, 4)]
43 | return iplist
44 | elif os.path.isfile('/etc/resolv.conf'):
45 | with open('/etc/resolv.conf', 'rb') as fp:
46 | return re.findall(r'(?m)^nameserver\s+(\S+)', fp.read())
47 | else:
48 | logging.warning("get_dnsserver_list failed: unsupport platform '%s-%s'", sys.platform, os.name)
49 | return []
50 |
51 |
52 | def parse_hostport(host, default_port=80):
53 | m = re.match(r'(.+)[#](\d+)$', host)
54 | if m:
55 | return m.group(1).strip('[]'), int(m.group(2))
56 | else:
57 | return host.strip('[]'), default_port
58 |
59 |
60 | class ExpireCache(object):
61 | """ A dictionary-like object, supporting expire semantics."""
62 | def __init__(self, max_size=1024):
63 | self.__maxsize = max_size
64 | self.__values = {}
65 | self.__expire_times = {}
66 | self.__expire_heap = []
67 |
68 | def size(self):
69 | return len(self.__values)
70 |
71 | def clear(self):
72 | self.__values.clear()
73 | self.__expire_times.clear()
74 | del self.__expire_heap[:]
75 |
76 | def exists(self, key):
77 | return key in self.__values
78 |
79 | def set(self, key, value, expire):
80 | try:
81 | et = self.__expire_times[key]
82 | pos = self.__expire_heap.index((et, key))
83 | del self.__expire_heap[pos]
84 | if pos < len(self.__expire_heap):
85 | heapq._siftup(self.__expire_heap, pos)
86 | except KeyError:
87 | pass
88 | et = int(time.time() + expire)
89 | self.__expire_times[key] = et
90 | heapq.heappush(self.__expire_heap, (et, key))
91 | self.__values[key] = value
92 | self.cleanup()
93 |
94 | def get(self, key):
95 | et = self.__expire_times[key]
96 | if et < time.time():
97 | self.cleanup()
98 | raise KeyError(key)
99 | return self.__values[key]
100 |
101 | def delete(self, key):
102 | et = self.__expire_times.pop(key)
103 | pos = self.__expire_heap.index((et, key))
104 | del self.__expire_heap[pos]
105 | if pos < len(self.__expire_heap):
106 | heapq._siftup(self.__expire_heap, pos)
107 | del self.__values[key]
108 |
109 | def cleanup(self):
110 | t = int(time.time())
111 | eh = self.__expire_heap
112 | ets = self.__expire_times
113 | v = self.__values
114 | size = self.__maxsize
115 | heappop = heapq.heappop
116 | #Delete expired, ticky
117 | while eh and eh[0][0] <= t or len(v) > size:
118 | _, key = heappop(eh)
119 | del v[key], ets[key]
120 |
121 |
122 | def dnslib_resolve_over_udp(query, dnsservers, timeout, **kwargs):
123 | """
124 | http://gfwrev.blogspot.com/2009/11/gfwdns.html
125 | http://zh.wikipedia.org/wiki/%E5%9F%9F%E5%90%8D%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%BC%93%E5%AD%98%E6%B1%A1%E6%9F%93
126 | http://support.microsoft.com/kb/241352
127 | https://gist.github.com/klzgrad/f124065c0616022b65e5
128 | """
129 | if not isinstance(query, (basestring, dnslib.DNSRecord)):
130 | raise TypeError('query argument requires string/DNSRecord')
131 | blacklist = kwargs.get('blacklist', ())
132 | turstservers = kwargs.get('turstservers', ())
133 | dns_v4_servers = [x for x in dnsservers if ':' not in x]
134 | dns_v6_servers = [x for x in dnsservers if ':' in x]
135 | sock_v4 = sock_v6 = None
136 | socks = []
137 | if dns_v4_servers:
138 | sock_v4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
139 | socks.append(sock_v4)
140 | if dns_v6_servers:
141 | sock_v6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
142 | socks.append(sock_v6)
143 | timeout_at = time.time() + timeout
144 | try:
145 | for _ in xrange(4):
146 | try:
147 | for dnsserver in dns_v4_servers:
148 | if isinstance(query, basestring):
149 | if dnsserver in ('8.8.8.8', '8.8.4.4'):
150 | query = '.'.join(x[:-1] + x[-1].upper() for x in query.split('.')).title()
151 | query = dnslib.DNSRecord(q=dnslib.DNSQuestion(query))
152 | query_data = query.pack()
153 | if query.q.qtype == 1 and dnsserver in ('8.8.8.8', '8.8.4.4'):
154 | query_data = query_data[:-5] + '\xc0\x04' + query_data[-4:]
155 | sock_v4.sendto(query_data, parse_hostport(dnsserver, 53))
156 | for dnsserver in dns_v6_servers:
157 | if isinstance(query, basestring):
158 | query = dnslib.DNSRecord(q=dnslib.DNSQuestion(query, qtype=dnslib.QTYPE.AAAA))
159 | query_data = query.pack()
160 | sock_v6.sendto(query_data, parse_hostport(dnsserver, 53))
161 | while time.time() < timeout_at:
162 | ins, _, _ = select.select(socks, [], [], 0.1)
163 | for sock in ins:
164 | reply_data, reply_address = sock.recvfrom(512)
165 | reply_server = reply_address[0]
166 | record = dnslib.DNSRecord.parse(reply_data)
167 | iplist = [str(x.rdata) for x in record.rr if x.rtype in (1, 28, 255)]
168 | if any(x in blacklist for x in iplist):
169 | logging.warning('query=%r dnsservers=%r record bad iplist=%r', query, dnsservers, iplist)
170 | elif record.header.rcode and not iplist and reply_server in turstservers:
171 | logging.info('query=%r trust reply_server=%r record rcode=%s', query, reply_server, record.header.rcode)
172 | return record
173 | elif iplist:
174 | logging.debug('query=%r reply_server=%r record iplist=%s', query, reply_server, iplist)
175 | return record
176 | else:
177 | logging.debug('query=%r reply_server=%r record null iplist=%s', query, reply_server, iplist)
178 | continue
179 | except socket.error as e:
180 | logging.warning('handle dns query=%s socket: %r', query, e)
181 | raise socket.gaierror(11004, 'getaddrinfo %r from %r failed' % (query, dnsservers))
182 | finally:
183 | for sock in socks:
184 | sock.close()
185 |
186 |
187 | def dnslib_resolve_over_tcp(query, dnsservers, timeout, **kwargs):
188 | """dns query over tcp"""
189 | if not isinstance(query, (basestring, dnslib.DNSRecord)):
190 | raise TypeError('query argument requires string/DNSRecord')
191 | blacklist = kwargs.get('blacklist', ())
192 | def do_resolve(query, dnsserver, timeout, queobj):
193 | if isinstance(query, basestring):
194 | qtype = dnslib.QTYPE.AAAA if ':' in dnsserver else dnslib.QTYPE.A
195 | query = dnslib.DNSRecord(q=dnslib.DNSQuestion(query, qtype=qtype))
196 | query_data = query.pack()
197 | sock_family = socket.AF_INET6 if ':' in dnsserver else socket.AF_INET
198 | sock = socket.socket(sock_family)
199 | rfile = None
200 | try:
201 | sock.settimeout(timeout or None)
202 | sock.connect(parse_hostport(dnsserver, 53))
203 | sock.send(struct.pack('>h', len(query_data)) + query_data)
204 | rfile = sock.makefile('r', 1024)
205 | reply_data_length = rfile.read(2)
206 | if len(reply_data_length) < 2:
207 | raise socket.gaierror(11004, 'getaddrinfo %r from %r failed' % (query, dnsserver))
208 | reply_data = rfile.read(struct.unpack('>h', reply_data_length)[0])
209 | record = dnslib.DNSRecord.parse(reply_data)
210 | iplist = [str(x.rdata) for x in record.rr if x.rtype in (1, 28, 255)]
211 | if any(x in blacklist for x in iplist):
212 | logging.debug('query=%r dnsserver=%r record bad iplist=%r', query, dnsserver, iplist)
213 | raise socket.gaierror(11004, 'getaddrinfo %r from %r failed' % (query, dnsserver))
214 | else:
215 | logging.debug('query=%r dnsserver=%r record iplist=%s', query, dnsserver, iplist)
216 | queobj.put(record)
217 | except socket.error as e:
218 | logging.debug('query=%r dnsserver=%r failed %r', query, dnsserver, e)
219 | queobj.put(e)
220 | finally:
221 | if rfile:
222 | rfile.close()
223 | sock.close()
224 | queobj = Queue.Queue()
225 | for dnsserver in dnsservers:
226 | thread.start_new_thread(do_resolve, (query, dnsserver, timeout, queobj))
227 | for i in range(len(dnsservers)):
228 | try:
229 | result = queobj.get(timeout)
230 | except Queue.Empty:
231 | raise socket.gaierror(11004, 'getaddrinfo %r from %r failed' % (query, dnsservers))
232 | if result and not isinstance(result, Exception):
233 | return result
234 | elif i == len(dnsservers) - 1:
235 | logging.warning('dnslib_resolve_over_tcp %r with %s return %r', query, dnsservers, result)
236 | raise socket.gaierror(11004, 'getaddrinfo %r from %r failed' % (query, dnsservers))
237 |
238 |
239 | class DNSServer(gevent.server.DatagramServer):
240 | """DNS Proxy based on gevent/dnslib"""
241 |
242 | def __init__(self, *args, **kwargs):
243 | dns_blacklist = kwargs.pop('dns_blacklist')
244 | dns_servers = kwargs.pop('dns_servers')
245 | dns_tcpover = kwargs.pop('dns_tcpover', [])
246 | dns_timeout = kwargs.pop('dns_timeout', 2)
247 | super(self.__class__, self).__init__(*args, **kwargs)
248 | self.dns_servers = list(dns_servers)
249 | self.dns_tcpover = tuple(dns_tcpover)
250 | self.dns_intranet_servers = [x for x in self.dns_servers if is_local_addr(x)]
251 | self.dns_blacklist = set(dns_blacklist)
252 | self.dns_timeout = int(dns_timeout)
253 | self.dns_cache = ExpireCache(max_size=65536)
254 | self.dns_trust_servers = set(['8.8.8.8', '8.8.4.4', '2001:4860:4860::8888', '2001:4860:4860::8844'])
255 | for dirname in ('.', '/usr/share/GeoIP/', '/usr/local/share/GeoIP/'):
256 | filename = os.path.join(dirname, 'GeoIP.dat')
257 | if os.path.isfile(filename):
258 | geoip = pygeoip.GeoIP(filename)
259 | for dnsserver in self.dns_servers:
260 | if ':' not in dnsserver and geoip.country_name_by_addr(parse_hostport(dnsserver, 53)[0]) not in ('China',):
261 | self.dns_trust_servers.add(dnsserver)
262 | break
263 |
264 | def do_read(self):
265 | try:
266 | return gevent.server.DatagramServer.do_read(self)
267 | except socket.error as e:
268 | if e[0] not in (errno.ECONNABORTED, errno.ECONNRESET, errno.EPIPE):
269 | raise
270 |
271 | def get_reply_record(self, data):
272 | request = dnslib.DNSRecord.parse(data)
273 | qname = str(request.q.qname).lower()
274 | qtype = request.q.qtype
275 | dnsservers = self.dns_servers
276 | if qname.endswith('.in-addr.arpa'):
277 | ipaddr = '.'.join(reversed(qname[:-13].split('.')))
278 | record = dnslib.DNSRecord(header=dnslib.DNSHeader(id=request.header.id, qr=1,aa=1,ra=1), a=dnslib.RR(qname, rdata=dnslib.A(ipaddr)))
279 | return record
280 | if 'USERDNSDOMAIN' in os.environ:
281 | user_dnsdomain = '.' + os.environ['USERDNSDOMAIN'].lower()
282 | if qname.endswith(user_dnsdomain):
283 | qname = qname[:-len(user_dnsdomain)]
284 | if '.' not in qname:
285 | if not self.dns_intranet_servers:
286 | logging.warning('qname=%r is a plain hostname, need intranet dns server!!!', qname)
287 | return dnslib.DNSRecord(header=dnslib.DNSHeader(id=request.header.id, rcode=3))
288 | qname += user_dnsdomain
289 | dnsservers = self.dns_intranet_servers
290 | try:
291 | return self.dns_cache.get((qname, qtype))
292 | except KeyError:
293 | pass
294 | try:
295 | dns_resolve = dnslib_resolve_over_tcp if qname.endswith(self.dns_tcpover) else dnslib_resolve_over_udp
296 | kwargs = {'blacklist': self.dns_blacklist, 'turstservers': self.dns_trust_servers}
297 | record = dns_resolve(request, dnsservers, self.dns_timeout, **kwargs)
298 | ttl = max(x.ttl for x in record.rr) if record.rr else 600
299 | self.dns_cache.set((qname, qtype), record, ttl * 2)
300 | return record
301 | except socket.gaierror as e:
302 | logging.warning('resolve %r failed: %r', qname, e)
303 | return dnslib.DNSRecord(header=dnslib.DNSHeader(id=request.header.id, rcode=3))
304 |
305 | def handle(self, data, address):
306 | logging.debug('receive from %r data=%r', address, data)
307 | record = self.get_reply_record(data)
308 | return self.sendto(data[:2] + record.pack()[2:], address)
309 |
310 |
311 | def test():
312 | logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(asctime)s %(message)s', datefmt='[%b %d %H:%M:%S]')
313 | dns_servers = '8.8.8.8|8.8.4.4|168.95.1.1|168.95.192.1|223.5.5.5|223.6.6.6|114.114.114.114|114.114.115.115'.split('|')
314 | dns_blacklist = '1.1.1.1|255.255.255.255|74.125.127.102|74.125.155.102|74.125.39.102|74.125.39.113|209.85.229.138|4.36.66.178|8.7.198.45|37.61.54.158|46.82.174.68|59.24.3.173|64.33.88.161|64.33.99.47|64.66.163.251|65.104.202.252|65.160.219.113|66.45.252.237|72.14.205.104|72.14.205.99|78.16.49.15|93.46.8.89|128.121.126.139|159.106.121.75|169.132.13.103|192.67.198.6|202.106.1.2|202.181.7.85|203.161.230.171|203.98.7.65|207.12.88.98|208.56.31.43|209.145.54.50|209.220.30.174|209.36.73.33|211.94.66.147|213.169.251.35|216.221.188.182|216.234.179.13|243.185.187.3|243.185.187.39|23.89.5.60|37.208.111.120|49.2.123.56|54.76.135.1|77.4.7.92|118.5.49.6|188.5.4.96|189.163.17.5|197.4.4.12|249.129.46.48|253.157.14.165|183.207.229.|183.207.232.'.split('|')
315 | dns_tcpover = ['.youtube.com', '.googlevideo.com']
316 | logging.info('serving at port 53...')
317 | DNSServer(('', 53), dns_servers=dns_servers, dns_blacklist=dns_blacklist, dns_tcpover=dns_tcpover).serve_forever()
318 |
319 |
320 | if __name__ == '__main__':
321 | test()
322 |
--------------------------------------------------------------------------------
/local/goagent-gtk.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python2
2 | # coding:utf-8
3 | # Contributor:
4 | # Phus Lu
5 |
6 | __version__ = '1.6'
7 |
8 | GOAGENT_LOGO_DATA = """\
9 | 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
70 | AAAASUVORK5CYII="""
71 |
72 | import sys
73 | import os
74 | import re
75 | import thread
76 | import base64
77 | import platform
78 |
79 | try:
80 | import pygtk
81 | pygtk.require('2.0')
82 | import gtk
83 | # gtk.gdk.threads_init()
84 | except Exception:
85 | sys.exit(os.system(u'gdialog --title "GoAgent GTK" --msgbox "\u8bf7\u5b89\u88c5 python-gtk2" 15 60'.encode(sys.getfilesystemencoding() or sys.getdefaultencoding(), 'replace')))
86 | try:
87 | import pynotify
88 | pynotify.init('GoAgent Notify')
89 | except ImportError:
90 | pynotify = None
91 | try:
92 | import appindicator
93 | except ImportError:
94 | appindicator = None
95 | try:
96 | import vte
97 | except ImportError:
98 | sys.exit(gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, u'请安装 python-vte').run())
99 |
100 |
101 | def spawn_later(seconds, target, *args, **kwargs):
102 | def wrap(*args, **kwargs):
103 | import time
104 | time.sleep(seconds)
105 | return target(*args, **kwargs)
106 | return thread.start_new_thread(wrap, args, kwargs)
107 |
108 |
109 | def drop_desktop():
110 | filename = os.path.abspath(__file__)
111 | dirname = os.path.dirname(filename)
112 | DESKTOP_FILE = '''\
113 | #!/usr/bin/env xdg-open
114 | [Desktop Entry]
115 | Type=Application
116 | Name=GoAgent GTK
117 | Comment=GoAgent GTK Launcher
118 | Categories=Network;Proxy;
119 | Exec=/usr/bin/env python "%s"
120 | Icon=%s/goagent-logo.png
121 | Terminal=false
122 | StartupNotify=true
123 | ''' % (filename, dirname)
124 | for dirname in map(os.path.expanduser, ['~/Desktop', u'~/桌面']):
125 | if os.path.isdir(dirname):
126 | filename = os.path.join(dirname, 'goagent-gtk.desktop')
127 | with open(filename, 'w') as fp:
128 | fp.write(DESKTOP_FILE)
129 | os.chmod(filename, 0755)
130 |
131 |
132 | def should_visible():
133 | import ConfigParser
134 | ConfigParser.RawConfigParser.OPTCRE = re.compile(r'(?P