Ejemplo n.º 1
0
    def connect_socks_server(self, socks_server):
        """Connecting to SOCKS server."""

        if not isinstance(socks_server, tuple) and len(socks_server) != 5:
            raise SocksException("Server must be tuple with 5 items.")

        addr, port, version, user, pwd = socks_server

        try:
            # If version is SOCKS5 need to negotiate for authentication method.
            if version == V_SOCKS5:
                auth = AUTH_NONE if not user else AUTH_UPASS
                self._sock.send(self.socks.connect_message(auth))
                reply = self._sock.recv(_BUFSIZE)
                auth_rep = self.socks.parse_conn_reply(reply)

                # Check if require authentication.
                if auth_rep == AUTH_UPASS:
                    self._socks_auth(user, pwd)
                elif auth_rep == AUTH_NO_ACCEPTABLE:
                    raise SocksException("No acceptable authentication method")
        except socket.error as e:
            msg = "Unable to connect the SOCKS server. {0}".format(e.args[1])
            raise SocksException(msg)

        self._is_server_connected = True
Ejemplo n.º 2
0
    def _send_request(self, cmd, addr, port, uid_or_atype):
        """Sends SOCKS request and wait for reply.
        Returns a parsed reply tuple."""

        if not self._is_server_connected:
            try:
                self._sock.connect(
                    (self.socks_server[0], self.socks_server[1]))
            except socket.error as e:
                msg = "Unable to connect the SOCKS server. {0}"
                raise SocksException(msg.format(e.args[1]))
            self.connect_socks_server(self.socks_server)

        req = self.socks.generate_request(cmd, addr, port, uid_or_atype)
        try:
            self._sock.send(req)
            reply = self._sock.recv(_BUFSIZE)
            if len(reply) == 0:
                raise SocksException("Server has closed connection")
            reply = self.socks.parse_reply(reply)

            if reply[0] not in (REP_SUCCESS, REP_REQ_GRANT):
                msg = "The server has rejected the requet. {0}:{1}"
                raise SocksException(msg.format(reply[1], reply[0]))

        except socket.error as e:
            msg = "Unable to send requst to the SOCKS server. {0}"
            raise SocksException(msg.format(e.args[1]))

        return reply
Ejemplo n.º 3
0
    def parse_udp(self, dgram):
        """Parse the SOCKS5 client UDP datagram."""

        # For parsing request need to find where data starts.
        try:
            addr_type = ord(dgram[3])
            if addr_type == ADD_IPV4:
                data = dgram[10:]
            elif addr_type == ADD_IPV6:
                data = dgram[22:]
            else:
                data = dgram[7 + ord(dgram[4]):]
        except IndexError as e:
            raise SocksException("Invalid SOCKS datagram received")

        # Parse the header.
        t_msg = self._parse_message(dgram[:-len(data)])
        ver, cmd, frag, addr_type, dst_addr, dst_port = t_msg

        # Version check.
        self.validate_version(ver, "Client", 0)

        # The cmd should be zero for UDP.
        if cmd != 0:
            raise SocksException("Invalid data from the server")

        return frag, addr_type, dst_addr, dst_port, data
Ejemplo n.º 4
0
    def _parse_message(self, message):
        """Parse SOCKS5 messages."""

        # Parse the first 4 bytes.
        try:
            version, cmd, rsv, addr_type = unpack_from("BBBB", message)
        except struct_error as e:
            raise SocksException("Invalid SOCKS message received")

        # Get address.
        if addr_type not in _ADD_TYPES:
            raise SocksException("Server using unknown address type")
        if addr_type == ADD_IPV4:
            addr = socket.inet_ntoa(message[4:8])
        elif addr_type == ADD_IPV6:
            # Get every group of number from address remove hex "0x" prefix,
            # add zero padding and join with ":".
            addr = ":".join(
                hex(i).replace("0x", "").zfill(4)
                for i in unpack_from("!HHHHHHHH", message, 5))
        else:
            domain_len = ord(message[4])
            addr = message[5:5 + domain_len]

        # Get port.
        port = unpack("!H", message[-2:])[0]

        # Validate message parameter.
        if not self.validate_msg_params(addr_type, addr, port):
            raise SocksException("Invalid message parameters received")
        return version, cmd, rsv, addr_type, addr, port
