示例#1
0
    def handshake_socks4(self):
        """
        SOCKS4 handshake

        The danted SOCKS server doesn't seem to understand SOCKS4a,
        so this does not do the SOCKS4a trick of requesting DNS resolution
        on the proxy.

        The request has five fields:

        - The protocol number (4)
        
        - The request type (1 == TCP connection

        - The destination port (2 bytes, network order)

        - The destination ipaddr (4 bytes, network order)

        - The nul-terminated user-ID.  (1 byte, always 0)
        """

        # print 'SocksShim.handshake_socks4'

        request = struct.pack('!BBH', 4, 1, self.socks_dest_port)

        try:
            addr = socket.gethostbyname(self.socks_dest_host)
        except BaseException, exc:
            QE2LOG.error('SocksShim.handshake_socks4 ERROR [%s]', str(exc))
            self.transport.loseConnection()
            return
示例#2
0
    def connectionLost(self, reason=twisted.internet.protocol.connectionDone):
        """
        Connection to the SOCKS server was lost during handshake
        """

        QE2LOG.error('SocksShim.connectionLost [%s] %s', str(self),
                     str(reason))
示例#3
0
    def connectionMade(self):
        """
        After the connection is established, send the SOCKS4/SOCKS4a
        request to the server. 
        """

        # print '__SocksShim.connectionMade'

        self.socks_dest_host = self.factory.socks_dest_host
        self.socks_dest_port = self.factory.socks_dest_port
        self.socks_dest_class = self.factory.socks_dest_class

        # Decide whether to use SOCKS4 or SOCKS5 by testing
        # whether we have a dotted quad or something else.
        # If we already have a dotted quad address, then use
        # SOCKS4; otherwise use SOCKS5 with DNS resolution
        #
        try:
            _addr = socket.inet_aton(self.socks_dest_host)
        except socket.error:
            self.socks_dest_ver = 5
            self.handshake_socks5()
        except BaseException, exc:
            QE2LOG.error('SocksSum.connectionMade() [%s]', str(exc))
            self.transport.loseConnection()
示例#4
0
    def del_bottom(self, bottom):
        """
        Remove a *Bottom instance from this endpoint

        If the instance isn't part of this endpoint, print a diagnostic
        and swallow the error
        """

        try:
            self.bottoms.remove(bottom)
        except KeyError, exc:
            QE2LOG.error('Qe2Endpoint.del_bottom: missing bottom')
示例#5
0
    def data_recv_5(self):
        """
        Handle the SOCKS responses
        """

        # QE2LOG.debug('incoming len %d', len(self.shim_recv_buf))

        if self.handshake_state == self.HANDSHAKE_STATE_1:
            if len(self.shim_recv_buf) < 2:
                return

            if self.shim_recv_buf[:2] != '\x05\x00':
                QE2LOG.warn('failed SOCKS5 first response')
                self.transport.loseConnection()
            else:
                self.shim_recv_buf = self.shim_recv_buf[2:]
                self.handshake_state = self.HANDSHAKE_STATE_2

                self.handshake_socks5()

        elif self.handshake_state == self.HANDSHAKE_STATE_2:

            # NOTE: we only handle the case where the server returns
            # us an ordinary IPv4 address (which is expected for a
            # TCP connect request).  If a server returns a DNS-type
            # address, which has a variable length, this function
            # can't parse it yet.

            resp_len = 10
            if len(self.shim_recv_buf) < resp_len:
                return

            response = self.shim_recv_buf[:resp_len]
            self.shim_recv_buf = self.shim_recv_buf[resp_len:]

            expected_prefix = '\x05\x00\x00\x01'

            if response[:len(expected_prefix)] != expected_prefix:
                QE2LOG.warn('failed SOCKS5 second response [prefix]')
                self.transport.loseConnection()
                return

            # NOTE: we do not attempt to validate the returned IPv4
            # address or ephemeral port.  We could check them for
            # basic sanity (make sure they're valid, at least) but
            # we can't check that they're *correct*.

            self.handshake_state = self.HANDSHAKE_COMPLETE

        else:
            QE2LOG.error('SocksShim.data_recv_5 unhandled state')
            assert (0)
