text: import enum
  import ipaddress
  
  from .._helpers import is_ipv4_address
  from .._errors import ProxyError
  
  RSV = NULL = 0x00
  SOCKS_VER = 0x04
  
  
  class Command(enum.IntEnum):
      CONNECT = 0x01
      BIND = 0x02
  
  
  class ReplyCode(enum.IntEnum):
      REQUEST_GRANTED = 0x5A
      REQUEST_REJECTED_OR_FAILED = 0x5B
      CONNECTION_FAILED = 0x5C
      AUTHENTICATION_FAILED = 0x5D
  
  
  ReplyMessages = {
      ReplyCode.REQUEST_GRANTED: 'Request granted',
      ReplyCode.REQUEST_REJECTED_OR_FAILED: 'Request rejected or failed',
      ReplyCode.CONNECTION_FAILED: 'Request rejected because SOCKS server '
                                   'cannot connect to identd on the client',
      ReplyCode.AUTHENTICATION_FAILED: 'Request rejected because '
                                       'the client program '
                                       'and identd report different user-ids'
  }
  
  
  class ConnectRequest:
      def __init__(self, host: str, port: int, user_id: str, rdns: bool):
          self.host = host
          self.port = port
          self.user_id = user_id
          self.rdns = rdns
          self._resolved_host = None
  
      @property
      def need_resolve(self):
          return not is_ipv4_address(self.host) and not self.rdns
  
      def set_resolved_host(self, value):
          self._resolved_host = value
  
      def __bytes__(self):
          port_bytes = self.port.to_bytes(2, 'big')
          include_hostname = False
  
          if is_ipv4_address(self.host):
              host_bytes = ipaddress.IPv4Address(self.host).packed
          else:
              # not IP address, probably a DNS name
              if self.rdns:
                  # remote resolve (SOCKS4a)
                  include_hostname = True
                  host_bytes = bytes([NULL, NULL, NULL, 1])
              else:
                  # resolve locally
                  assert self._resolved_host is not None
                  addr = self._resolved_host
                  host_bytes = ipaddress.IPv4Address(addr).packed
  
          data = bytearray([SOCKS_VER, Command.CONNECT])
          data += port_bytes
          data += host_bytes
  
          if self.user_id:
              data += self.user_id.encode('ascii')
  
          data.append(NULL)
  
          if include_hostname:
              data += self.host.encode('idna')
              data.append(NULL)
  
          return bytes(data)
  
  
  class ConnectResponse:
      rsv: int
      reply: ReplyCode
  
      def __init__(self, data: bytes):
          assert len(data) == 2
          self.rsv = data[0]
          self.reply = data[1]  # noqa
  
      def validate(self):
          if self.rsv != RSV:  # pragma: no cover
              raise ProxyError('SOCKS4 proxy server sent invalid data')
  
          if self.reply != ReplyCode.REQUEST_GRANTED:  # pragma: no cover
              msg = ReplyMessages.get(self.reply, 'Unknown error')
              raise ProxyError(msg, self.reply)
  