Ejemplo n.º 5
0
    def _socks_auth(self, user, pwd):
        """User and password authenticaion for connecting the SOCKS server."""

        try:
            self._sock.send(self.socks.client_auth(user, pwd))
            if not self.socks.parse_server_auth(self._sock.recv(_BUFSIZE)):
                raise SocksException("Authentication Failed")
        except socket.error as e:
            raise SocksException("Communication error. {0}".format(e.args[1]))
Ejemplo n.º 6
0
    def __init__(self,
                 family=socket.AF_INET,
                 socktype=socket.SOCK_STREAM,
                 proto=0,
                 socks_server=None,
                 *args,
                 **kwargs):
        """Initialize socket parameters.

        socks_server = (address, port, version, user, password) or "ipaddress"
        user and password can be None for no authentication."""

        # Protocol check.
        if socktype not in (socket.SOCK_DGRAM, socket.SOCK_STREAM):
            err_msg = "SockSocket provide only stream or dtagram communication"
            raise SocksException(err_msg)

        self.family = family
        self.type = socktype
        self.proto = proto

        # Initialize parent socket class. This initialization include the
        # initialization of self._sock the .pyd module of python sockets.
        # We will use self._sock for all comunication with the SOCKS server.
        try:
            socket.socket.__init__(self, family, socktype, proto, *args,
                                   **kwargs)
        except socket.error as e:
            raise SocksException(e.args[1])
        except AttributeError as e:
            raise ValueError("Invalid arguments")
        # The sockets' initializer overrides the delegated methods (such as
        # send() so we need to delete them for providing Socksocket methods.
        for attr in socket._delegate_methods:
            try:
                delattr(self, attr)
            except AttributeError:
                pass

        # Socket for UDP communication after UDP associate and address that
        # the SOCKS server listen to.
        self._udp_sock = None
        self.udp_assoc = None

        # Attribute initialize.
        self.peer_name = None
        self.socks_server = None
        self.socks = None
        self._is_server_connected = None
        if socks_server:
            self.set_server(socks_server)
Ejemplo n.º 7
0
    def _parse_message(self, messsage):
        """Parse SOCKS4 messages."""

        try:
            version, cmd, port = unpack_from("!BBH", messsage)
        except struct_error as e:
            raise SocksException("Invalid SOCKS message received")
        addr = socket.inet_ntoa(messsage[4:8])

        # Validate message parameter.
        if not self.validate_msg_params(ADD_IPV4, addr, port):
            raise SocksException("Invalid message parameters received")

        return version, cmd, addr, port
Ejemplo n.º 8
0
    def reverse_socks_connect(self,
                              lcl_port=0,
                              rmt_addr=[],
                              backlog=0,
                              version=V_SOCKS5,
                              user=None,
                              pwd=None):
        """Listen on lcl_port and try to authenticate with incoming
        connections. rmt_addr is a list of accepted addresses tuple and if
        it is  empty (default) accept every address or port. backlog is like
        the backlog argument in regular socket.listen().
        Returns connected Socksocket."""

        if self.type == socket.SOCK_DGRAM:
            tmp_sock = socket.socket(self.family, socket.SOCK_STREAM,
                                     self.proto)
            self._sock = tmp_sock._sock

        self._sock.bind(("", lcl_port))
        self._sock.listen(backlog)

        # Setting socket to non-blocking for solve python bug on Windows that
        # doesn't allow to break if no datagram recieved.
        self._sock.setblocking(False)
        sock = addr = None
        while True:
            try:
                sock, addr = self._sock.accept()
            except socket.error as e:
                time.sleep(1)
            if sock:
                sock.setblocking(True)
                self._sock.setblocking(True)
                break

        if rmt_addr and addr not in rmt_addr:
            sock.close()
            msg = "{0}:{1} tried to connect and refused.".format(*addr)
            raise SocksException(msg)

        # Create new Socksocket.
        socks_serv = (addr[0], addr[1], version, user, pwd)
        socksocket = SockSocket(self.family, self.type, self.proto, socks_serv)
        socksocket._sock = sock
        socksocket.connect_socks_server(socksocket.socks_server)
        if not socksocket._is_server_connected:
            msg = "Failed to authenticate connection by the SOCKS server {}:{}."
            raise SocksException(msg.format(*addr))

        return socksocket