示例#6
0
    def segment_covers(segment, hole_start, hole_last):
        """
        Return a segment representing the subset of the hole that
        is covered by the segment, or None of if the segment does
        not overlap the hole
        """

        hole_len = 1 + hole_last - hole_start

        seg_start = segment[FIRST_OFF_IND]
        seg_last = segment[LAST_OFF_IND]
        seg_data = segment[DATA_IND]

        # Need to be very careful with the extents here
        #
        # First check whether the hole overlaps the segment
        # at all, and if not, return None.
        #
        # Then check whether the segment first within the hole.
        #
        # Finally check each of the edge cases
        #
        if (seg_last < hole_start) or (seg_start > hole_last):
            # print 'case X'
            return None

        elif (seg_start <= hole_start) and (seg_last > hole_last):
            start = hole_start - seg_start
            last = start + hole_len
            new_seg = [hole_start, hole_last, seg_data[start:last]]
            # print 'case 0 hole [%d, %d] %s' % (
            #         hole_start, hole_last, str(new_seg))
            return new_seg

        elif (seg_start >= hole_start) and (seg_last <= hole_last):
            return segment

        elif seg_start >= hole_start:
            return [seg_start, hole_last, seg_data[:1 + hole_last - seg_start]]

        elif seg_last >= hole_start:
            return [hole_start, seg_last, seg_data[hole_start - seg_start:]]

        else:
            QE2LOG.error('unhandled case')
            assert (False)
示例#7
0
    def handle_init(self, msg):
        """
        Handle receipt of a OP_INIT msg.

        Note that it is an error for this to be received
        by a server, so QuiltServers should subclass this method

        The server_quilt_uuid is the uuid assigned to this
        quilt by the server.  If this is the first OP_INIT
        we've gotten from the server, then remember make note
        of it.  If it's not the first OP_INIT, then check that
        the server_quilt_uuid matches previous server_quilt_uuids,
        and drop the connection if it does not.
        """

        server_quilt_uuid = msg.data[:16]

        if self.server_quilt_uuid == None:
            QE2LOG.info('got server_quilt_uuid %s',
                        server_quilt_uuid.encode('hex'))
            self.server_quilt_uuid = server_quilt_uuid
        elif self.server_quilt_uuid != server_quilt_uuid:
            QE2LOG.warn('server_quilt_uuid mismatch: expected %s got %s',
                        self.server_quilt_uuid.encode('hex'),
                        server_quilt_uuid.encode('hex'))
            QE2LOG.info('dropping quilt')

            # If there is a channel manager, ask it to stop
            # all channels
            #
            if self.chanman:
                QE2LOG.warn('stopping all connections')
                self.chanman.stop_all()
            else:
                QE2LOG.error('no chanman?')

            # reactor.callLater(2, reactor.stop)
            reactor.stop()
示例#8
0
    def dataReceived(self, data):
        """
        Receive data in response to a handshake message, and dispatch
        to the correct handler (depending on which handshake we're
        doing

        If the handshake is complete, then monkey-patch this instance
        to have the desired class and initialize the new instance
        (with connectionMade() and dataReceived, if appropriate)
        """

        # print '__SocksShim.dataReceived'

        self.shim_recv_buf += data

        if self.socks_dest_ver == 4:
            self.data_recv_4()
        elif self.socks_dest_ver == 5:
            self.data_recv_5()
        else:
            QE2LOG.error('unhandled SOCKS version [%s]',
                         str(self.socks_dest_ver))
            assert (0)

        if self.handshake_state == self.HANDSHAKE_COMPLETE:
            # If we've reached this point, then everything is OK,
            # at least as far as we can tell at this point.
            #
            # Monkey-patch this instance, connect to the new class,
            # and then pretend to receive any pending data

            received = self.shim_recv_buf
            self.__class__ = self.socks_dest_class

            self.__init__()
            self.connectionMade()
            if received:
                self.dataReceived(received)
