def _on_remote_read(self): # handle all remote read events data = None if self._is_local: buf_size = UP_STREAM_BUF_SIZE else: buf_size = DOWN_STREAM_BUF_SIZE try: data = self._remote_sock.recv(buf_size) except (OSError, IOError) as e: if eventloop.errno_from_exception(e) in \ (errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK): return if not data: self.destroy() return self._update_activity(len(data)) logging.info("before encrypt len:%d data:%s" % (len(data), utils.encode(data))) if self._is_local: data = self._cryptor.decrypt(data) else: data = self._cryptor.encrypt(data) logging.info("after encrypt len:%d data:%s" % (len(data), utils.encode(data))) try: self._write_to_sock(data, self._local_sock) except Exception as e: shell.print_exception(e) if self._config['verbose']: traceback.print_exc() # TODO use logging when debug completed self.destroy()
def _on_local_read(self): # handle all local read events and dispatch them to methods for # each stage logging.info("_is_local:%d _is_tunnel:%d" % (self._is_local, self._is_tunnel)) if not self._local_sock: return is_local = self._is_local data = None if is_local: buf_size = UP_STREAM_BUF_SIZE else: buf_size = DOWN_STREAM_BUF_SIZE try: data = self._local_sock.recv(buf_size) except (OSError, IOError) as e: if eventloop.errno_from_exception(e) in \ (errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK): return if not data: self.destroy() return logging.info("before decrypt. len:%d data:%s" % (len(data), utils.encode(data))) self._update_activity(len(data)) if not is_local: data = self._cryptor.decrypt(data) if not data: return logging.info("after decrypt. len:%d data:%s" % (len(data), utils.encode(data))) if self._stage == STAGE_STREAM: self._handle_stage_stream(data) return elif is_local and self._stage == STAGE_INIT: # jump over socks5 init if self._is_tunnel: self._handle_stage_addr(data) return else: self._handle_stage_init(data) elif self._stage == STAGE_CONNECTING: self._handle_stage_connecting(data) elif (is_local and self._stage == STAGE_ADDR) or \ (not is_local and self._stage == STAGE_INIT): self._handle_stage_addr(data)
def _check_auth_method(self, data): # VER, NMETHODS, and at least 1 METHODS logging.info("len:%d data:" % (len(data), utils.encode(data))) if len(data) < 3: logging.warning('method selection header too short') raise BadSocksHeader socks_version = common.ord(data[0]) nmethods = common.ord(data[1]) logging.info("socks_version:%d nmethods:%d data:%s %s" % (socks_version, nmethods, str(data[0]), str(data[1]))) if socks_version != 5: logging.warning('unsupported SOCKS protocol version ' + str(socks_version)) raise BadSocksHeader if nmethods < 1 or len(data) != nmethods + 2: logging.warning('NMETHODS and number of METHODS mismatch') raise BadSocksHeader noauth_exist = False for method in data[2:]: if common.ord(method) == METHOD_NOAUTH: noauth_exist = True break if not noauth_exist: logging.warning('none of SOCKS METHOD\'s ' 'requested by client is supported') raise NoAcceptableMethods
def _on_local_write(self): # handle local writable event if self._data_to_write_to_local: data = b''.join(self._data_to_write_to_local) self._data_to_write_to_local = [] logging.info("len:%d data:%s" % (len(data), utils.encode(data))) self._write_to_sock(data, self._local_sock) else: logging.info("") self._update_stream(STREAM_DOWN, WAIT_STATUS_READING)
def _on_remote_write(self): # handle remote writable event self._stage = STAGE_STREAM if self._data_to_write_to_remote: data = b''.join(self._data_to_write_to_remote) self._data_to_write_to_remote = [] logging.info("len:%d data:%s" % (len(data), utils.encode(data))) self._write_to_sock(data, self._remote_sock) else: logging.info("") self._update_stream(STREAM_UP, WAIT_STATUS_READING)
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') logging.info("header_result :type:%s %s" % (type(header_result), header_result.__str__())) 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: logging.info("addrtype:%d _ota_enable:%d" % (addrtype, self._ota_enable)) # 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.warning('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 logging.info("current stage:%d _is_local:%d", self._stage, self._is_local) 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) logging.info("_data_to_write_to_remote:%s" % utils.encode(data)) elif len(data) > header_length: self._data_to_write_to_remote.append(data[header_length:]) logging.info("_data_to_write_to_remote:%s" % utils.encode(data[header_length:])) # notice here may go into _handle_dns_resolved directly self._dns_resolver.resolve(remote_addr, self._handle_dns_resolved)