Ejemplo n.º 9
0
    def parse_server_auth(self, reply):
        """Parse server response for user:password authentication message."""

        if len(reply) != 2:
            raise SocksException("Server response is invalid")

        # Parse response.
        if ord(reply[0]) != 1:
            raise SocksException("Invalid data from the server")
        elif ord(response[1]) != 0:
            return False

        # Status is 0.
        return True
Ejemplo n.º 10
0
    def connect(self, addr):
        """Connect the socksocket to remote address.
        The address must be a pair (host, port).

        If socks_server is not defined connect as normal OS socket.
        else connect through the SOCKS server."""

        if self.socks_server:
            # Connect with UDP just bind and set destination address.
            if self.type == socket.SOCK_DGRAM:
                self.peer_name = addr
                return self.bind(("0.0.0.0", 0))
            # Connect to SOCKS server and send CONNECT request.
            address, port = addr
            if self.socks.version == V_SOCKS4:
                uid_or_atype = self.socks_server[3]
            else:
                # Get address type.
                uid_or_atype = self.socks.get_addr_type(address)
                if not uid_or_atype:
                    msg = "The address type is not supported by SOCKS"
                    raise SocksException(msg)

            # Create socket connection.
            self._send_request(CMD_CONNECT, address, port, uid_or_atype)

        # There is no SOCKS server defined. connect as regular socket.
        else:
            socket.socket.connect(self, addr)
        self.peer_name = addr
Ejemplo n.º 11
0
    def sendto(self, data, flags_or_addr, addr=None):
        """Send data to unconnected socket. If SOCKS server defined and
        socket type is datagram first connect to SOCKS server and send
        a UDP request. SockSocket doesn't support fragmentation for now."""

        # Use normal sendto if using TCP (if not connected will raise
        # error) or if there is no SOCKS server defined.
        if not self.socks_server or self.type != socket.SOCK_DGRAM:
            if not addr:
                return self._sock.sendto(data, flags_or_addr)
            return self._sock.sendto(data, flags_or_addr, addr)

        if self.socks.version != V_SOCKS5:
            msg = "UDP datagram is not supported by the SOCKS version"
            raise SocksException(msg)

        # Send UDP request for UDP association.
        if not self.udp_assoc:
            if not self._udp_sock:
                self.bind(("", 0))
            dst_addr, dst_port = self._udp_sock.getsockname()
            addr_type = self.socks.get_addr_type(dst_addr)
            reply = self._send_request(CMD_UDP, dst_addr, dst_port, addr_type)
            self.udp_assoc = reply[-2:]

        #Send UDP as standalone with no fragmentation.
        t_addr, t_port = addr if addr else flags_or_addr
        addr_type = self.socks.get_addr_type(t_addr)
        dgram = self.socks.generate_udp(0, t_addr, t_port, addr_type, data)
        if not addr:
            return self._udp_sock.sendto(dgram, self.udp_assoc)
        return self._udp_sock.sendto(dgram, flags_or_addr, self.udp_assoc)
Ejemplo n.º 12
0
    def validate_version(self, rep_version, sndr, version=None):
        """Check SOCKS version in the response."""

        # Set default version if needed.
        version = version if version is not None else self.version

        if rep_version != version:
            raise SocksException("{0} has invalid SOCKS version".format(sndr))