示例#9
0
    def process_msgs(self, msgs):
        """
        Process messages received by a channel
        """

        max_offset = -1
        delivered = False

        old_remote_ack_recv = self.remote_ack_recv

        for msg in msgs:
            QE2LOG.debug('RECEIVE MSG %s', str(msg))

            # update max_offset and remote_ack_recv, no
            # matter what the message type is
            #
            if max_offset < msg.ack_send:
                max_offset = msg.ack_send

            if self.remote_ack_recv < msg.ack_recv:
                self.remote_ack_recv = msg.ack_recv
            if self.remote_ack_send < msg.ack_send:
                self.remote_ack_send = msg.ack_send

            if msg.opcode == Qe2Msg.OP_DATA:
                if len(msg.data) > 0:
                    self.deliver(msg.send_offset, msg.data)
                    delivered = True

            elif msg.opcode == Qe2Msg.OP_PING:
                pass
            elif msg.opcode == Qe2Msg.OP_HOLE:

                if self.remote_ack_recv > msg.hole[0]:
                    QE2LOG.info('UNEXPECTED hole start before remote_ack_recv')

                self.add_remote_hole(msg.hole[0],
                                     msg.hole[0] + msg.hole[1] - 1)
            elif msg.opcode == Qe2Msg.OP_CHAN:
                QE2LOG.info('got OP_CHAN message')
                pass
            elif msg.opcode == Qe2Msg.OP_HALT:
                # TODO: this should close down the quilt (not just
                # one particular channel)
                #
                QE2LOG.info('got OP_HALT message')
            elif msg.opcode == Qe2Msg.OP_INIT:
                self.handle_init(msg)
            else:
                QE2LOG.error('UNHANDLED msg %d', msg.opcode)

        # If this sequence of msgs included delivered data, then
        # see if there's anything to push to the app
        #
        if delivered:
            self.pending_to_app()

        if max_offset > self.ack_recv:
            QE2LOG.info('max_offset %d > self.ack_rev %d: something missing',
                        max_offset, self.ack_recv)
            self.add_local_hole(self.ack_recv + 1, max_offset)
            QE2LOG.debug('LOCAL holes %s', str(sorted(self.local_holes)))

        # We've gotten acknowledgment from the remote endpoint
        # for additional data.  Throw away our old copy.
        #
        # TODO: it is much more efficient to delete larger chunks
        # periodically rather than small chunks constantly.
        #
        if old_remote_ack_recv < self.remote_ack_recv:
            self.pending_out.discard(self.remote_ack_recv -
                                     old_remote_ack_recv)
示例#10
0
                request += chr(len(self.socks_dest_host))
                request += self.socks_dest_host
            except BaseException, exc:
                QE2LOG.warn('SocksShim.handshake_socks5 (2) ERR [%s]',
                            str(exc))
                self.transport.loseConnection()
                return
            else:
                request += '\x01'
                request += addr

            request += struct.pack('!H', self.socks_dest_port)

            self.transport.write(request)
        else:
            QE2LOG.error('SocksShim.handshake_socks5 unhandled state')

    def data_recv_4(self):
        """
        Handle the SOCKS4 response
        """

        needed = 8

        if len(self.shim_recv_buf) < needed:
            return

        response = self.shim_recv_buf[:needed]
        self.shim_recv_buf = self.shim_recv_buf[needed:]

        (_null, status, _port, _ipaddr) = struct.unpack('!BBHL', response)
示例#11
0
 def handle_init(self, msg):
     QE2LOG.error('server received an OP_INIT msg; unexpected')