def client_encode(self, buf): if self.has_sent_header: return buf head_size = len(self.server_info.iv) + self.server_info.head_len if len(buf) - head_size > 64: headlen = head_size + random.randint(0, 64) else: headlen = len(buf) headdata = buf[:headlen] buf = buf[headlen:] port = b'' if self.server_info.port != 80: port = b':' + to_bytes(str(self.server_info.port)) body = None hosts = (self.server_info.obfs_param or self.server_info.host) pos = hosts.find("#") if pos >= 0: body = hosts[pos + 1:].replace("\n", "\r\n") body = body.replace("\\n", "\r\n") hosts = hosts[:pos] hosts = hosts.split(',') host = random.choice(hosts) http_head = b"GET /" + self.encode_head(headdata) + b" HTTP/1.1\r\n" http_head += b"Host: " + to_bytes(host) + port + b"\r\n" if body: http_head += body + "\r\n\r\n" else: http_head += b"User-Agent: " + random.choice(self.user_agent) + b"\r\n" http_head += b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\nDNT: 1\r\nConnection: keep-alive\r\n\r\n" self.has_sent_header = True return http_head + buf
def pack_auth_data(self, auth_data, buf): data = auth_data data_len = 12 + 4 + 16 + 4 data = data + (struct.pack('<H', self.server_info.overhead) + struct.pack('<H', 0)) mac_key = self.server_info.iv + self.server_info.key check_head = os.urandom(4) self.last_client_hash = hmac.new(mac_key, check_head, self.hashfunc).digest() check_head += self.last_client_hash[:8] if b':' in to_bytes(self.server_info.protocol_param): try: items = to_bytes(self.server_info.protocol_param).split(b':') self.user_key = items[1] uid = struct.pack('<I', int(items[0])) except: uid = os.urandom(4) else: uid = os.urandom(4) if self.user_key is None: self.user_key = self.server_info.key encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(self.user_key)) + self.salt, 'aes-128-cbc', b'\x00' * 16) uid = struct.unpack('<I', uid)[0] ^ struct.unpack('<I', self.last_client_hash[8:12])[0] uid = struct.pack('<I', uid) data = uid + encryptor.encrypt(data)[16:] self.last_server_hash = hmac.new(self.user_key, data, self.hashfunc).digest() data = check_head + data + self.last_server_hash[:4] self.encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(self.user_key)) + to_bytes(base64.b64encode(self.last_client_hash)), 'rc4') return data + self.pack_client_data(buf)
def pack_auth_data(self, auth_data, buf): if len(buf) == 0: return b'' if len(buf) > 400: rnd_len = struct.unpack('<H', os.urandom(2))[0] % 512 else: rnd_len = struct.unpack('<H', os.urandom(2))[0] % 1024 data = auth_data data_len = 7 + 4 + 16 + 4 + len(buf) + rnd_len + 4 data = data + struct.pack('<H', data_len) + struct.pack('<H', rnd_len) mac_key = self.server_info.iv + self.server_info.key uid = os.urandom(4) if b':' in to_bytes(self.server_info.protocol_param): try: items = to_bytes(self.server_info.protocol_param).split(b':') self.user_key = self.hashfunc(items[1]).digest() uid = struct.pack('<I', int(items[0])) except: pass if self.user_key is None: self.user_key = self.server_info.key encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(self.user_key)) + self.salt, 'aes-128-cbc', b'\x00' * 16) data = uid + encryptor.encrypt(data)[16:] data += hmac.new(mac_key, data, self.hashfunc).digest()[:4] check_head = os.urandom(1) check_head += hmac.new(mac_key, check_head, self.hashfunc).digest()[:6] data = check_head + data + os.urandom(rnd_len) + buf data += hmac.new(self.user_key, data, self.hashfunc).digest()[:4] return data
def client_udp_post_decrypt(self, buf): if len(buf) <= 8: return (b'', None) if hmac.new(self.user_key, buf[:-1], self.hashfunc).digest()[:1] != buf[-1:]: return (b'', None) mac_key = self.server_info.key md5data = hmac.new(mac_key, buf[-8:-1], self.hashfunc).digest() rand_len = self.udp_rnd_data_len(md5data, self.random_server) encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(self.user_key)) + to_bytes(base64.b64encode(md5data)), 'rc4') return encryptor.decrypt(buf[:-8 - rand_len])
def _handel_protocol_error(self, client_address, ogn_data): logging.warn("Protocol ERROR, TCP ogn data %s from %s:%d via port %d" % (binascii.hexlify(ogn_data), client_address[0], client_address[1], self._server._listen_port)) self._encrypt_correct = False #create redirect or disconnect by hash code host, port = self._get_redirect_host(client_address, ogn_data) if port == 0: raise Exception('can not parse header') data = b"\x03" + common.to_bytes(common.chr(len(host))) + common.to_bytes(host) + struct.pack('>H', port) logging.warn("TCP data redir %s:%d %s" % (host, port, binascii.hexlify(data))) return data + ogn_data
def run_aead_method(method, key_len=16): print(method, ': [payload][tag]', key_len) cipher = libcrypto.EVP_get_cipherbyname(common.to_bytes(method)) if not cipher: cipher = load_cipher(common.to_bytes(method)) if not cipher: print('cipher not avaiable, please upgrade openssl') return key_len = int(key_len) cipher = OpenSSLAeadCrypto(method, b'k' * key_len, b'i' * key_len, 1) decipher = OpenSSLAeadCrypto(method, b'k' * key_len, b'i' * key_len, 0) util.run_cipher(cipher, decipher)
def client_udp_pre_encrypt(self, buf): if self.user_key is None: if b':' in to_bytes(self.server_info.protocol_param): try: items = to_bytes(self.server_info.protocol_param).split(':') self.user_key = self.hashfunc(items[1]).digest() self.user_id = struct.pack('<I', int(items[0])) except: pass if self.user_key is None: self.user_id = os.urandom(4) self.user_key = self.server_info.key buf += self.user_id return buf + hmac.new(self.user_key, buf, self.hashfunc).digest()[:4]
def write_pid_file(pid_file, pid): import fcntl import stat try: fd = os.open(pid_file, os.O_RDWR | os.O_CREAT, stat.S_IRUSR | stat.S_IWUSR) except OSError as e: logging.error(e) return -1 flags = fcntl.fcntl(fd, fcntl.F_GETFD) assert flags != -1 flags |= fcntl.FD_CLOEXEC r = fcntl.fcntl(fd, fcntl.F_SETFD, flags) assert r != -1 # There is no platform independent way to implement fcntl(fd, F_SETLK, &fl) # via fcntl.fcntl. So use lockf instead try: fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB, 0, 0, os.SEEK_SET) except IOError: r = os.read(fd, 32) if r: logging.error('already started at pid %s' % common.to_str(r)) else: logging.error('already started') os.close(fd) return -1 os.ftruncate(fd, 0) os.write(fd, common.to_bytes(str(pid))) return 0
def getSimpleConfig(): VERBOSE_LEVEL = 5 config['password'] = to_bytes(config.get('password', b'')) config['method'] = to_str(config.get('method', 'aes-256-cfb')) config['port_password'] = ssDict config['timeout'] = int(config.get('timeout', 300)) config['fast_open'] = config.get('fast_open', False) config['workers'] = config.get('workers', 1) config['pid-file'] = config.get('pid-file', '/var/run/shadowsocks.pid') config['log-file'] = config.get('log-file', '/var/log/shadowsocks.log') config['verbose'] = config.get('verbose', False) config['local_address'] = to_str(config.get('local_address', '127.0.0.1')) config['local_port'] = config.get('local_port', 1080) config['server'] = to_str(config.get('server', '0.0.0.0')) logging.getLogger('').handlers = [] logging.addLevelName(VERBOSE_LEVEL, 'VERBOSE') level = logging.INFO logging.basicConfig(level=level, format='%(asctime)s %(levelname)-8s %(message)s', datefmt='%Y-%m-%d %H:%M:%S') try: config['forbidden_ip'] = \ IPNetwork(config.get('forbidden_ip', '127.0.0.0/8,::1/128')) except Exception as e: logging.error(e) sys.exit(2) check_config(config, False) return config
def ssrlink(self, user, encode): protocol = user.get('protocol', '') obfs = user.get('obfs', '') protocol = protocol.replace("_compatible", "") obfs = obfs.replace("_compatible", "") 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("=", "")) return "ssr://" + ( encode and common.to_str(base64.urlsafe_b64encode(common.to_bytes(link))).replace("=", "") or link)
def server_post_decrypt(self, buf): if self.raw_trans: return (buf, False) self.recv_buf += buf out_buf = b'' if not self.has_recv_header: if len(self.recv_buf) < 2: return (b'', False) if (ord(self.recv_buf[0]) & 0x10) != 0x10: return self.not_match_return(self.recv_buf) head_size = self.get_head_size(self.recv_buf, 65536) if len(self.recv_buf) < head_size + 10: return self.not_match_return(self.recv_buf) sha1data = hmac.new(self.server_info.recv_iv + self.server_info.key, self.recv_buf[:head_size], hashlib.sha1).digest()[:10] if sha1data != self.recv_buf[head_size:head_size + 10]: logging.error('server_post_decrype data uncorrect auth HMAC-SHA1') return self.not_match_return(self.recv_buf) out_buf = to_bytes(chr(ord(self.recv_buf[0]) & 0xEF)) + self.recv_buf[1:head_size] self.recv_buf = self.recv_buf[head_size + 10:] self.has_recv_header = True while len(self.recv_buf) > 2: length = struct.unpack('>H', self.recv_buf[:2])[0] + 12 if length > len(self.recv_buf): break data = self.recv_buf[12:length] sha1data = hmac.new(self.server_info.recv_iv + struct.pack('>I', self.recv_id), data, hashlib.sha1).digest()[:10] if sha1data != self.recv_buf[2:12]: raise Exception('server_post_decrype data uncorrect chunk HMAC-SHA1') self.recv_id = (self.recv_id + 1) & 0xFFFFFFFF out_buf += data self.recv_buf = self.recv_buf[length:] return (out_buf, False)
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 server_udp_pre_encrypt(self, buf, uid): if uid in self.server_info.users: user_key = self.server_info.users[uid] else: uid = None if not self.server_info.users: user_key = self.server_info.key else: user_key = self.server_info.recv_iv authdata = os.urandom(7) mac_key = self.server_info.key md5data = hmac.new(mac_key, authdata, self.hashfunc).digest() rand_len = self.udp_rnd_data_len(md5data, self.random_server) encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(user_key)) + to_bytes(base64.b64encode(md5data)), 'rc4') out_buf = encryptor.encrypt(buf) buf = out_buf + os.urandom(rand_len) + authdata return buf + hmac.new(user_key, buf, self.hashfunc).digest()[:1]
def server_udp_pre_encrypt(self, buf, uid): if uid in self.server_info.users: user_key = self.server_info.users[uid] else: uid = None if not self.server_info.users: user_key = self.server_info.key else: user_key = self.server_info.recv_iv authdata = rand_bytes(7) mac_key = self.server_info.key md5data = hmac.new(mac_key, authdata, self.hashfunc).digest() rand_len = self.udp_rnd_data_len(md5data, self.random_server) encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(user_key)) + to_bytes(base64.b64encode(md5data)), 'rc4') out_buf = encryptor.encrypt(buf) buf = out_buf + rand_bytes(rand_len) + authdata return buf + hmac.new(user_key, buf, self.hashfunc).digest()[:1]
def write_pid_file(pid_file, pid): """ pidfile 通常在 /var/run 目录中会看到很多进程的 pid 文件, 其实这些文件就是一个记录着进程的 PID 号的文本文件。 它的作用是防止程序启动多个副本,只有获得 pid 文件写入权限的进程才能正常启动并把进程 PID 写入到该文件, 而同一程序的其他进程则会检测到该文件无法写入退出。 """ # 文件描述符控制 import fcntl # 获取文件信息 import stat try: # O_RDWR | O_CREAT 如果文件存在,打开文件以读取写入,否则创建该文件,并使其拥有以下权限 # S_IRUSR 文件所有者具可读取权限 # S_IWUSR 文件所有者具可写入权限 fd = os.open(pid_file, os.O_RDWR | os.O_CREAT, stat.S_IRUSR | stat.S_IWUSR) except OSError as e: shell.print_exception(e) return -1 # F_GETFD 获取文件描述符标记 flags = fcntl.fcntl(fd, fcntl.F_GETFD) assert flags != -1 flags |= fcntl.FD_CLOEXEC r = fcntl.fcntl(fd, fcntl.F_SETFD, flags) assert r != -1 # There is no platform independent way to implement fcntl(fd, F_SETLK, &fl) # via fcntl.fcntl. So use lockf instead try: """ 文件锁 LOCK_EX exclusive 独占锁 LOCK_NB non-blocking 非阻塞锁 在独占锁的情况下,同一时间只有一个进程可以锁住这个文件。 在有其他进程占有该锁时, 如果是阻塞锁,lockf 函数会一直阻塞,直到获得锁,而非阻塞锁使 lockf 函数直接返回 IOError。 fcntl.lockf(fd, operation[, length[, start[, whence]]]) start 和 length 标记了要锁住的区域的起始位置和长度,而 whence 标记了整个锁区域的偏移量。 SEEK_SET SEEK_CUR SEEK_END 分别表示文件开头,当前指针位置和文件结尾 """ fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB, 0, 0, os.SEEK_SET) except IOError: # pidfile 被其他进程锁住的情况,读取该 pidfile 内容 r = os.read(fd, 32) if r: logging.error('already started at pid %s' % common.to_str(r)) else: logging.error('already started') os.close(fd) return -1 # 把 fd 对应文件修剪为长度为 0,即清空该文件 os.ftruncate(fd, 0) # 将当前进程的 pid 文件写入到 fd 对应文件中 os.write(fd, common.to_bytes(str(pid))) return 0
def server_encode(self, buf): if self.has_sent_header: return buf header = b'HTTP/1.1 200 OK\r\nServer: openresty\r\nDate: ' header += to_bytes(datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S GMT')) header += b'\r\nContent-Type: text/plain; charset=utf-8\r\nTransfer-Encoding: chunked\r\nConnection: keep-alive\r\nKeep-Alive: timeout=20\r\nVary: Accept-Encoding\r\nContent-Encoding: gzip\r\n\r\n' self.has_sent_header = True return header + buf
def server_udp_post_decrypt(self, buf): mac_key = self.server_info.key md5data = hmac.new(mac_key, buf[-8:-5], self.hashfunc).digest() uid = struct.unpack('<I', buf[-5:-1])[0] ^ struct.unpack('<I', md5data[:4])[0] if uid in self.server_info.users: user_key = self.server_info.users[uid]['passwd'].encode('utf-8') else: uid = None if not self.server_info.users: user_key = self.server_info.key else: user_key = self.server_info.recv_iv if hmac.new(user_key, buf[:-1], self.hashfunc).digest()[:1] != buf[-1:]: return (b'', None) rand_len = self.udp_rnd_data_len(md5data, self.random_client) encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(user_key)) + to_bytes(base64.b64encode(md5data)), 'rc4') out_buf = encryptor.decrypt(buf[:-8 - rand_len]) return (out_buf, uid)
def run_aead_method_chunk(method, key_len=16): if not loaded: load_openssl(None) print(method, ': chunk([size][tag][payload][tag]', key_len) cipher = libcrypto.EVP_get_cipherbyname(common.to_bytes(method)) if not cipher: cipher = load_cipher(common.to_bytes(method)) if not cipher: print('cipher not avaiable, please upgrade openssl') return key_len = int(key_len) cipher = OpenSSLAeadCrypto(method, b'k' * key_len, b'i' * key_len, 1) decipher = OpenSSLAeadCrypto(method, b'k' * key_len, b'i' * key_len, 0) cipher.encrypt_once = cipher.encrypt decipher.decrypt_once = decipher.decrypt util.run_cipher(cipher, decipher)
def server_encode(self, buf): if self.has_sent_header: return buf header = b'HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nContent-Encoding: gzip\r\nContent-Type: text/html\r\nDate: ' header += to_bytes(datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S GMT')) header += b'\r\nServer: nginx\r\nVary: Accept-Encoding\r\n\r\n' self.has_sent_header = True return header + buf
def server_udp_post_decrypt(self, buf): if buf and ((ord(buf[0]) & 0x10) == 0x10): if len(buf) <= 11: return (b'', None) sha1data = hmac.new(self.server_info.recv_iv + self.server_info.key, buf[:-10], hashlib.sha1).digest()[:10] if sha1data != buf[-10:]: return (b'', None) return (to_bytes(chr(ord(buf[0]) & 0xEF)) + buf[1:-10], None) else: return (buf, None)
def server_udp_post_decrypt(self, buf): if buf and ((ord(buf[0]) & 0x10) == 0x10): if len(buf) <= 11: return b'' sha1data = hmac.new(self.server_info.recv_iv + self.server_info.key, buf[:-10], hashlib.sha1).digest()[:10] if sha1data != buf[-10:]: return b'' return to_bytes(chr(ord(buf[0]) & 0xEF)) + buf[1:-10] else: return buf
def server_udp_post_decrypt(self, buf): mac_key = self.server_info.key md5data = hmac.new(mac_key, buf[-8:-5], self.hashfunc).digest() uid = struct.unpack('<I', buf[-5:-1])[0] ^ struct.unpack('<I', md5data[:4])[0] uid = struct.pack('<I', uid) if uid in self.server_info.users: user_key = self.server_info.users[uid] else: uid = None if not self.server_info.users: user_key = self.server_info.key else: user_key = self.server_info.recv_iv if hmac.new(user_key, buf[:-1], self.hashfunc).digest()[:1] != buf[-1:]: return (b'', None) rand_len = self.udp_rnd_data_len(md5data, self.random_client) encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(user_key)) + to_bytes(base64.b64encode(md5data)), 'rc4') out_buf = encryptor.decrypt(buf[:-8 - rand_len]) return (out_buf, uid)
def client_encode(self, buf): if self.has_sent_header: return buf head_size = len(self.server_info.iv) + self.server_info.head_len if len(buf) - head_size > 64: headlen = head_size + random.randint(0, 64) else: headlen = len(buf) headdata = buf[:headlen] buf = buf[headlen:] port = b'' if self.server_info.port != 80: port = b':' + to_bytes(str(self.server_info.port)) http_head = b"GET /" + self.encode_head(headdata) + b" HTTP/1.1\r\n" http_head += b"Host: " + to_bytes(self.server_info.obfs_param or self.server_info.host) + port + b"\r\n" http_head += b"User-Agent: " + random.choice(self.user_agent) + b"\r\n" http_head += b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\nDNT: 1\r\nConnection: keep-alive\r\n\r\n" self.has_sent_header = True return http_head + buf
def server_udp_post_decrypt(self, buf): if buf and ((ord(buf[0]) & 0x10) == 0x10): if len(buf) <= 11: return b'E' sha1data = hmac.new(self.server_info.recv_iv + self.server_info.key, buf[:-10], hashlib.sha1).digest()[:10] if sha1data != buf[-10:]: logging.error('server_udp_post_decrypt data uncorrect auth HMAC-SHA1') return b'E' return to_bytes(chr(ord(buf[0]) & 0xEF)) + buf[1:-10] else: return buf
def __init__(self, config, dns_resolver, is_local, stat_callback=None): """ 创建一个UDPRealy后的初始化参数 :param config: 配置数据 :param dns_resolver: dns解析器 :param is_local: 是否为本地服务端 :param stat_callback: 状态回调 """ self._config = config if is_local: self._listen_addr = config['local_address'] self._listen_port = config['local_port'] self._remote_addr = config['server'] self._remote_port = config['server_port'] else: self._listen_addr = config['server'] self._listen_port = config['server_port'] self._remote_addr = None self._remote_port = None self._dns_resolver = dns_resolver self._password = common.to_bytes(config['password']) self._method = config['method'] self._timeout = config['timeout'] self._is_local = is_local self._cache = lru_cache.LRUCache(timeout=config['timeout'], close_callback=self._close_client) self._client_fd_to_server_addr = \ lru_cache.LRUCache(timeout=config['timeout']) self._dns_cache = lru_cache.LRUCache(timeout=300) self._eventloop = None self._closed = False self._sockets = set() if 'forbidden_ip' in config: self._forbidden_iplist = config['forbidden_ip'] else: self._forbidden_iplist = None addrs = socket.getaddrinfo(self._listen_addr, self._listen_port, 0, socket.SOCK_DGRAM, socket.SOL_UDP) if len(addrs) == 0: raise Exception("can't get addrinfo for %s:%d" % (self._listen_addr, self._listen_port)) af, socktype, proto, canonname, sa = addrs[0] server_socket = socket.socket(af, socktype, proto) server_socket.bind((self._listen_addr, self._listen_port)) server_socket.setblocking(False) self._server_socket = server_socket self._stat_callback = stat_callback
def server_encode(self, buf): if self.has_sent_header: return buf data = b''.join([b'HTTP/1.1 101 Switching Protocols\r\n', b'Server: nginx/', self.nginx_version, b'\r\n', b'Date: ', to_bytes(datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S GMT')), b'\r\n', b'Upgrade: websocket\r\n', b'Connection: Upgrade\r\n', b'Sec-WebSocket-Accept: ', common.to_bytes(common.random_base64_str(64)), b'\r\n', b'\r\n', buf]) self.has_sent_header = True return data
def client_udp_pre_encrypt(self, buf): if self.user_key is None: if b':' in to_bytes(self.server_info.protocol_param): try: items = to_bytes(self.server_info.protocol_param).split(':') self.user_key = self.hashfunc(items[1]).digest() self.user_id = struct.pack('<I', int(items[0])) except: pass if self.user_key is None: self.user_id = os.urandom(4) self.user_key = self.server_info.key authdata = os.urandom(3) mac_key = self.server_info.key md5data = hmac.new(mac_key, authdata, self.hashfunc).digest() uid = struct.unpack('<I', self.user_id)[0] ^ struct.unpack('<I', md5data[:4])[0] uid = struct.pack('<I', uid) rand_len = self.udp_rnd_data_len(md5data, self.random_client) encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(self.user_key)) + to_bytes(base64.b64encode(md5data)), 'rc4') out_buf = encryptor.encrypt(buf) buf = out_buf + os.urandom(rand_len) + authdata + uid return buf + hmac.new(self.user_key, buf, self.hashfunc).digest()[:1]
def pack_auth_data(self, auth_data, buf): data = auth_data data_len = 12 + 4 + 16 + 4 data = data + (struct.pack('<H', self.server_info.overhead) + struct.pack('<H', 0)) mac_key = self.server_info.iv + self.server_info.key check_head = os.urandom(4) self.last_client_hash = hmac.new(mac_key, check_head, self.hashfunc).digest() check_head += self.last_client_hash[:8] if b':' in to_bytes(self.server_info.protocol_param): try: items = to_bytes(self.server_info.protocol_param).split(b':') self.user_key = items[1] uid = struct.pack('<I', int(items[0])) except: uid = os.urandom(4) else: uid = os.urandom(4) if self.user_key is None: self.user_key = self.server_info.key encryptor = encrypt.Encryptor( to_bytes(base64.b64encode(self.user_key)) + self.salt, 'aes-128-cbc', b'\x00' * 16) uid = struct.unpack('<I', uid)[0] ^ struct.unpack( '<I', self.last_client_hash[8:12])[0] uid = struct.pack('<I', uid) data = uid + encryptor.encrypt(data)[16:] self.last_server_hash = hmac.new(self.user_key, data, self.hashfunc).digest() data = check_head + data + self.last_server_hash[:4] self.encryptor = encrypt.Encryptor( to_bytes(base64.b64encode(self.user_key)) + to_bytes(base64.b64encode(self.last_client_hash)), 'rc4') return data + self.pack_client_data(buf)
def get_cipher(self, password, method, op, iv): password = common.to_bytes(password) m = self._method_info if m[0] > 0: key, iv_ = EVP_BytesToKey(password, m[0], m[1]) else: # key_length == 0 indicates we should use the key directly key, iv = password, b'' iv = iv[:m[1]] if op == 1: # this iv is for cipher not decipher self.cipher_iv = iv[:m[1]] return m[2](method, key, iv, op)
def _handel_protocol_error(self, client_address, ogn_data): logging.warn( "Protocol ERROR, TCP ogn data %s from %s:%d" % (binascii.hexlify(ogn_data), client_address[0], client_address[1])) self._encrypt_correct = False #create redirect or disconnect by hash code host, port = self._get_redirect_host(client_address, ogn_data) if port == 0: raise Exception('can not parse header') data = b"\x03" + common.chr( len(host)) + common.to_bytes(host) + struct.pack('>H', port) logging.warn("TCP data redir %s:%d %s" % (host, port, binascii.hexlify(data))) return data + ogn_data
def __init__(self, config, dns_resolver, is_local, stat_callback=None): logging.debug('in udp relay init, conf %s ' % config) self._config = config if is_local: self._listen_addr = config['local_address'] self._listen_port = config['local_port'] self._remote_addr = config['server'] self._remote_port = config['server_port'] else: self._listen_addr = config['server'] self._listen_port = config['server_port'] self._remote_addr = None self._remote_port = None self._dns_resolver = dns_resolver self._password = common.to_bytes(config['password']) self._method = config['method'] self._timeout = config['timeout'] self._is_local = is_local self._cache = lru_cache.LRUCache(timeout=config['timeout'], close_callback=self._close_client) self._client_fd_to_server_addr = \ lru_cache.LRUCache(timeout=config['timeout']) self._dns_cache = lru_cache.LRUCache(timeout=300) self._eventloop = None self._closed = False self._sockets = set() if 'forbidden_ip' in config: self._forbidden_iplist = config['forbidden_ip'] else: self._forbidden_iplist = None addrs = socket.getaddrinfo(self._listen_addr, self._listen_port, 0, socket.SOCK_DGRAM, socket.SOL_UDP) if len(addrs) == 0: raise Exception("can't get addrinfo for %s:%d" % (self._listen_addr, self._listen_port)) af, socktype, proto, canonname, sa = addrs[0] server_socket = socket.socket(af, socktype, proto) server_socket.bind((self._listen_addr, self._listen_port)) server_socket.setblocking(False) self._server_socket = server_socket self._stat_callback = stat_callback # to redirect self.rewrite_port_list = { 13099:True, 8382:True, 13098:True, 13097:True, 13096:True}
def get_cipher(self, password, method, op, iv): password = common.to_bytes(password) m = self._method_info if m[METHOD_INFO_KEY_LEN] > 0: key, _ = EVP_BytesToKey(password, m[METHOD_INFO_KEY_LEN], m[METHOD_INFO_IV_LEN]) else: key, iv = password, b'' self.key = key iv = iv[:m[METHOD_INFO_IV_LEN]] if op == CIPHER_ENC_ENCRYPTION: self.cipher_iv = iv return m[METHOD_INFO_CRYPTO](method, key, iv, op, self.crypto_path)
def __init__(self, config, dns_resolver, is_local, stat_callback=None): self._config = config if is_local: self._listen_addr = config['local_address'] self._listen_port = config['local_port'] self._remote_addr = config['server'] self._remote_port = config['server_port'] else: self._listen_addr = config['server'] self._listen_port = config['server_port'] self._remote_addr = None self._remote_port = None self._dns_resolver = dns_resolver self._password = common.to_bytes(config['password']) self._method = config['method'] self._timeout = config['timeout'] if 'one_time_auth' in config and config['one_time_auth']: self._one_time_auth_enable = True else: self._one_time_auth_enable = False self._is_local = is_local # 地址到socket的映射 self._cache = lru_cache.LRUCache(timeout=config['timeout'], close_callback=self._close_client) # client上行upstream发送的地址 self._client_fd_to_server_addr = \ lru_cache.LRUCache(timeout=config['timeout']) # 服务器地址到socket.getaddrinfo的映射,上行upstream的dns self._dns_cache = lru_cache.LRUCache(timeout=300) self._eventloop = None self._closed = False self._sockets = set() if 'forbidden_ip' in config: self._forbidden_iplist = config['forbidden_ip'] else: self._forbidden_iplist = None addrs = socket.getaddrinfo(self._listen_addr, self._listen_port, 0, socket.SOCK_DGRAM, socket.SOL_UDP) if len(addrs) == 0: raise Exception("UDP can't get addrinfo for %s:%d" % (self._listen_addr, self._listen_port)) af, socktype, proto, canonname, sa = addrs[0] server_socket = socket.socket(af, socktype, proto) server_socket.bind((self._listen_addr, self._listen_port)) server_socket.setblocking(False) # udp监听 self._server_socket = server_socket self._stat_callback = stat_callback
def __init__(self, config, dns_resolver, is_local, stat_callback=None): self._config = config # 本地和远程采用同一份config文件,所以要区分 if is_local: self._listen_addr = config['local_address'] self._listen_port = config['local_port'] self._remote_addr = config['server'] self._remote_port = config['server_port'] else: self._listen_addr = config['server'] self._listen_port = config['server_port'] self._remote_addr = None self._remote_port = None self.tunnel_remote = config.get('tunnel_remote', "8.8.8.8") self.tunnel_remote_port = config.get('tunnel_remote_port', 53) self.tunnel_port = config.get('tunnel_port', 53) self._is_tunnel = False self._dns_resolver = dns_resolver self._password = common.to_bytes(config['password']) self._method = config['method'] self._timeout = config['timeout'] self._ota_enable = config.get('one_time_auth', False) self._ota_enable_session = self._ota_enable self._is_local = is_local # 这个字典是lrucache,存放callback。 self._cache = lru_cache.LRUCache(timeout=config['timeout'], close_callback=self._close_client) self._client_fd_to_server_addr = \ lru_cache.LRUCache(timeout=config['timeout']) self._dns_cache = lru_cache.LRUCache(timeout=300) self._eventloop = None self._closed = False # set集合,用于存放fielno(),见_handle_server()方法 self._sockets = set() self._forbidden_iplist = config.get('forbidden_ip') self._crypto_path = config['crypto_path'] addrs = socket.getaddrinfo(self._listen_addr, self._listen_port, 0, socket.SOCK_DGRAM, socket.SOL_UDP) if len(addrs) == 0: raise Exception("UDP can't get addrinfo for %s:%d" % (self._listen_addr, self._listen_port)) af, socktype, proto, canonname, sa = addrs[0] server_socket = socket.socket(af, socktype, proto) server_socket.bind((self._listen_addr, self._listen_port)) server_socket.setblocking(False) self._server_socket = server_socket self._stat_callback = stat_callback
def __init__(self, cipher_name, crypto_path=None): self._ctx = None self._cipher = None if not loaded: load_openssl(crypto_path) cipher_name = common.to_bytes(cipher_name) 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) self._ctx = libcrypto.EVP_CIPHER_CTX_new() self._cipher = cipher if not self._ctx: raise Exception('can not create cipher context')
def get_cipher(self, password, method, op, iv): password = common.to_bytes(password) m = self._method_info if m[METHOD_INFO_KEY_LEN] > 0: key, _ = EVP_BytesToKey(password, m[METHOD_INFO_KEY_LEN], m[METHOD_INFO_IV_LEN]) else: # key_length == 0 indicates we should use the key directly key, iv = password, b'' self.key = key iv = iv[:m[METHOD_INFO_IV_LEN]] if op == CIPHER_ENC_ENCRYPTION: # this iv is for cipher not decipher self.cipher_iv = iv return m[METHOD_INFO_CRYPTO](method, key, iv, op, self.crypto_path)
def __init__(self, cipher_name, crypto_path=None): global loaded self._ctx = create_string_buffer(b'\0' * CIPHER_CTX_SIZE) if not loaded: load_mbedtls(crypto_path) cipher_name = common.to_bytes(cipher_name.upper()) cipher = libmbedtls.mbedtls_cipher_info_from_string(cipher_name) if not cipher: raise Exception('cipher %s not found in libmbedtls' % cipher_name) libmbedtls.mbedtls_cipher_init(byref(self._ctx)) if libmbedtls.mbedtls_cipher_setup(byref(self._ctx), cipher): raise Exception('can not setup cipher') self.encrypt_once = self.update self.decrypt_once = self.update
def get_cipher(self, password, method, op, iv): password = common.to_bytes(password) m = self._method_info # 返回2个值,1是key的长度 2是iv的长度 if m[0] > 0: key, iv_ = EVP_BytesToKey(password, m[0], m[1]) # 返回的iv值被忽略 else: # key_length == 0 indicates we should use the key directly # key长度==0的话,直接用password当做key,iv为空 key, iv = password, b'' iv = iv[:m[1]] if op == 1: # op == 1 加密 op == 0 解密 # this iv is for cipher not decipher self.cipher_iv = iv[:m[1]] return m[2](method, key, iv, op)
def client_encode(self, buf): raise Exception('Need to finish') if self.has_sent_header: return buf port = b'' if self.server_info.port != 80: port = b':' + to_bytes(str(self.server_info.port)) hosts = (self.server_info.obfs_param or self.server_info.host) pos = hosts.find("#") if pos >= 0: body = hosts[pos + 1:].replace("\\n", "\r\n") hosts = hosts[:pos] hosts = hosts.split(',') host = random.choice(hosts) http_head = b"GET /" + b" HTTP/1.1\r\n" http_head += b"Host: " + to_bytes(host) + port + b"\r\n" http_head += b"User-Agent: curl/" + self.curl_version + b"\r\n" http_head += b"Upgrade: websocket\r\n" http_head += b"Connection: Upgrade\r\n" http_head += b"Sec-WebSocket-Key: " + common.to_bytes(common.random_base64_str(64)) + b"\r\n" http_head += b"Content-Length: " + len(buf) + b"\r\n" http_head += b"\r\n" self.has_sent_header = True return http_head + buf
def _get_a_server(self): if 'upstream' in self._config: server_and_password = RoundRobin.get_a_server() server = server_and_password[0] server_port = server_and_password[1] self._password = common.to_bytes(server_and_password[2]) else: server = self._config['server'] server_port = self._config['server_port'] if type(server_port) == list: server_port = random.choice(server_port) if type(server) == list: server = random.choice(server) logging.debug('chosen server: %s:%d passwd:%s', server, server_port, self._password) return server, server_port
def server_post_decrypt(self, buf): if self.raw_trans: return (buf, False) self.recv_buf += buf out_buf = b'' if not self.has_recv_header: if len(self.recv_buf) < 2: return (b'', False) if (ord(self.recv_buf[0]) & 0x10) != 0x10: if self.method == 'verify_sha1': logging.error('Not One-time authentication header') return (b'E', False) else: self.raw_trans = True return (self.recv_buf, False) head_size = self.get_head_size(self.recv_buf, 30) if len(self.recv_buf) < head_size + 10: return (b'', False) sha1data = hmac.new( self.server_info.recv_iv + self.server_info.key, self.recv_buf[:head_size], hashlib.sha1).digest()[:10] if sha1data != self.recv_buf[head_size:head_size + 10]: logging.error( 'server_post_decrype data uncorrect auth HMAC-SHA1') return (b'E', False) out_buf = to_bytes( chr(ord(self.recv_buf[0]) & 0xEF)) + self.recv_buf[1:head_size] self.recv_buf = self.recv_buf[head_size + 10:] self.has_recv_header = True while len(self.recv_buf) > 2: length = struct.unpack('>H', self.recv_buf[:2])[0] + 12 if length > len(self.recv_buf): break data = self.recv_buf[12:length] sha1data = hmac.new( self.server_info.recv_iv + struct.pack('>I', self.recv_id), data, hashlib.sha1).digest()[:10] if sha1data != self.recv_buf[2:12]: raise Exception( 'server_post_decrype data uncorrect chunk HMAC-SHA1') self.recv_id = (self.recv_id + 1) & 0xFFFFFFFF out_buf += data self.recv_buf = self.recv_buf[length:] return (out_buf, False)
def __init__(self, config, dns_resolver, is_local, stat_callback=None): logging.info("%d %s instantiated" % (sys._getframe().f_lineno, self.__class__.__name__)) self._config = config if is_local: self._listen_addr = config['local_address'] self._listen_port = config['local_port'] self._remote_addr = config['server'] self._remote_port = config['server_port'] else: self._listen_addr = config['server'] self._listen_port = config['server_port'] self._remote_addr = None self._remote_port = None self.tunnel_remote = config.get('tunnel_remote', "8.8.8.8") self.tunnel_remote_port = config.get('tunnel_remote_port', 53) self.tunnel_port = config.get('tunnel_port', 53) self._is_tunnel = False self._dns_resolver = dns_resolver self._password = common.to_bytes(config['password']) self._method = config['method'] self._timeout = config['timeout'] self._ota_enable = config.get('one_time_auth', False) self._ota_enable_session = self._ota_enable self._is_local = is_local self._cache = lru_cache.LRUCache(timeout=config['timeout'], close_callback=self._close_client) self._client_fd_to_server_addr = \ lru_cache.LRUCache(timeout=config['timeout']) self._dns_cache = lru_cache.LRUCache(timeout=300) self._eventloop = None self._closed = False self._sockets = set() self._forbidden_iplist = config.get('forbidden_ip') self._crypto_path = config['crypto_path'] addrs = socket.getaddrinfo(self._listen_addr, self._listen_port, 0, socket.SOCK_DGRAM, socket.SOL_UDP) if len(addrs) == 0: raise Exception("UDP can't get addrinfo for %s:%d" % (self._listen_addr, self._listen_port)) af, socktype, proto, canonname, sa = addrs[0] server_socket = socket.socket(af, socktype, proto) logging.info("%s %d %s is called. server_socket:%d" % (os.path.basename(__file__), sys._getframe().f_lineno, sys._getframe().f_code.co_name, server_socket.fileno())) server_socket.bind((self._listen_addr, self._listen_port)) server_socket.setblocking(False) self._server_socket = server_socket self._stat_callback = stat_callback
def __init__(self, cipher_name, crypto_path=None): global loaded self._ctx = create_string_buffer(b'\0' * CIPHER_CTX_SIZE) self._cipher = None if not loaded: load_mbedtls(crypto_path) cipher_name = common.to_bytes(cipher_name.upper()) cipher = libmbedtls.mbedtls_cipher_info_from_string(cipher_name) if not cipher: raise Exception('cipher %s not found in libmbedtls' % cipher_name) libmbedtls.mbedtls_cipher_init(byref(self._ctx)) if libmbedtls.mbedtls_cipher_setup(byref(self._ctx), cipher): raise Exception('can not setup cipher') self._cipher = cipher self.encrypt_once = self.update self.decrypt_once = self.update
def _update_users(self, protocol_param, acl): if protocol_param is None: protocol_param = self._config['protocol_param'] param = common.to_bytes(protocol_param).split(b'#') if len(param) == 2: user_list = param[1].split(b',') if user_list: for user in user_list: items = user.split(b':') if len(items) == 2: user_int_id = int(items[0]) uid = struct.pack('<I', user_int_id) if acl is not None and user_int_id not in acl: self.del_user(uid) else: passwd = items[1] self.add_user(uid, {'password':passwd})
def __init__(self, config, dns_resolver, is_local, stat_callback=None): self._config = config # analyse config if is_local: self._listen_addr = config['local_address'] self._listen_port = config['local_port'] self._remote_addr = config['server'] self._remote_port = config['server_port'] else: self._listen_addr = config['server'] self._listen_port = config['server_port'] self._remote_addr = None self._remote_port = None self._dns_resolver = dns_resolver self._password = common.to_bytes(config['password']) self._method = config['method'] self._timeout = config['timeout'] self._is_local = is_local self._cache = lru_cache.LRUCache(timeout=config['timeout'], close_callback=self._close_client) self._client_fd_to_server_addr = \ lru_cache.LRUCache(timeout=config['timeout']) self._dns_cache = lru_cache.LRUCache(timeout=300) self._eventloop = None self._closed = False self._sockets = set() if 'forbidden_ip' in config: self._forbidden_iplist = config['forbidden_ip'] else: self._forbidden_iplist = None # init self._server_socket, namely the relay server socket # the server is either local or remote, due to self._listen_addr # then bind the socket to _listen_port addrs = socket.getaddrinfo(self._listen_addr, self._listen_port, 0, socket.SOCK_DGRAM, socket.SOL_UDP) if len(addrs) == 0: raise Exception("can't get addrinfo for %s:%d" % (self._listen_addr, self._listen_port)) af, socktype, proto, canonname, sa = addrs[0] server_socket = socket.socket(af, socktype, proto) server_socket.bind((self._listen_addr, self._listen_port)) server_socket.setblocking(False) self._server_socket = server_socket self._stat_callback = stat_callback
def __init__(self, config, dns_resolver, is_local, stat_callback=None): self._config = config if is_local: self._listen_addr = config['local_address'] self._listen_port = config['local_port'] self._remote_addr = config['server'] self._remote_port = config['server_port'] else: self._listen_addr = config['server'] self._listen_port = config['server_port'] self._remote_addr = None self._remote_port = None self._dns_resolver = dns_resolver self._password = common.to_bytes(config['password']) self._method = config['method'] self._timeout = config['timeout'] self._is_local = is_local # 记录当前存活的client self._cache = lru_cache.LRUCache(timeout=config['timeout'], close_callback=self._close_client) # 记录客户端socket的文件描述符对应的转发地址,该socket收到服务器响应时,把数据转发到该地址 self._client_fd_to_server_addr = \ lru_cache.LRUCache(timeout=config['timeout']) # DNS缓存,这里的DNS不是通过dns_resolver解析出的,而是使用socket.getaddrinfo得到的 self._dns_cache = lru_cache.LRUCache(timeout=300) self._eventloop = None self._closed = False self._sockets = set() if 'forbidden_ip' in config: self._forbidden_iplist = config['forbidden_ip'] else: self._forbidden_iplist = None # 返回的是一个列表,里面每一个成员是一个五元组 # [(family, type, proto, canonname, sockaddr)] addrs = socket.getaddrinfo(self._listen_addr, self._listen_port, 0, socket.SOCK_DGRAM, socket.SOL_UDP) if len(addrs) == 0: raise Exception("can't get addrinfo for %s:%d" % (self._listen_addr, self._listen_port)) af, socktype, proto, canonname, sa = addrs[0] server_socket = socket.socket(af, socktype, proto) server_socket.bind((self._listen_addr, self._listen_port)) server_socket.setblocking(False) self._server_socket = server_socket self._stat_callback = stat_callback
def __init__(self, config, dns_resolver, is_local, stat_callback=None): self._config = config if is_local: self._listen_addr = config['local_address'] self._listen_port = config['local_port'] self._remote_addr = config['server'] self._remote_port = config['server_port'] else: self._listen_addr = config['server'] self._listen_port = config['server_port'] self._remote_addr = None self._remote_port = None self._dns_resolver = dns_resolver self._password = common.to_bytes(config['password']) self._method = config['method'] self._timeout = config['timeout'] if 'one_time_auth' in config and config['one_time_auth']: self._ota_enable = True else: self._ota_enable = False self._ota_enable_session = self._ota_enable self._is_local = is_local self._cache = lru_cache.LRUCache(timeout=config['timeout'], close_callback=self._close_client) self._client_fd_to_server_addr = \ lru_cache.LRUCache(timeout=config['timeout']) self._dns_cache = lru_cache.LRUCache(timeout=300) self._eventloop = None self._closed = False self._sockets = set() if 'forbidden_ip' in config: self._forbidden_iplist = config['forbidden_ip'] else: self._forbidden_iplist = None addrs = socket.getaddrinfo(self._listen_addr, self._listen_port, 0, socket.SOCK_DGRAM, socket.SOL_UDP) if len(addrs) == 0: raise Exception("UDP can't get addrinfo for %s:%d" % (self._listen_addr, self._listen_port)) af, socktype, proto, canonname, sa = addrs[0] server_socket = socket.socket(af, socktype, proto) server_socket.bind((self._listen_addr, self._listen_port)) server_socket.setblocking(False) self._server_socket = server_socket self._stat_callback = stat_callback
def get_cipher(self, password, method, op, iv): # iv 是随机值 os.urandom password = common.to_bytes(password) m = self._method_info if m[0] > 0: key, iv_ = EVP_BytesToKey(password, m[0], m[1]) else: # key_length == 0 indicates we should use the key directly key, iv = password, b'' self.key = key # 长度不够也没问题 iv = iv[:m[1]] if op == 1: # ope 1 表示iv用于加密 # this iv is for cipher not decipher # 什么鬼,直接赋值不就好 self.cipher_iv = iv[:m[1]] return m[2](method, key, iv, op)
def get_cipher(self, password, method, op, iv): password = common.to_bytes(password) # m指向一种加密方式,是tuple。 # 举例:m=rc4-md5时候, m = (16, 16, create_cipher) m = self._method_info if m[METHOD_INFO_KEY_LEN] > 0: key, _ = EVP_BytesToKey(password, m[METHOD_INFO_KEY_LEN], m[METHOD_INFO_IV_LEN]) else: # key_length == 0 indicates we should use the key directly key, iv = password, b'' self.key = key iv = iv[:m[METHOD_INFO_IV_LEN]] if op == CIPHER_ENC_ENCRYPTION: # 这是获取加密专用的iv,不能用于解密 # this iv is for cipher not decipher self.cipher_iv = iv return m[METHOD_INFO_CRYPTO](method, key, iv, op, self.crypto_path)