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: # 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_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)
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])) #logging.info('connecting %s:%d from %s:%d, %s' % # (common.to_str(remote_addr), remote_port, # self._client_address[0], self._client_address[1], data)) 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)