Ejemplo n.º 13
0
    def set_server(self,
                   addr,
                   port=DEFAULT_PORT,
                   version=V_SOCKS5,
                   user=None,
                   pwd=None):
        """Set SOCKS server for tunnel.

        addr can be an IP address or Domain name and can be tuple
        with the format (address, port, version, user, password)
        user and password can be None for no authentication. If
        addr is a tuple then all the other parameter is overwritten."""

        # tuple check.
        if isinstance(addr, tuple):
            if len(addr) != 5:
                err = "addr must be tuple with total length of 5 or an address"
                raise SocksException(err)
            addr, port, version, user, pwd = addr

        if self.type == socket.SOCK_DGRAM and version == V_SOCKS4:
            raise SocksException("SOCKS4 can't UDP associate.")

        # Address and parameters check and get _sock.
        self.socks = socks(version)
        s = None
        try:
            if self.socks.get_addr_type(addr):
                addr_info = socket.getaddrinfo(addr, port, socket.AF_UNSPEC,
                                               socket.SOCK_STREAM)
                for res in addr_info:
                    try:
                        s = socket.socket(res[0], socket.SOCK_STREAM, res[2])
                    except socket.error:
                        s = None
        except socket.error as e:
            err = "Invalid server parameters. {0}".format(e.args[1])
            raise SocksException(err)

        if not s:
            raise SocksException("Could not open socket to the SOCKS server")

        # Set the right attributes for connecting through SOCKS server.
        self._sock = s._sock
        self.socks_server = (addr, port, version, user, pwd)
        self._is_server_connected = False
Ejemplo n.º 14
0
    def generate_reply(self, rep, bnd_addr, bnd_port, addr_type):
        """Generate a SOCKS5 reply that will be sent to the client."""

        # Parameters check.
        if (rep not in _SOCKS5_REPLIES or
                not self.validate_msg_params(addr_type, bnd_addr, bnd_port)):
            raise SocksException("Invalid server reply parameters")

        return self._generate_message(rep, bnd_addr, bnd_port, addr_type)
Ejemplo n.º 15
0
    def generate_request(self, cmd, dst_addr, dst_port, addr_type):
        """Generate a SOCKS5 request that will be sent to the server."""

        # Parameters check.
        if (cmd not in _CMD or
                not self.validate_msg_params(addr_type, dst_addr, dst_port)):
            raise SocksException("Invalid client request parameters")

        return self._generate_message(cmd, dst_addr, dst_port, addr_type)
Ejemplo n.º 16
0
    def __init__(self, version=V_SOCKS5):
        """Initialize SOCKS parameters."""

        # SOCKS parameter check.
        if version not in _VERSIONS:
            raise SocksException("Wrong SOCKS version")

        # Instance attributes initialization.
        self.version = version
Ejemplo n.º 17
0
    def generate_reply(self, rep, dst_addr, dst_port):
        """Generate a SOCKS4 reply that will be sent to the client."""

        # Parameters check.
        if (rep not in _SOCKS4_REPLIES
                or not self.validate_msg_params(ADD_IPV4, dst_addr, dst_port)):
            raise SocksException("Invalid server reply parameters")

        return self._generate_message(rep, dst_addr, dst_port, 0)
Ejemplo n.º 18
0
    def _recv_atrr(self, func, *args, **kwargs):
        """Activate the socket recv method needed."""

        if self.type != socket.SOCK_DGRAM or not self.socks_server:
            recv = getattr(self._sock, func)
        elif not self._udp_sock:
            raise SocksException("Socket must be bound")
        else:
            recv = getattr(self._udp_sock, func)

        return recv(*args, **kwargs)
Ejemplo n.º 19
0
    def client_auth(self, user, pwd):
        """Generate authentication message with user and password for
        connecting SOCKS5 server."""

        # Validate user and password.
        if not self.validate_user_pwd(user, pwd):
            raise SocksException("Invalid client authentication parameters")

        msg = pack("BB", 1, len(user)) + user
        if not pwd:
            return msg

        return msg + pack("B", len(pwd)) + pwd
Ejemplo n.º 20
0
    def wait_bind_connect(self):
        """Wait for the second bind reply from SOCKS server and set
        self.peer_name. Return the peer name on success and  raise exception if
        connection is not succeeded."""

        reply = self.socks.parse_reply(self._sock.recv(_BUFSIZE))

        if reply[0] not in (REP_SUCCESS, REP_REQ_GRANT):
            msg = "The server has rejected the requet. {0}"
            raise SocksException(msg.format(reply[1]), reply[0])

        self.peer_name = (reply[-2], reply[-1])
        return self.peer_name
