This commit is contained in:
老A 2013-02-27 21:39:20 -08:00
commit 4f7a6d06c5

234
server.py
View file

@ -20,138 +20,144 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # 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
import SocketServer
import struct
import string
import hashlib import hashlib
import os
import json import json
import logging import logging
import getopt import optparse
import os
import socket
import string
import struct
def get_table(key): from tornado import ioloop
m = hashlib.md5() from tornado import iostream
m.update(key) from tornado import netutil
s = m.digest()
(a, b) = struct.unpack('<QQ', s)
table = [c for c in string.maketrans('', '')]
for i in xrange(1, 1024):
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):
allow_reuse_address = True
class Socks5Server(SocketServer.StreamRequestHandler): class Crypto(object):
def handle_tcp(self, sock, remote): def __init__(self, password):
try: m = hashlib.md5()
fdset = [sock, remote] m.update(password)
while True: s = m.digest()
r, w, e = select.select(fdset, [], []) a, b = struct.unpack('<QQ', s)
if sock in r: trans = string.maketrans('', '')
data = sock.recv(4096) table = list(trans)
if len(data) <= 0: for i in xrange(1, 1024):
break table.sort(lambda x, y: int(a % (ord(x) + i) - a % (ord(y) + i)))
result = send_all(remote, self.decrypt(data)) self.encrypt_table = ''.join(table)
if result < len(data): self.decrypt_table = string.maketrans(self.encrypt_table, trans)
raise Exception('failed to send all data')
if remote in r:
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()
def encrypt(self, data): def encrypt(self, data):
return data.translate(encrypt_table) return data.translate(self.encrypt_table)
def decrypt(self, data): def decrypt(self, data):
return data.translate(decrypt_table) return data.translate(self.decrypt_table)
def handle(self):
try: class Socks5Server(netutil.TCPServer):
sock = self.connection def handle_stream(self, stream, address):
addrtype = ord(self.decrypt(sock.recv(1))) soc = stream.socket
if addrtype == 1: soc.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True)
addr = socket.inet_ntoa(self.decrypt(self.rfile.read(4))) ConnHandler(soc).wait_for_data()
elif addrtype == 3:
addr = self.decrypt(
self.rfile.read(ord(self.decrypt(sock.recv(1))))) class PairedStream(iostream.IOStream):
def __init__(self, soc):
super(PairedStream, self).__init__(soc)
self.remote = None
def on_close(self):
remote = self.remote
if isinstance(remote, PairedStream) and not remote.closed():
if remote.writing():
remote.write("", callback=remote.close())
else: else:
# not support remote.close()
logging.warn('addr_type not support')
return
port = struct.unpack('>H', self.decrypt(self.rfile.read(2)))
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
logging.warn(e)
return
self.handle_tcp(sock, remote)
except socket.error, e:
logging.warn(e)
if __name__ == '__main__':
os.chdir(os.path.dirname(__file__) or '.')
print 'shadowsocks v0.9.3' class ConnHandler(PairedStream):
def wait_for_data(self):
self.read_bytes(1, self.on_addrtype)
with open('config.json', 'rb') as f: def on_addrtype(self, addrtype):
addrtype = ord(crypto.decrypt(addrtype))
if addrtype == 1:
self.read_bytes(4, self.on_ipaddr)
elif addrtype == 3:
self.read_bytes(1, self.on_hostname_length)
else:
logging.warn("addr_type %d not support" % addrtype)
self.close()
def on_ipaddr(self, addr):
self.remote_addr = socket.inet_ntoa(crypto.decrypt(addr))
self.read_bytes(2, self.on_port)
def on_hostname_length(self, length):
length = ord(crypto.decrypt(length))
self.read_bytes(length, self.on_hostname)
def on_hostname(self, addr):
self.remote_addr = crypto.decrypt(addr)
self.read_bytes(2, self.on_port)
def on_port(self, port):
self.remote_port = struct.unpack('>H', crypto.decrypt(port))[0]
logging.debug("Connecting to %s:%d" % (self.remote_addr, self.remote_port))
remote_soc = socket.socket()
remote_soc.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True)
self.remote = PairedStream(remote_soc)
self.set_close_callback(self.remote.on_close)
self.remote.set_close_callback(self.on_close)
self.remote.connect((self.remote_addr, self.remote_port), self.on_remote_connected)
def on_remote_connected(self):
self.read_until_close(callback=self.on_client_read, streaming_callback=self.on_client_read)
self.remote.read_until_close(callback=self.on_remote_read, streaming_callback=self.on_remote_read)
self._try_inline_read() # We must call this to empty filled buffer otherwise nothing will be read in again.
def on_client_read(self, data):
if data and not self.remote.closed():
self.remote.write(crypto.decrypt(data))
def on_remote_read(self, data):
if data and not self.closed():
self.write(crypto.encrypt(data))
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)-8s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S', filemode='a+')
parser = optparse.OptionParser("usage: %prog [options] arg")
parser.add_option("-c", "--config", dest="config_path",
default=os.path.join(os.path.dirname(__file__), "config.json"))
parser.add_option("-p", "--port", dest="server_port")
parser.add_option("-k", "--key", dest="server_password")
parser.add_option("-6", "--ipv6", action="store_true", dest="ipv6")
options, args = parser.parse_args()
with open(options.config_path, "rb") as f:
config = json.load(f) config = json.load(f)
SERVER = config['server'] server_port = int(options.server_port) if options.server_port else config["server_port"]
PORT = config['server_port'] server_password = options.server_password if options.server_password else config['password']
KEY = config['password']
optlist, args = getopt.getopt(sys.argv[1:], 'p:k:') if getattr(options, "ipv6"):
for key, value in optlist: address_family = socket.AF_INET6
if key == '-p': else:
PORT = int(value) address_family = socket.AF_INET
elif key == '-k':
KEY = value
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)-8s %(message)s', crypto = Crypto(server_password)
datefmt='%Y-%m-%d %H:%M:%S', filemode='a+')
encrypt_table = ''.join(get_table(KEY)) logging.info("starting server at port %d ..." % server_port)
decrypt_table = string.maketrans(encrypt_table, string.maketrans('', '')) server = Socks5Server()
if '-6' in sys.argv[1:]: server.bind(port=server_port, family=address_family)
ThreadingTCPServer.address_family = socket.AF_INET6 server.start()
try: try:
server = ThreadingTCPServer(('', PORT), Socks5Server) ioloop.IOLoop.instance().start()
logging.info("starting server at port %d ..." % PORT) except KeyboardInterrupt:
server.serve_forever() pass
except socket.error, e: except Exception:
logging.error(e) logging.exception("Uncaught Exception")