diff --git a/README.md b/README.md index 2a6b079..9472e79 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ shadowsocks =========== +Current version: 0.9 + shadowsocks is a lightweight tunnel proxy which can help you get through firewalls usage @@ -11,6 +13,7 @@ First, make sure you have Python 2.6 or 2.7. $ python --version Python 2.6.8 + Then edit `config.json`, change the following values: server your server ip or hostname @@ -34,8 +37,15 @@ You can use args to override settings from `config.json`. python local.py -s server_name -p server_port -l local_port -k password python server.py -p server_port -k password - - + +You may want to install gevent for better performance. + + $ apt-get install python-gevent + +Or: + + $ sudo easy_install gevent + troubleshooting --------------- diff --git a/local.py b/local.py index bd6e09d..04f283b 100755 --- a/local.py +++ b/local.py @@ -20,6 +20,14 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import sys + +try: + import gevent, gevent.monkey + gevent.monkey.patch_all(dns=gevent.version_info[0]>=1) +except ImportError: + gevent = None + print >>sys.stderr, 'warning: gevent not found, using threading instead' import socket import select @@ -27,7 +35,6 @@ import SocketServer import struct import string import hashlib -import sys import os import json import logging @@ -43,9 +50,18 @@ def get_table(key): table.sort(lambda x, y: int(a % (ord(x) + i) - a % (ord(y) + i))) return table +def send_all(sock, data): + bytes_sent = 0 + while True: + r = sock.send(data[bytes_sent:]) + if r < 0: + return r + bytes_sent += r + if bytes_sent == len(data): + return bytes_sent class ThreadingTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): - pass + allow_reuse_address = True class Socks5Server(SocketServer.StreamRequestHandler): @@ -55,11 +71,20 @@ class Socks5Server(SocketServer.StreamRequestHandler): while True: r, w, e = select.select(fdset, [], []) if sock in r: - if remote.send(self.encrypt(sock.recv(4096))) <= 0: + data = sock.recv(4096) + if len(data) <= 0: break + result = send_all(remote, self.encrypt(data)) + if result < len(data): + raise Exception('failed to send all data') + if remote in r: - if sock.send(self.decrypt(remote.recv(4096))) <= 0: + data = remote.recv(4096) + if len(data) <= 0: break + result = send_all(sock, self.decrypt(data)) + if result < len(data): + raise Exception('failed to send all data') finally: sock.close() remote.close() @@ -90,7 +115,7 @@ class Socks5Server(SocketServer.StreamRequestHandler): addr = socket.inet_ntoa(addr_ip) addr_to_send += addr_ip elif addrtype == 3: - addr_len = sock.recv(1) + addr_len = self.rfile.read(1) addr = self.rfile.read(ord(addr_len)) addr_to_send += addr_len + addr else: @@ -103,12 +128,13 @@ class Socks5Server(SocketServer.StreamRequestHandler): try: reply = "\x05\x00\x00\x01" reply += socket.inet_aton('0.0.0.0') + struct.pack(">H", 2222) - sock.send(reply) + self.wfile.write(reply) # reply immediately if '-6' in sys.argv[1:]: remote = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) else: remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + remote.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) remote.connect((SERVER, REMOTE_PORT)) self.send_encrypt(remote, addr_to_send) logging.info('connecting %s:%d' % (addr, port[0])) @@ -122,6 +148,7 @@ class Socks5Server(SocketServer.StreamRequestHandler): if __name__ == '__main__': os.chdir(os.path.dirname(__file__) or '.') + print 'shadowsocks v0.9' with open('config.json', 'rb') as f: config = json.load(f) @@ -148,7 +175,6 @@ if __name__ == '__main__': decrypt_table = string.maketrans(encrypt_table, string.maketrans('', '')) try: server = ThreadingTCPServer(('', PORT), Socks5Server) - server.allow_reuse_address = True logging.info("starting server at port %d ..." % PORT) server.serve_forever() except socket.error, e: diff --git a/server.py b/server.py index e9e8944..3beddca 100755 --- a/server.py +++ b/server.py @@ -20,6 +20,14 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import sys + +try: + import gevent, gevent.monkey + gevent.monkey.patch_all(dns=gevent.version_info[0]>=1) +except ImportError: + gevent = None + print >>sys.stderr, 'warning: gevent not found, using threading instead' import socket import select @@ -27,7 +35,6 @@ import SocketServer import struct import string import hashlib -import sys import os import json import logging @@ -43,9 +50,18 @@ def get_table(key): table.sort(lambda x, y: int(a % (ord(x) + i) - a % (ord(y) + i))) return table +def send_all(sock, data): + bytes_sent = 0 + while True: + r = sock.send(data[bytes_sent:]) + if r < 0: + return r + bytes_sent += r + if bytes_sent == len(data): + return bytes_sent class ThreadingTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): - pass + allow_reuse_address = True class Socks5Server(SocketServer.StreamRequestHandler): @@ -55,11 +71,20 @@ class Socks5Server(SocketServer.StreamRequestHandler): while True: r, w, e = select.select(fdset, [], []) if sock in r: - if remote.send(self.decrypt(sock.recv(4096))) <= 0: + data = sock.recv(4096) + if len(data) <= 0: break + result = send_all(remote, self.decrypt(data)) + if result < len(data): + raise Exception('failed to send all data') if remote in r: - if sock.send(self.encrypt(remote.recv(4096))) <= 0: + data = remote.recv(4096) + if len(data) <= 0: break + result = send_all(sock, self.encrypt(data)) + if result < len(data): + raise Exception('failed to send all data') + finally: sock.close() remote.close() @@ -87,6 +112,7 @@ class Socks5Server(SocketServer.StreamRequestHandler): try: logging.info('connecting %s:%d' % (addr, port[0])) remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + remote.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) remote.connect((addr, port[0])) except socket.error, e: # Connection refused @@ -99,6 +125,8 @@ class Socks5Server(SocketServer.StreamRequestHandler): if __name__ == '__main__': os.chdir(os.path.dirname(__file__) or '.') + print 'shadowsocks v0.9' + with open('config.json', 'rb') as f: config = json.load(f) @@ -122,7 +150,6 @@ if __name__ == '__main__': ThreadingTCPServer.address_family = socket.AF_INET6 try: server = ThreadingTCPServer(('', PORT), Socks5Server) - server.allow_reuse_address = True logging.info("starting server at port %d ..." % PORT) server.serve_forever() except socket.error, e: