def start_channel(self): logging.info("connect to %s:%d from %s:%d use %s:%d", self.remote_address[0], self.remote_address[1], self.local_address[0], self.local_address[1], self.shadow_address[0], self.shadow_address[1]) try: start = time.time() self.remote_stream = yield tcpclient.TCPClient().connect( host=self.shadow_address[0], port=self.shadow_address[1]) if self.stat_cb: self.stat_cb(self.shadow_address[0], time.time() - start) except IOError: logging.warning("connect shadow %s:%d failed", self.shadow_address[0], self.shadow_address[1]) if self.stat_cb: self.stat_cb(self.shadow_address[0], -1) self.destroy() raise gen.Return() try: data = common.chr(self.address_type) if self.address_type == SOCKS5_ADDRESS_TYPE_HOST: data += common.chr(len(self.remote_address[0])) data += self.remote_address[0] + struct.pack( "!H", self.remote_address[1]) self.remote_stream.write(self.encryptor.encrypt(data)) self.local_stream.read_until_close( streaming_callback=self._read_from_local) self.remote_stream.read_until_close( streaming_callback=self._read_from_remote) # self.destroy() except tornado.iostream.StreamClosedError: logging.warning("stream is closed")
def rnd_data(self, buf_size, full_buf_size): data_len = self.rnd_data_len(buf_size, full_buf_size) if data_len < 128: return common.chr(data_len + 1) + os.urandom(data_len) return common.chr(255) + struct.pack( '<H', data_len + 1) + os.urandom(data_len - 2)
def rnd_data(self, buf_size): if buf_size > 1200: return b'\x01' if buf_size > 400: rnd_data = os.urandom(common.ord(os.urandom(1)[0]) % 256) else: rnd_data = os.urandom(struct.unpack('>H', os.urandom(2))[0] % 512) if len(rnd_data) < 128: return common.chr(len(rnd_data) + 1) + rnd_data else: return common.chr(255) + struct.pack('>H', len(rnd_data) + 3) + rnd_data
def build_address(address): address = address.strip(b'.') labels = address.split(b'.') results = [] for label in labels: l = len(label) if l > 63: return None results.append(common.chr(l)) results.append(label) results.append(b'\0') return b''.join(results)
def nonce_increment(nonce, nlen): """ Increase nonce by 1 in little endian From libsodium sodium_increment(): for (; i < nlen; i++) { c += (uint_fast16_t) n[i]; n[i] = (unsigned char) c; c >>= 8; } :param nonce: string_buffer nonce :param nlen: nonce length :return: nonce plus by 1 """ c = 1 i = 0 # n = create_string_buffer(nlen) while i < nlen: c += ord(nonce[i]) nonce[i] = chr(c & 0xFF) c >>= 8 i += 1 return # n.raw
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 _ota_chunk_data_gen(self, key, iv, data): data = common.chr(common.ord(data[0]) | ADDRTYPE_AUTH) + data[1:] key = iv + key return data + onetimeauth_gen(data, key)
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)