def _ota_chunk_data(self, data, data_cb): # spec https://shadowsocks.org/en/spec/one-time-auth.html unchunk_data = b'' while len(data) > 0: if self._ota_len == 0: # get DATA.LEN + HMAC-SHA1 length = ONETIMEAUTH_CHUNK_BYTES - len(self._ota_buff_head) self._ota_buff_head += data[:length] data = data[length:] if len(self._ota_buff_head) < ONETIMEAUTH_CHUNK_BYTES: # wait more data return data_len = self._ota_buff_head[:ONETIMEAUTH_CHUNK_DATA_LEN] self._ota_len = struct.unpack('>H', data_len)[0] length = min(self._ota_len, len(data)) self._ota_buff_data += data[:length] data = data[length:] if len(self._ota_buff_data) == self._ota_len: # get a chunk data _hash = self._ota_buff_head[ONETIMEAUTH_CHUNK_DATA_LEN:] _data = self._ota_buff_data index = struct.pack('>I', self._ota_chunk_idx) key = self._encryptor.decipher_iv + index if onetimeauth_verify(_hash, _data, key) is False: logging.warn('one time auth fail, drop chunk !') else: unchunk_data += _data self._ota_chunk_idx += 1 self._ota_buff_head = b'' self._ota_buff_data = b'' self._ota_len = 0 data_cb(unchunk_data) return
def _one_time_auth_chunk_data(self, data, data_cb): # spec https://shadowsocks.org/en/spec/one-time-auth.html while len(data) > 0: if self._one_time_auth_len == 0: # get DATA.LEN + HMAC-SHA1 length = ONETIMEAUTH_CHUNK_BYTES - len(self._one_time_auth_buff_head) self._one_time_auth_buff_head += data[:length] data = data[length:] if len(self._one_time_auth_buff_head) < ONETIMEAUTH_CHUNK_BYTES: # wait more data return self._one_time_auth_len = struct.unpack('>H', self._one_time_auth_buff_head[:ONETIMEAUTH_CHUNK_DATA_LEN])[0] length = min(self._one_time_auth_len, len(data)) self._one_time_auth_buff_data += data[:length] data = data[length:] if len(self._one_time_auth_buff_data) == self._one_time_auth_len: # get a chunk data if onetimeauth_verify(self._one_time_auth_buff_head[ONETIMEAUTH_CHUNK_DATA_LEN:], self._one_time_auth_buff_data, self._encryptor.decipher_iv + struct.pack('>I', self._one_time_auth_chunk_idx)) \ is False: # logging.warn('one time auth fail, drop chunk !') else: data_cb(self._one_time_auth_buff_data) self._one_time_auth_buff_head = '' self._one_time_auth_buff_data = '' self._one_time_auth_chunk_idx += 1 self._one_time_auth_len = 0 return
def _ota_chunk_data(self, data, data_cb): # spec https://shadowsocks.org/en/spec/one-time-auth.html unchunk_data = b'' while len(data) > 0: if self._ota_len == 0: # get DATA.LEN + HMAC-SHA1 length = ONETIMEAUTH_CHUNK_BYTES - len(self._ota_buff_head) self._ota_buff_head += data[:length] data = data[length:] if len(self._ota_buff_head) < ONETIMEAUTH_CHUNK_BYTES: # wait more data return data_len = self._ota_buff_head[:ONETIMEAUTH_CHUNK_DATA_LEN] self._ota_len = struct.unpack('>H', data_len)[0] length = min(self._ota_len - len(self._ota_buff_data), len(data)) self._ota_buff_data += data[:length] data = data[length:] if len(self._ota_buff_data) == self._ota_len: # get a chunk data _hash = self._ota_buff_head[ONETIMEAUTH_CHUNK_DATA_LEN:] _data = self._ota_buff_data index = struct.pack('>I', self._ota_chunk_idx) key = self._cryptor.decipher_iv + index if onetimeauth_verify(_hash, _data, key) is False: logging.warn('one time auth fail, drop chunk !') else: unchunk_data += _data self._ota_chunk_idx += 1 self._ota_buff_head = b'' self._ota_buff_data = b'' self._ota_len = 0 data_cb(unchunk_data) return
def _ota_chunk_data(self, data, data_cb): # spec https://shadowsocks.org/en/spec/one-time-auth.html # 前面数据是 len + hac-sha1,这里就是处理粘包情况,每个数据头 # 都有一个len字段表示这段数据的长度,每个数据包都有一个hmac-sha1 # hmac-sha1是iv+包序列号计算得到 # +----------+-----------+----------+---- # | DATA.LEN | HMAC - SHA1 | DATA | ... # +----------+-----------+----------+---- # | 2 | 10 | Variable | ... # +----------+-----------+----------+---- while len(data) > 0: if self._ota_len == 0: # get DATA.LEN + HMAC-SHA1 # 总共12字节,2字节长度,10字节hmac-sha1,这里是取得 # len和hmac-sha1,赋值给ota_len length = ONETIMEAUTH_CHUNK_BYTES - len(self._ota_buff_head) # 去后面的数据,length字段超过len(data)也没关系,python列表的data[:len]超过len(data) # 只会得到len(data)大小的数据 self._ota_buff_head += data[:length] data = data[length:] # 还没完整取得整个包头 if len(self._ota_buff_head) < ONETIMEAUTH_CHUNK_BYTES: # wait more data return # 得到data.len data_len = self._ota_buff_head[:ONETIMEAUTH_CHUNK_DATA_LEN] self._ota_len = struct.unpack('>H', data_len)[0] # 开始取数据 length = min(self._ota_len, len(data)) self._ota_buff_data += data[:length] data = data[length:] # 得到一个完整数据包,开始校验 hmac-ha1 if len(self._ota_buff_data) == self._ota_len: # get a chunk data # 跨过2字节len _hash = self._ota_buff_head[ONETIMEAUTH_CHUNK_DATA_LEN:] _data = self._ota_buff_data # 计数器 iv+counter作为key index = struct.pack('>I', self._ota_chunk_idx) key = self._encryptor.decipher_iv + index if onetimeauth_verify(_hash, _data, key) is False: logging.warn('one time auth fail, drop chunk !') else: data_cb(self._ota_buff_data) self._ota_chunk_idx += 1 self._ota_buff_head = b'' self._ota_buff_data = b'' self._ota_len = 0 return
def _handle_stage_addr(self, data): try: if self._is_local: cmd = common.ord(data[1]) if cmd == CMD_UDP_ASSOCIATE: logging.debug('UDP associate') if self._local_sock.family == socket.AF_INET6: header = b'\x05\x00\x00\x04' else: header = b'\x05\x00\x00\x01' addr, port = self._local_sock.getsockname()[:2] addr_to_send = socket.inet_pton(self._local_sock.family, addr) port_to_send = struct.pack('>H', port) self._write_to_sock(header + addr_to_send + port_to_send, self._local_sock) self._stage = STAGE_UDP_ASSOC # just wait for the client to disconnect return elif cmd == CMD_CONNECT: # just trim VER CMD RSV data = data[3:] else: logging.error('unknown command %d', cmd) self.destroy() return header_result = parse_header(data) if header_result is None: raise Exception('can not parse header') addrtype, remote_addr, remote_port, header_length = header_result logging.info('connecting %s:%d from %s:%d' % (common.to_str(remote_addr), remote_port, self._client_address[0], self._client_address[1])) if self._is_local is False: # spec https://shadowsocks.org/en/spec/one-time-auth.html self._ota_enable_session = addrtype & ADDRTYPE_AUTH if self._ota_enable and not self._ota_enable_session: logging.warn('client one time auth is required') return if self._ota_enable_session: if len(data) < header_length + ONETIMEAUTH_BYTES: logging.warn('one time auth header is too short') return None offset = header_length + ONETIMEAUTH_BYTES _hash = data[header_length: offset] _data = data[:header_length] key = self._encryptor.decipher_iv + self._encryptor.key if onetimeauth_verify(_hash, _data, key) is False: logging.warn('one time auth fail') self.destroy() return header_length += ONETIMEAUTH_BYTES self._remote_address = (common.to_str(remote_addr), remote_port) # pause reading self._update_stream(STREAM_UP, WAIT_STATUS_WRITING) self._stage = STAGE_DNS if self._is_local: # forward address to remote self._write_to_sock((b'\x05\x00\x00\x01' b'\x00\x00\x00\x00\x10\x10'), self._local_sock) # spec https://shadowsocks.org/en/spec/one-time-auth.html # ATYP & 0x10 == 1, then OTA is enabled. if self._ota_enable_session: data = common.chr(addrtype | ADDRTYPE_AUTH) + data[1:] key = self._encryptor.cipher_iv + self._encryptor.key data += onetimeauth_gen(data, key) data_to_send = self._encryptor.encrypt(data) self._data_to_write_to_remote.append(data_to_send) # notice here may go into _handle_dns_resolved directly self._dns_resolver.resolve(self._chosen_server[0], self._handle_dns_resolved) else: if self._ota_enable_session: data = data[header_length:] self._ota_chunk_data(data, self._data_to_write_to_remote.append) elif len(data) > header_length: self._data_to_write_to_remote.append(data[header_length:]) # notice here may go into _handle_dns_resolved directly self._dns_resolver.resolve(remote_addr, self._handle_dns_resolved) except Exception as e: self._log_error(e) if self._config['verbose']: traceback.print_exc() self.destroy()
def _handle_server(self): server = self._server_socket data, r_addr = server.recvfrom(BUF_SIZE) key = None iv = None if not data: logging.debug('UDP handle_server: data is empty') if self._stat_callback: self._stat_callback(self._listen_port, len(data)) if self._is_local: if self._is_tunnel: # add ss header to data tunnel_remote = self.tunnel_remote tunnel_remote_port = self.tunnel_remote_port data = common.add_header(tunnel_remote, tunnel_remote_port, data) else: frag = common.ord(data[2]) if frag != 0: logging.warn('UDP drop a message since frag is not 0') return else: data = data[3:] else: # decrypt data try: data, key, iv = cryptor.decrypt_all(self._password, self._method, data, self._crypto_path) except Exception: logging.debug('UDP handle_server: decrypt data failed') return if not data: logging.debug('UDP handle_server: data is empty after decrypt') return header_result = parse_header(data) if header_result is None: return addrtype, dest_addr, dest_port, header_length = header_result logging.info("[%d] udp data to %s:%d from %s:%d" % (self._config['server_port'], dest_addr, dest_port, r_addr[0], r_addr[1])) if self._is_local: server_addr, server_port = self._get_a_server() else: server_addr, server_port = dest_addr, dest_port # spec https://shadowsocks.org/en/spec/one-time-auth.html self._ota_enable_session = addrtype & ADDRTYPE_AUTH if self._ota_enable and not self._ota_enable_session: logging.warn('client one time auth is required') return if self._ota_enable_session: if len(data) < header_length + ONETIMEAUTH_BYTES: logging.warn('UDP one time auth header is too short') return _hash = data[-ONETIMEAUTH_BYTES:] data = data[:-ONETIMEAUTH_BYTES] _key = iv + key if onetimeauth_verify(_hash, data, _key) is False: logging.warn('UDP one time auth fail') return addrs = self._dns_cache.get(server_addr, None) if addrs is None: addrs = socket.getaddrinfo(server_addr, server_port, 0, socket.SOCK_DGRAM, socket.SOL_UDP) if not addrs: # drop return else: self._dns_cache[server_addr] = addrs af, socktype, proto, canonname, sa = addrs[0] key = client_key(r_addr, af) client = self._cache.get(key, None) if not client: # TODO async getaddrinfo if self._forbidden_iplist: if common.to_str(sa[0]) in self._forbidden_iplist: logging.debug('IP %s is in forbidden list, drop' % common.to_str(sa[0])) # drop return client = socket.socket(af, socktype, proto) client.setblocking(False) self._cache[key] = client self._client_fd_to_server_addr[client.fileno()] = r_addr self._sockets.add(client.fileno()) self._eventloop.add(client, eventloop.POLL_IN, self) if self._is_local: key, iv, m = cryptor.gen_key_iv(self._password, self._method) # spec https://shadowsocks.org/en/spec/one-time-auth.html if self._ota_enable_session: data = self._ota_chunk_data_gen(key, iv, data) try: data = cryptor.encrypt_all_m(key, iv, m, self._method, data, self._crypto_path) except Exception: logging.debug("UDP handle_server: encrypt data failed") return if not data: return else: data = data[header_length:] if not data: return try: client.sendto(data, (server_addr, server_port)) except IOError as e: err = eventloop.errno_from_exception(e) if err in (errno.EINPROGRESS, errno.EAGAIN): pass else: shell.print_exception(e)
def _handle_stage_addr(self, data): if self._is_local: if self._is_tunnel: # add ss header to data tunnel_remote = self.tunnel_remote tunnel_remote_port = self.tunnel_remote_port data = common.add_header(tunnel_remote, tunnel_remote_port, data) else: cmd = common.ord(data[1]) if cmd == CMD_UDP_ASSOCIATE: logging.debug('UDP associate') if self._local_sock.family == socket.AF_INET6: header = b'\x05\x00\x00\x04' else: header = b'\x05\x00\x00\x01' addr, port = self._local_sock.getsockname()[:2] addr_to_send = socket.inet_pton(self._local_sock.family, addr) port_to_send = struct.pack('>H', port) self._write_to_sock(header + addr_to_send + port_to_send, self._local_sock) self._stage = STAGE_UDP_ASSOC # just wait for the client to disconnect return elif cmd == CMD_CONNECT: # just trim VER CMD RSV data = data[3:] else: logging.error('unknown command %d', cmd) self.destroy() return header_result = parse_header(data) if header_result is None: raise Exception('can not parse header') addrtype, remote_addr, remote_port, header_length = header_result logging.info('connecting %s:%d from %s:%d' % (common.to_str(remote_addr), remote_port, self._client_address[0], self._client_address[1])) if self._is_local is False: # spec https://shadowsocks.org/en/spec/one-time-auth.html self._ota_enable_session = addrtype & ADDRTYPE_AUTH if self._ota_enable and not self._ota_enable_session: logging.warn('client one time auth is required') return if self._ota_enable_session: if len(data) < header_length + ONETIMEAUTH_BYTES: logging.warn('one time auth header is too short') return None offset = header_length + ONETIMEAUTH_BYTES _hash = data[header_length: offset] _data = data[:header_length] key = self._cryptor.decipher_iv + self._cryptor.key if onetimeauth_verify(_hash, _data, key) is False: logging.warn('one time auth fail') self.destroy() return header_length += ONETIMEAUTH_BYTES self._remote_address = (common.to_str(remote_addr), remote_port) # pause reading self._update_stream(STREAM_UP, WAIT_STATUS_WRITING) self._stage = STAGE_DNS if self._is_local: # jump over socks5 response if not self._is_tunnel: # forward address to remote self._write_to_sock((b'\x05\x00\x00\x01' b'\x00\x00\x00\x00\x10\x10'), self._local_sock) # spec https://shadowsocks.org/en/spec/one-time-auth.html # ATYP & 0x10 == 0x10, then OTA is enabled. if self._ota_enable_session: data = common.chr(addrtype | ADDRTYPE_AUTH) + data[1:] key = self._cryptor.cipher_iv + self._cryptor.key _header = data[:header_length] sha110 = onetimeauth_gen(data, key) data = _header + sha110 + data[header_length:] data_to_send = self._cryptor.encrypt(data) self._data_to_write_to_remote.append(data_to_send) # notice here may go into _handle_dns_resolved directly self._dns_resolver.resolve(self._chosen_server[0], self._handle_dns_resolved) else: if self._ota_enable_session: data = data[header_length:] self._ota_chunk_data(data, self._data_to_write_to_remote.append) elif len(data) > header_length: self._data_to_write_to_remote.append(data[header_length:]) # notice here may go into _handle_dns_resolved directly self._dns_resolver.resolve(remote_addr, self._handle_dns_resolved)
def _handle_server(self): server = self._server_socket data, r_addr = server.recvfrom(BUF_SIZE) key = None iv = None if not data: logging.debug('UDP handle_server: data is empty') if self._stat_callback: self._stat_callback(self._listen_port, len(data)) if self._is_local: if self._is_tunnel: # add ss header to data tunnel_remote = self.tunnel_remote tunnel_remote_port = self.tunnel_remote_port data = common.add_header(tunnel_remote, tunnel_remote_port, data) else: frag = common.ord(data[2]) if frag != 0: logging.warn('UDP drop a message since frag is not 0') return else: data = data[3:] else: # decrypt data try: data, key, iv = cryptor.decrypt_all(self._password, self._method, data, self._crypto_path) except Exception: logging.debug('UDP handle_server: decrypt data failed') return if not data: logging.debug('UDP handle_server: data is empty after decrypt') return header_result = parse_header(data) if header_result is None: return addrtype, dest_addr, dest_port, header_length = header_result logging.info("udp data to %s:%d from %s:%d" % (dest_addr, dest_port, r_addr[0], r_addr[1])) if self._is_local: server_addr, server_port = self._get_a_server() else: server_addr, server_port = dest_addr, dest_port # spec https://shadowsocks.org/en/spec/one-time-auth.html self._ota_enable_session = addrtype & ADDRTYPE_AUTH if self._ota_enable and not self._ota_enable_session: logging.warn('client one time auth is required') return if self._ota_enable_session: if len(data) < header_length + ONETIMEAUTH_BYTES: logging.warn('UDP one time auth header is too short') return _hash = data[-ONETIMEAUTH_BYTES:] data = data[: -ONETIMEAUTH_BYTES] _key = iv + key if onetimeauth_verify(_hash, data, _key) is False: logging.warn('UDP one time auth fail') return addrs = self._dns_cache.get(server_addr, None) if addrs is None: addrs = socket.getaddrinfo(server_addr, server_port, 0, socket.SOCK_DGRAM, socket.SOL_UDP) if not addrs: # drop return else: self._dns_cache[server_addr] = addrs af, socktype, proto, canonname, sa = addrs[0] key = client_key(r_addr, af) client = self._cache.get(key, None) if not client: # TODO async getaddrinfo if self._forbidden_iplist: if common.to_str(sa[0]) in self._forbidden_iplist: logging.debug('IP %s is in forbidden list, drop' % common.to_str(sa[0])) # drop return client = socket.socket(af, socktype, proto) client.setblocking(False) self._cache[key] = client self._client_fd_to_server_addr[client.fileno()] = r_addr self._sockets.add(client.fileno()) self._eventloop.add(client, eventloop.POLL_IN, self) if self._is_local: key, iv, m = cryptor.gen_key_iv(self._password, self._method) # spec https://shadowsocks.org/en/spec/one-time-auth.html if self._ota_enable_session: data = self._ota_chunk_data_gen(key, iv, data) try: data = cryptor.encrypt_all_m(key, iv, m, self._method, data, self._crypto_path) except Exception: logging.debug("UDP handle_server: encrypt data failed") return if not data: return else: data = data[header_length:] if not data: return try: client.sendto(data, (server_addr, server_port)) except IOError as e: err = eventloop.errno_from_exception(e) if err in (errno.EINPROGRESS, errno.EAGAIN): pass else: shell.print_exception(e)
def _handle_stage_addr(self, data): try: if self._is_local: cmd = common.ord(data[1]) if cmd == CMD_UDP_ASSOCIATE: logging.debug('UDP associate') if self._local_sock.family == socket.AF_INET6: # 0x4 ipv6 header = b'\x05\x00\x00\x04' else: # 0x1 ipv4 header = b'\x05\x00\x00\x01' addr, port = self._local_sock.getsockname()[:2] addr_to_send = socket.inet_pton(self._local_sock.family, addr) port_to_send = struct.pack('>H', port) # 响应 CMD_UDP_ASSOCIATE 命令,告诉客户端udp地址 self._write_to_sock(header + addr_to_send + port_to_send, self._local_sock) self._stage = STAGE_UDP_ASSOC # just wait for the client to disconnect # 这里已经返回 return elif cmd == CMD_CONNECT: # just trim VER CMD RSV # 跨过socks5前面3字节,后面字节与socks5兼容,不过在addrtype字段加入了ota验证 data = data[3:] else: # bing命令? logging.error('unknown command %d', cmd) self.destroy() return # 服务器端初始时候解析包头,下面也是socks5请求时从第三个字节开始的地址数据 # 这里ssserver刚好兼容剩下的socks5的请求头 header_result = parse_header(data) if header_result is None: raise Exception('can not parse header') addrtype, remote_addr, remote_port, header_length = header_result # 经常看到的服务器接收到请求时候打印的日志 logging.info('connecting %s:%d from %s:%d' % (common.to_str(remote_addr), remote_port, self._client_address[0], self._client_address[1])) if self._is_local is False: # spec https://shadowsocks.org/en/spec/one-time-auth.html # 验证ota正确性 if self._ota_enable or addrtype & ADDRTYPE_AUTH: self._ota_enable = True if len(data) < header_length + ONETIMEAUTH_BYTES: logging.warn('one time auth header is too short') return None offset = header_length + ONETIMEAUTH_BYTES # header后的一段hash值, 10字节one-time-auth _hash = data[header_length: offset] _data = data[:header_length] # iv+key key = self._encryptor.decipher_iv + self._encryptor.key # 验证是否允许 if onetimeauth_verify(_hash, _data, key) is False: logging.warn('one time auth fail') self.destroy() return header_length += ONETIMEAUTH_BYTES self._remote_address = (common.to_str(remote_addr), remote_port) # pause reading 上送数据 self._update_stream(STREAM_UP, WAIT_STATUS_WRITING) self._stage = STAGE_DNS if self._is_local: # 客户端 # forward address to remote # 告诉浏览器,socks5验证成功 self._write_to_sock((b'\x05\x00\x00\x01' b'\x00\x00\x00\x00\x10\x10'), self._local_sock) # spec https://shadowsocks.org/en/spec/one-time-auth.html # ATYP & 0x10 == 1, then OTA is enabled. if self._ota_enable: # 自己实现的ota,在浏览器之类发来的socks5请求头部的addrtype中加入ADDRTYPE_AUTH # 然后发往ssserver,修改第一字节,实际是socks5头部的第3字节 data = common.chr(addrtype | ADDRTYPE_AUTH) + data[1:] key = self._encryptor.cipher_iv + self._encryptor.key # 附在数据尾部? 这里的data是socks5请求报文第三字节之后的数据 data += onetimeauth_gen(data, key) # 加密 data_to_send = self._encryptor.encrypt(data) self._data_to_write_to_remote.append(data_to_send) # notice here may go into _handle_dns_resolved directly # 选择一个ssserver发送数据 self._dns_resolver.resolve(self._chosen_server[0], self._handle_dns_resolved) else: # 服务端 if self._ota_enable: # 过滤前面的远程地址和ota hmac-sha1头 data = data[header_length:] self._ota_chunk_data(data, self._data_to_write_to_remote.append) elif len(data) > header_length: # 过滤掉头部还有数据要发送 self._data_to_write_to_remote.append(data[header_length:]) # notice here may go into _handle_dns_resolved directly # 解析dns之后remote_sock连接远端web站点 self._dns_resolver.resolve(remote_addr, self._handle_dns_resolved) except Exception as e: self._log_error(e) if self._config['verbose']: traceback.print_exc() self.destroy()
def _handle_stage_addr(self, data): try: addr, port = self._local_sock.getpeername()[:2] if self._is_local: cmd = common.ord(data[1]) if cmd == CMD_UDP_ASSOCIATE: logging.debug('U[%d] UDP associate' % self._config['server_port']) if self._local_sock.family == socket.AF_INET6: header = b'\x05\x00\x00\x04' else: header = b'\x05\x00\x00\x01' # TODO: inet_pton is added for windows in Py 3.4 addr_to_send = socket.inet_pton(self._local_sock.family, addr) port_to_send = struct.pack('>H', port) self._write_to_sock(header + addr_to_send + port_to_send, self._local_sock) self._stage = STAGE_UDP_ASSOC # just wait for the client to disconnect return elif cmd == CMD_CONNECT: # just trim VER CMD RSV data = data[3:] else: logging.error('U[%d] Unknown command %d', self._config['server_port'], cmd) self.destroy() return header_result = parse_header(data) if header_result is None: raise Exception('TCP Can not parse header') addrtype, remote_addr, remote_port, header_length = header_result if self._config['firewall_ports'] and self._config['server_port'] not in self._config['firewall_trusted']: # Firewall enabled if self._config['firewall_mode'] == 'blacklist' and remote_port in self._config['firewall_ports']: firewall_blocked = True elif self._config['firewall_mode'] == 'whitelist' and remote_port not in self._config['firewall_ports']: firewall_blocked = True else: firewall_blocked = False else: firewall_blocked = False if firewall_blocked: logging.warning('U[%d] TCP PORT BANNED: RP[%d] A[%s-->%s]' % ( self._config['server_port'], remote_port, addr, common.to_str(remote_addr) )) return else: logging.info('U[%d] TCP CONN: RP[%d] A[%s-->%s]' % ( self._config['server_port'], remote_port, addr, common.to_str(remote_addr) )) if self._is_local is False: # spec https://shadowsocks.org/en/spec/one-time-auth.html if self._ota_enable or (addrtype & ADDRTYPE_AUTH == ADDRTYPE_AUTH): if not self._ota_enable and self._config['verbose']: logging.info('U[%d] TCP one time auth automatically enabled' % self._config['server_port']) self._ota_enable = True if len(data) < header_length + ONETIMEAUTH_BYTES: logging.warn('U[%d] One time auth header is too short' % self._config['server_port']) return None offset = header_length + ONETIMEAUTH_BYTES _hash = data[header_length: offset] _data = data[:header_length] key = self._encryptor.decipher_iv + self._encryptor.key if onetimeauth_verify(_hash, _data, key) is False: logging.warn('U[%d] One time auth fail' % self._config['server_port']) self.destroy() header_length += ONETIMEAUTH_BYTES self._remote_address = (common.to_str(remote_addr), remote_port) # pause reading self._update_stream(STREAM_UP, WAIT_STATUS_WRITING) self._stage = STAGE_DNS if self._is_local: # forward address to remote self._write_to_sock((b'\x05\x00\x00\x01' b'\x00\x00\x00\x00\x10\x10'), self._local_sock) # spec https://shadowsocks.org/en/spec/one-time-auth.html # ATYP & 0x10 == 0x10, then OTA is enabled. if self._ota_enable: data = common.chr(addrtype | ADDRTYPE_AUTH) + data[1:] key = self._encryptor.cipher_iv + self._encryptor.key data += onetimeauth_gen(data, key) data_to_send = self._encryptor.encrypt(data) self._data_to_write_to_remote.append(data_to_send) # notice here may go into _handle_dns_resolved directly self._dns_resolver.resolve(self._chosen_server[0], self._handle_dns_resolved) else: if self._ota_enable: data = data[header_length:] self._ota_chunk_data(data, self._data_to_write_to_remote.append) elif len(data) > header_length: self._data_to_write_to_remote.append(data[header_length:]) # notice here may go into _handle_dns_resolved directly self._dns_resolver.resolve(remote_addr, self._handle_dns_resolved) except Exception as e: self._log_error(e) if self._config['verbose']: traceback.print_exc() self.destroy()
def _handle_server(self): server = self._server_socket data, r_addr = server.recvfrom(BUF_SIZE) key = None iv = None if not data: logging.debug('UDP handle_server: data is empty') if self._stat_callback: self._stat_callback(self._listen_port, len(data)) if self._is_local: frag = common.ord(data[2]) if frag != 0: logging.warn('UDP drop a message since frag is not 0') return else: data = data[3:] else: data, key, iv = encrypt.dencrypt_all(self._password, self._method, data) # decrypt data if not data: logging.debug( 'UDP handle_server: data is empty after decrypt' ) return header_result = parse_header(data) if header_result is None: return addrtype, dest_addr, dest_port, header_length = header_result if self._is_local: server_addr, server_port = self._get_a_server() else: server_addr, server_port = dest_addr, dest_port # spec https://shadowsocks.org/en/spec/one-time-auth.html if self._one_time_auth_enable or addrtype & ADDRTYPE_AUTH: if len(data) < header_length + ONETIMEAUTH_BYTES: logging.warn('UDP one time auth header is too short') return _hash = data[-ONETIMEAUTH_BYTES:] data = data[: -ONETIMEAUTH_BYTES] _key = iv + key if onetimeauth_verify(_hash, data, _key) is False: logging.warn('UDP one time auth fail') return addrs = self._dns_cache.get(server_addr, None) if addrs is None: addrs = socket.getaddrinfo(server_addr, server_port, 0, socket.SOCK_DGRAM, socket.SOL_UDP) if not addrs: # drop return else: self._dns_cache[server_addr] = addrs af, socktype, proto, canonname, sa = addrs[0] key = client_key(r_addr, af) client = self._cache.get(key, None) if not client: # TODO async getaddrinfo if self._forbidden_iplist: if common.to_str(sa[0]) in self._forbidden_iplist: logging.debug('IP %s is in forbidden list, drop' % common.to_str(sa[0])) # drop return client = socket.socket(af, socktype, proto) client.setblocking(False) self._cache[key] = client self._client_fd_to_server_addr[client.fileno()] = r_addr self._sockets.add(client.fileno()) self._eventloop.add(client, eventloop.POLL_IN, self) if self._is_local: key, iv, m = encrypt.gen_key_iv(self._password, self._method) # spec https://shadowsocks.org/en/spec/one-time-auth.html if self._one_time_auth_enable: data = self._ota_chunk_data_gen(key, iv, data) data = encrypt.encrypt_all_m(key, iv, m, self._method, data) if not data: return else: data = data[header_length:] if not data: return try: client.sendto(data, (server_addr, server_port)) except (socket.error, OSError, IOError) as e: error_no = eventloop.errno_from_exception(e) if sys.platform == "win32": if error_no in (errno.EAGAIN, errno.EINPROGRESS, errno.EWOULDBLOCK, errno.WSAEWOULDBLOCK): pass else: shell.print_exception(e) elif error_no in (errno.EAGAIN, errno.EINPROGRESS, errno.EWOULDBLOCK): pass else: shell.print_exception(e)
def _handle_server(self): server = self._server_socket data, r_addr = server.recvfrom(BUF_SIZE) key = None iv = None if not data: logging.debug('UDP handle_server: data is empty') if self._stat_callback: self._stat_callback(self._listen_port, len(data)) # 若本地端从监听1080端口收到本机应用进程(例如chrome)的数据,进行切除header if self._is_local: if self._is_tunnel: # add ss header to data tunnel_remote = self.tunnel_remote tunnel_remote_port = self.tunnel_remote_port data = common.add_header(tunnel_remote, tunnel_remote_port, data) else: frag = common.ord(data[2]) if frag != 0: logging.warn('UDP drop a message since frag is not 0') return else: data = data[3:] # 如果是服务端收到本地端发出的udp数据,先进行解密 else: # decrypt data try: data, key, iv = cryptor.decrypt_all(self._password, self._method, data, self._crypto_path) except Exception: logging.debug('UDP handle_server: decrypt data failed') return if not data: logging.debug('UDP handle_server: data is empty after decrypt') return # 处理header header_result = parse_header(data) if header_result is None: return addrtype, dest_addr, dest_port, header_length = header_result logging.info("udp data to %s:%d from %s:%d" % (dest_addr, dest_port, r_addr[0], r_addr[1])) if self._is_local: # 如果是local收到,则server_addr server_port都是远程的 server_addr, server_port = self._get_a_server() else: # 如果远程收到,则将server_addr这些改成dest_addr dest_port,方便操作 # dest就是最终目标,例如 www.youtube.com:443 server_addr, server_port = dest_addr, dest_port # spec https://shadowsocks.org/en/spec/one-time-auth.html self._ota_enable_session = addrtype & ADDRTYPE_AUTH if self._ota_enable and not self._ota_enable_session: logging.warn('client one time auth is required') return if self._ota_enable_session: if len(data) < header_length + ONETIMEAUTH_BYTES: logging.warn('UDP one time auth header is too short') return _hash = data[-ONETIMEAUTH_BYTES:] data = data[: -ONETIMEAUTH_BYTES] _key = iv + key if onetimeauth_verify(_hash, data, _key) is False: logging.warn('UDP one time auth fail') return addrs = self._dns_cache.get(server_addr, None) if addrs is None: addrs = socket.getaddrinfo(server_addr, server_port, 0, socket.SOCK_DGRAM, socket.SOL_UDP) if not addrs: # drop return else: self._dns_cache[server_addr] = addrs af, socktype, proto, canonname, sa = addrs[0] key = client_key(r_addr, af) client = self._cache.get(key, None) if not client: # TODO async getaddrinfo if self._forbidden_iplist: if common.to_str(sa[0]) in self._forbidden_iplist: logging.debug('IP %s is in forbidden list, drop' % common.to_str(sa[0])) # drop return client = socket.socket(af, socktype, proto) client.setblocking(False) self._cache[key] = client self._client_fd_to_server_addr[client.fileno()] = r_addr self._sockets.add(client.fileno()) # 添加进Eventloop,标志设置为可读 self._eventloop.add(client, eventloop.POLL_IN, self) # 如果是local,要向远程发,要过墙,所以要加密 if self._is_local: key, iv, m = cryptor.gen_key_iv(self._password, self._method) # spec https://shadowsocks.org/en/spec/one-time-auth.html if self._ota_enable_session: data = self._ota_chunk_data_gen(key, iv, data) try: data = cryptor.encrypt_all_m(key, iv, m, self._method, data, self._crypto_path) except Exception: logging.debug("UDP handle_server: encrypt data failed") return if not data: return # 如果是远程,要向dest发请求,所以把除数据的部分除去,即除去header。 else: # data已经在上面进行数据解密了。不需要像local一样加密发送。 # data已经被切除头的3个字节了 data = data[header_length:] if not data: return try: # 发送,完美无瑕。。。。 # 这个sendto同时有udp的和tcp的两种,sendto函数主要用于UDP,但这里两种都用了 # 调用sendto时候会自动加上那个首3个字节,貌似是x00 x00 x00 client.sendto(data, (server_addr, server_port)) except IOError as e: err = eventloop.errno_from_exception(e) if err in (errno.EINPROGRESS, errno.EAGAIN): pass else: shell.print_exception(e)
def _handle_server(self): server = self._server_socket data, r_addr = server.recvfrom(BUF_SIZE) key = None iv = None if not data: logging.debug('UDP handle_server: data is empty') if self._stat_callback: self._stat_callback(self._listen_port, len(data)) if self._is_local: # +----+------+------+----------+----------+----------+ # |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | # +----+------+------+----------+----------+----------+ # ~~~~~~~~ frag = common.ord(data[2]) #shadowsocks中FRAG就使用0 if frag != 0: logging.warn('UDP drop a message since frag is not 0') return else: # +------+----------+----------+----------+ # | ATYP | DST.ADDR | DST.PORT | DATA | # +------+----------+----------+----------+ data = data[3:] else: data, key, iv = encrypt.dencrypt_all(self._password, self._method, data) # decrypt data if not data: logging.debug('UDP handle_server: data is empty after decrypt') return header_result = parse_header(data) if header_result is None: return # +------+----------+----------+----------+ # | ATYP | DST.ADDR | DST.PORT | DATA | # +------+----------+----------+----------+ # . . # |<----- header_length ------>| addrtype, dest_addr, dest_port, header_length = header_result # 如果是sslocal,则需要查找的是ssserver的地址及端口 # 如果是ssserver,则需要获取的是目标服务器的地址及端口 if self._is_local: # ssserver 地址和端口 server_addr, server_port = self._get_a_server() else: # 「目标服务器」地址和端口 server_addr, server_port = dest_addr, dest_port # spec https://shadowsocks.org/en/spec/one-time-auth.html self._ota_enable_session = addrtype & ADDRTYPE_AUTH if self._ota_enable and not self._ota_enable_session: logging.warn('client one time auth is required') return if self._ota_enable_session: if len(data) < header_length + ONETIMEAUTH_BYTES: logging.warn('UDP one time auth header is too short') return _hash = data[-ONETIMEAUTH_BYTES:] data = data[:-ONETIMEAUTH_BYTES] _key = iv + key if onetimeauth_verify(_hash, data, _key) is False: logging.warn('UDP one time auth fail') return # 从缓存中取 server_addr 解析后的地址 addrs = self._dns_cache.get(server_addr, None) # 如果找不到,则解析 server_addr 的地址并存入缓存 if addrs is None: # 注意,getaddrinfo 函数是阻塞的 addrs = socket.getaddrinfo(server_addr, server_port, 0, socket.SOCK_DGRAM, socket.SOL_UDP) if not addrs: # drop return else: self._dns_cache[server_addr] = addrs af, socktype, proto, canonname, sa = addrs[0] # 根据地址、端口、af 生成一个 key,这个 key 与 UDP 套接字一一对应 key = client_key(r_addr, af) client = self._cache.get(key, None) if not client: # TODO async getaddrinfo if self._forbidden_iplist: if common.to_str(sa[0]) in self._forbidden_iplist: logging.debug('IP %s is in forbidden list, drop' % common.to_str(sa[0])) # drop return # 创建 UDP 套接字 client = socket.socket(af, socktype, proto) client.setblocking(False) self._cache[key] = client # 将套接字与其地址关联起来,`_handle_client` 会用到 self._client_fd_to_server_addr[client.fileno()] = r_addr # 将套接字关联的文件描述符加入 `self._sockets` 中,`handle_event` 会用到 self._sockets.add(client.fileno()) # 将套接字加入事件循环, self._eventloop.add(client, eventloop.POLL_IN, self) # 如果是 sslocal,那么需要将数据加密 if self._is_local: key, iv, m = encrypt.gen_key_iv(self._password, self._method) # spec https://shadowsocks.org/en/spec/one-time-auth.html if self._ota_enable_session: data = self._ota_chunk_data_gen(key, iv, data) data = encrypt.encrypt_all_m(key, iv, m, self._method, data) if not data: return # 如果是 ssserver,在将接收到的数据发送给目标服务器之前, # 需要解密并且去掉头部,解密在上面已经完成了 else: # +------+----------+----------+----------+ # | ATYP | DST.ADDR | DST.PORT | DATA | # +------+----------+----------+----------+ # data = data[header_length:] if not data: return # - 对于 sslocal 而言,将加密后的数据发送给 ssserver,数据格式如下: # # +------+----------+----------+----------+ # | ATYP | DST.ADDR | DST.PORT | DATA | # +------+----------+----------+----------+ # # - 对于 ssserver 而言,将解密后的数据发送给目标服务器(只剩 `DATA` 部分了) try: client.sendto(data, (server_addr, server_port)) except IOError as e: err = eventloop.errno_from_exception(e) if err in (errno.EINPROGRESS, errno.EAGAIN): pass else: shell.print_exception(e)
def _handle_server(self): server = self._server_socket data, r_addr = server.recvfrom(BUF_SIZE) client_address = r_addr[0] key = None iv = None if not data: logging.debug('U[%d] UDP handle_server: data is empty' % self._config['server_port']) if self._stat_callback: self._stat_callback(self._listen_port, len(data)) if self._is_local: frag = common.ord(data[2]) if frag != 0: logging.warn('U[%d] UDP drop a message since frag is not 0' % self._config['server_port']) return else: data = data[3:] else: # decrypt data try: data, key, iv = cryptor.decrypt_all(self._password, self._method, data) except Exception: logging.debug('U[%d] UDP handle_server: decrypt data failed' % self._config['server_port']) return if not data: logging.debug( 'U[%d] UDP handle_server: data is empty after decrypt' % self._config['server_port']) return header_result = parse_header(data) if header_result is None: return addrtype, dest_addr, dest_port, header_length = header_result if self._config['firewall_ports'] and self._config[ 'server_port'] not in self._config['firewall_trusted']: # Firewall enabled if self._config[ 'firewall_mode'] == 'blacklist' and dest_port in self._config[ 'firewall_ports']: firewall_blocked = True elif self._config[ 'firewall_mode'] == 'whitelist' and dest_port not in self._config[ 'firewall_ports']: firewall_blocked = True else: firewall_blocked = False else: firewall_blocked = False if firewall_blocked: logging.warning('U[%d] UDP PORT BANNED: RP[%d] A[%s-->%s]' % (self._config['server_port'], dest_port, client_address, common.to_str(dest_addr))) return else: logging.info('U[%d] UDP CONN: RP[%d] A[%s-->%s]' % (self._config['server_port'], dest_port, client_address, common.to_str(dest_addr))) if self._is_local: server_addr, server_port = self._get_a_server() else: server_addr, server_port = dest_addr, dest_port # spec https://shadowsocks.org/en/spec/one-time-auth.html if self._one_time_auth_enable or (addrtype & ADDRTYPE_AUTH == ADDRTYPE_AUTH): if not self._one_time_auth_enable and self._config['verbose']: logging.info( 'U[%d] UDP one time auth automatically enabled' % self._config['server_port']) self._one_time_auth_enable = True if len(data) < header_length + ONETIMEAUTH_BYTES: logging.warn( 'U[%d] UDP one time auth header is too short' % self._config['server_port']) return _hash = data[-ONETIMEAUTH_BYTES:] data = data[:-ONETIMEAUTH_BYTES] _key = iv + key if onetimeauth_verify(_hash, data, _key) is False: logging.warn('U[%d] UDP one time auth fail' % self._config['server_port']) return addrs = self._dns_cache.get(server_addr, None) if addrs is None: addrs = socket.getaddrinfo(server_addr, server_port, 0, socket.SOCK_DGRAM, socket.SOL_UDP) if not addrs: # drop return else: self._dns_cache[server_addr] = addrs af, socktype, proto, canonname, sa = addrs[0] key = client_key(r_addr, af) client = self._cache.get(key, None) if not client: # TODO async getaddrinfo if self._forbidden_iplist: if common.to_str(sa[0]) in self._forbidden_iplist: logging.debug( 'U[%d] IP %s is in forbidden list, drop' % (self._config['server_port'], common.to_str(sa[0]))) # drop return client = socket.socket(af, socktype, proto) client.setblocking(False) self._cache[key] = client self._client_fd_to_server_addr[client.fileno()] = r_addr self._sockets.add(client.fileno()) self._eventloop.add(client, eventloop.POLL_IN, self) if self._is_local: key, iv, m = cryptor.gen_key_iv(self._password, self._method) # spec https://shadowsocks.org/en/spec/one-time-auth.html if self._one_time_auth_enable: data = self._ota_chunk_data_gen(key, iv, data) try: data = cryptor.encrypt_all_m(key, iv, m, self._method, data) except Exception: logging.debug("UDP handle_server: encrypt data failed") return if not data: return else: data = data[header_length:] if not data: return try: client.sendto(data, (server_addr, server_port)) except (socket.error, OSError, IOError) as e: error_no = eventloop.errno_from_exception(e) if sys.platform == "win32": if error_no in (errno.EAGAIN, errno.EINPROGRESS, errno.EWOULDBLOCK, errno.WSAEWOULDBLOCK): pass else: shell.print_exception(e) elif error_no in (errno.EAGAIN, errno.EINPROGRESS, errno.EWOULDBLOCK): pass else: shell.print_exception(e)
def _handle_server(self): server = self._server_socket data, r_addr = server.recvfrom(BUF_SIZE) key = None iv = None if not data: logging.debug('UDP handle_server: data is empty') if self._stat_callback: self._stat_callback(self._listen_port, len(data)) if self._is_local: frag = common.ord(data[2]) if frag != 0: logging.warn('UDP drop a message since frag is not 0') return else: data = data[3:] else: data, key, iv = encrypt.dencrypt_all(self._password, self._method, data) # decrypt data if not data: logging.debug('UDP handle_server: data is empty after decrypt') return header_result = parse_header(data) if header_result is None: return addrtype, dest_addr, dest_port, header_length = header_result if self._is_local: server_addr, server_port = self._get_a_server() else: server_addr, server_port = dest_addr, dest_port # spec https://shadowsocks.org/en/spec/one-time-auth.html if self._one_time_auth_enable or addrtype & ADDRTYPE_AUTH: if len(data) < header_length + ONETIMEAUTH_BYTES: logging.warn('UDP one time auth header is too short') return _hash = data[-ONETIMEAUTH_BYTES:] data = data[:-ONETIMEAUTH_BYTES] _key = iv + key if onetimeauth_verify(_hash, data, _key) is False: logging.warn('UDP one time auth fail') return addrs = self._dns_cache.get(server_addr, None) if addrs is None: addrs = socket.getaddrinfo(server_addr, server_port, 0, socket.SOCK_DGRAM, socket.SOL_UDP) if not addrs: # drop return else: self._dns_cache[server_addr] = addrs af, socktype, proto, canonname, sa = addrs[0] key = client_key(r_addr, af) client = self._cache.get(key, None) if not client: # TODO async getaddrinfo if self._forbidden_iplist: if common.to_str(sa[0]) in self._forbidden_iplist: logging.debug('IP %s is in forbidden list, drop' % common.to_str(sa[0])) # drop return client = socket.socket(af, socktype, proto) client.setblocking(False) self._cache[key] = client self._client_fd_to_server_addr[client.fileno()] = r_addr self._sockets.add(client.fileno()) self._eventloop.add(client, eventloop.POLL_IN, self) if self._is_local: key, iv, m = encrypt.gen_key_iv(self._password, self._method) # spec https://shadowsocks.org/en/spec/one-time-auth.html if self._one_time_auth_enable: data = self._ota_chunk_data_gen(key, iv, data) data = encrypt.encrypt_all_m(key, iv, m, self._method, data) if not data: return else: data = data[header_length:] if not data: return try: client.sendto(data, (server_addr, server_port)) except (socket.error, OSError, IOError) as e: error_no = eventloop.errno_from_exception(e) if sys.platform == "win32": if error_no in (errno.EAGAIN, errno.EINPROGRESS, errno.EWOULDBLOCK, errno.WSAEWOULDBLOCK): pass else: shell.print_exception(e) elif error_no in (errno.EAGAIN, errno.EINPROGRESS, errno.EWOULDBLOCK): pass else: shell.print_exception(e)
def _handle_server(self): server = self._server_socket data, r_addr = server.recvfrom(BUF_SIZE) key = None iv = None if not data: logging.debug('UDP handle_server: data is empty') if self._stat_callback: self._stat_callback(self._listen_port, len(data)) if self._is_local: frag = common.ord(data[2]) if frag != 0: logging.warn('UDP drop a message since frag is not 0') return else: # RSV and FRAG are dropped data = data[3:] else: # 服务器解密 data, key, iv = encrypt.dencrypt_all(self._password, self._method, data) # decrypt data if not data: logging.debug('UDP handle_server: data is empty after decrypt') return # +------+---------------------+------------------+----------+-----------+ # | ATYP | Destination Address | Destination Port | DATA | HMAC - SHA1 | # +------+---------------------+------------------+----------+-----------+ # | 1 | Variable | 2 | Variable | 10 | # +------+---------------------+------------------+----------+-----------+ header_result = parse_header(data) if header_result is None: return addrtype, dest_addr, dest_port, header_length = header_result if self._is_local: server_addr, server_port = self._get_a_server() else: # 服务端拆包,ota验证 server_addr, server_port = dest_addr, dest_port # spec https://shadowsocks.org/en/spec/one-time-auth.html if self._one_time_auth_enable or addrtype & ADDRTYPE_AUTH: self._one_time_auth_enable = True if len(data) < header_length + ONETIMEAUTH_BYTES: logging.warn('UDP one time auth header is too short') return # 尾巴的hmac-sha1 _hash = data[-ONETIMEAUTH_BYTES:] data = data[:-ONETIMEAUTH_BYTES] _key = iv + key if onetimeauth_verify(_hash, data, _key) is False: logging.warn('UDP one time auth fail') return addrs = self._dns_cache.get(server_addr, None) if addrs is None: # 同步查询DNS addrs = socket.getaddrinfo(server_addr, server_port, 0, socket.SOCK_DGRAM, socket.SOL_UDP) if not addrs: # drop return else: self._dns_cache[server_addr] = addrs af, socktype, proto, canonname, sa = addrs[0] key = client_key(r_addr, af) client = self._cache.get(key, None) if not client: # 创建client socket # TODO async getaddrinfo if self._forbidden_iplist: if common.to_str(sa[0]) in self._forbidden_iplist: logging.debug('IP %s is in forbidden list, drop' % common.to_str(sa[0])) # drop return client = socket.socket(af, socktype, proto) client.setblocking(False) self._cache[key] = client # 记录数据要返回给谁 self._client_fd_to_server_addr[client.fileno()] = r_addr self._sockets.add(client.fileno()) self._eventloop.add(client, eventloop.POLL_IN, self) if self._is_local: # 客户端 ota数据生成,然后加密 key, iv, m = encrypt.gen_key_iv(self._password, self._method) # spec https://shadowsocks.org/en/spec/one-time-auth.html if self._one_time_auth_enable: data = self._ota_chunk_data_gen(key, iv, data) data = encrypt.encrypt_all_m(key, iv, m, self._method, data) if not data: return else: # 服务器忽略前面的地址与端口头 data = data[header_length:] if not data: return try: # 数据上行upstream client.sendto(data, (server_addr, server_port)) except IOError as e: err = eventloop.errno_from_exception(e) if err in (errno.EINPROGRESS, errno.EAGAIN): pass else: shell.print_exception(e)