Ejemplo n.º 21
0
    def generate_udp(self, frag, dst_addr, dst_port, addr_type, data):
        """Generate UDP request header for sending UDP datagram.."""

        # Parameters check.
        if not self.validate_msg_params(addr_type, dst_addr, dst_port):
            raise SocksException("Invalid client request parameters")

        # Create message and add data.
        udp_msg = self._generate_message(0, dst_addr, dst_port, addr_type, 0,
                                         frag)
        udp_msg += data

        return udp_msg
Ejemplo n.º 22
0
    def _set_chain(self, chain):
        """Set the SOCKS chain for for setting socks_chain as a property."""

        if self.connected:
            msg = "Can't change SOCKS chain after connect through it"
            raise SocksException(msg)

        if not isinstance(chain, list):
            raise SocksException("socks_chain must be a list")

        if self.udp and len(chain) != 1:
            msg = "SocksClient support only one chain link for UDP"
            raise SocksException(msg)

        # Check if every item in the chain is string or tuple
        # with the length of 2 or 5.
        for i in chain:
            if (not isinstance(i[0], str)
                    and (not isinstance(i, tuple) or (len(i) not in (2, 5)))):
                raise SocksException("Invalid items is sock_chain")

        self._socks_chain = chain
Ejemplo n.º 23
0
    def generate_request(self, cmd, dst_addr, dst_port, userid):
        """Generate a SOCKS4 request that will be sent to the server."""

        # Parameters check.
        if (cmd not in _CMD or cmd == CMD_UDP
                or not self.validate_msg_params(ADD_IPV4, dst_addr, dst_port)):
            raise SocksException("Invalid client request parameters")

        # Create request message.
        req_msg = self._generate_message(cmd, dst_addr, dst_port)
        if userid:
            req_msg += userid
        req_msg += pack("B", 0)

        return req_msg
Ejemplo n.º 24
0
    def parse_conn_reply(self, reply):
        """Parse SOCKS5 server reply for connect message."""

        if len(reply) != 2:
            raise SocksException("Server response is invalid")

        # Parse reply.
        self.validate_version(ord(reply[0]), "Server")

        if ord(reply[1]) == AUTH_NONE:
            return AUTH_NONE
        elif ord(reply[1]) == AUTH_UPASS:
            return AUTH_UPASS

        return AUTH_NO_ACCEPTABLE
Ejemplo n.º 25
0
    def parse_auth(self, msg):
        """Parse authentication message with user and password from
        SOCKS5 client."""

        try:
            version, user_len = unpack_from("BB", msg)
        except struct_error as e:
            raise SocksException("Invalid authentication message from client")
        self.validate_version(version, "Client", 1)

        # The password is the last byte of the message. therefore if we know
        # the length of the username we know where the password starts.
        user = msg[2:user_len]
        pwd = msg[3 + user_len:]

        return user, pwd
Ejemplo n.º 26
0
    def socks_bind(self, addr, port):
        """Send SOCKS bind request to the SOCKS server. Returns the new
        SOCKS server bound address and port. The bind request must follow
        another socket connect request for notify the target where to
        connect."""

        if not self.socks_server:
            raise SocksException("SOCKS server is undefined")

        # Set parameters and send request.
        if self.socks.version == V_SOCKS4:
            uid_or_atype = self.socks_server[3]
        else:
            uid_or_atype = self.socks.get_addr_type(addr)

        # Send request and return address and port.
        reply = self._send_request(CMD_BIND, addr, port, uid_or_atype)
        return reply[-2], reply[-1]
Ejemplo n.º 27
0
    def parse_connect(self, request):
        """Parse connect message from SOCKS5 client. Returns a list
        with all SOCKS client supported authentication methods."""

        try:
            ver, nmethod = unpack_from("BB", request)
        except struct_error as e:
            raise SocksException("Invalid connect message from client")

        self.validate_version(ver, "Client")

        # Parse authentication methods.
        methods = []
        for i in xrange(nmethod):
            if ord(request[i + 2]) in _AUTH_METHODS:
                methods.append(ord(request[i + 2]))

        if not methods:
            methods.append(AUTH_NO_ACCEPTABLE)

        return methods