├── 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