Example #1
0
    def connect(self, addr):
        if self in _active_socks:
            raise SockError('connect', 'connection alreay exists')
        if self in _listening_socks:
            raise SockError('connect', 'listening socket')

        ip, port = addr
        ip = ip4(ip)
        if ip == 0:
            raise SockError('connect', 'ip address not specified')
        if port == 0:
            raise SockError('connect', 'port not specified')
        if not 0 < port < 65535:
            raise SockError('connect', 'invalid port number %d' % port)

        with _slock:
            if self in _binding_socks:
                if self.la.ip == 0:
                    lip = net.ip_hint(ip)
                    if not lip:
                        raise SockError('connect', 'no route to %s' % ip)
                    assert (self.la.port)
                    self.la = sockaddr(lip, self.la.port)
                _binding_socks.remove(self)
            else:
                assert self.la is None
                lip = net.ip_hint(ip)
                if not lip:
                    raise SockError('connect', 'no route to %s' % ip)
                self.la = sockaddr(lip, _alloc_port())

            self.fa = sockaddr(ip, port)
            assert all(s.la != self.la or s.fa != self.fa
                       for s in _active_socks)
            self._inq = queue.Queue()
            self._txbuf = b''
            self._rtq = []
            _active_socks.append(self)

            self.iss = gen_isn(self.la, self.fa)
            self.snd_una = self.iss
            self.snd_nxt = self.iss + 1
            self._send(None,
                       self.iss,
                       None, (MaxSynRetries, SynInterval),
                       SYN=1,
                       wnd=DefWindowSize,
                       mss=1460)
            self._trans(TCPS.SYN_SENT)

        # block
        self._connect_evt = threading.Event()
        self._connect_status = None
        self._connect_evt.wait()
        if self._connect_status is not None:
            raise SockError('Connect', self._connect_status)
Example #2
0
def addrmask(ip):
    '''For testing only'''
    ip = ip4(ip)
    sip = net.ip_hint(ip)
    if not sip:
        raise ValueError('no route to %s', ip)
    send(sip, ip, Pack.MaskRequest(12345, 1))
Example #3
0
def ping(ip):
    '''For testing only'''
    ip = ip4(ip)
    sip = net.ip_hint(ip)
    if not sip:
        raise ValueError('no route to %s', ip)
    send(sip, ip, Pack.Echo(12345, 1, b'1234567' * 8))
Example #4
0
def time(ip):
    '''For testing only'''
    ip = ip4(ip)
    sip = net.ip_hint(ip)
    if not sip:
        raise ValueError('no route to %s', ip)
    ts = _timestamp()
    send(sip, ip, Pack.TimestampRequest(12345, 1, ts, 0, 0))
Example #5
0
 def sendto(self, ip, pkt):
     if self._closed:
         raise SockError('socket closed')
     if self.fip:
         raise SockError('foreign address already bound')
     if self.IP_HDRINCL:
         raise SockError('calling sendto() with IP_HDRINCL')
     ip = ip4(ip)
     if ip == 0:
         raise SockError('not a valid IP address')
     lip = self.lip
     if not lip:
         lip = net.ip_hint(ip)
         if not lip:
             raise SockError('no route to %s' % ip)
     log.info('SEND %s -> %s, %r', lip, ip, pkt)
     ipv4.send(lip, ip, pkt)
Example #6
0
 def send(self, pkt):
     if self._closed:
         raise SockError('socket closed')
     if self.IP_HDRINCL:
         if not isinstance(pkt, ipv4.Pack):
             raise SockError('not a IPv4 packet')
         _stat.rawout += 1
         log.info('SEND %s', pkt)
         ipv4.send_ip(pkt)
     else:
         fip = self.fip
         if not fip:
             raise SockError('foreign address not bound')
         lip = self.lip
         if not lip:
             lip = net.ip_hint(fip)
             if not lip:
                 raise SockError('no route to %s' % fip)
         log.info('SEND %s -> %s, %r', lip, fip, pkt)
         ipv4.send(lip, fip, pkt)
Example #7
0
def send(from_ip, to_ip, pdu, **extras):
    if to_ip == 0:
        log.error('target IP cannot be 0!')
        return 0

    if from_ip == 0:
        from_ip = net.ip_hint(to_ip)

    _stat.tx_count += 1

    pac = Pack(from_ip, to_ip, pdu, **extras)

    log.debug('SEND %s', pac)

    # TODO: obtain MTU from the sending interface
    mtu = 1500

    # Fast path - no fragmentation
    if len(pac) <= mtu:
        net.send(pac)
        return 1

    # fragmentation
    if pac.DF:
        # TODO: ICMP error
        log.warning('cannot send large packet with DF set')
        _stat.tx_dropped += 1
        return 0

    _stat.tx_fragmented += 1
    log.info('fragmenting large packet')

    hdr = pac.hdr
    nhdr = len(hdr)
    if hasattr(pdu, 'pack'):
        pdu = pdu.pack(None)
    npdu = len(pdu)

    ps = (mtu - nhdr) >> 3 << 3
    (n, r) = divmod(npdu, ps)
    if r == 0:
        n -= 1

    # send the last frag first
    net.send(Pack(from_ip, to_ip, pdu[n * ps:],
                  DSCP=pac.DSCP, ECN=pac.ECN,
                  proto=pac.proto, ttl=pac.ttl, ident=pac.ident,
                  MF=0, offset=n*ps>>3,
                  opts=tuple(opt for opt in pac.opts if opt.Copy)))

    # send remaining frags in reversed order
    while n > 0:
        n -= 1
        offset = n * ps
        net.send(Pack(from_ip, to_ip, pdu[offset:offset+ps],
                      DSCP=pac.DSCP, ECN=pac.ECN,
                      proto=pac.proto, ttl=pac.ttl, ident=pac.ident,
                      MF=1, offset=offset>>3,
                      opts=tuple(opt for opt in pac.opts if n==0 or opt.Copy)))

    return 1
Example #8
0
    def _recv(self, from_ip, seg):
        faddr = sockaddr(from_ip, seg.sp)
        self.dbg('RECV from %s: %s', faddr, seg)

        if self._st == TCPS.CLOSED:
            # TODO: check ACK?
            self._send(None, seg.ack, seg.seq + seg.len, None, RST=1)
            return

        if self._st == TCPS.LISTEN:
            if seg.RST:
                self.warn('RST from %s', faddr)
            elif seg.ACK:
                self.warn('ACK from %s', faddr)
                if self in _active_socks:
                    self._send(None, seg.ack, None, None, RST=1)
            elif seg.FIN:
                self.warn('FIN from %s', faddr)
                if self in _active_socks:
                    self._send(None, seg.ack, None, None, RST=1)
                return
            elif seg.SYN:
                if seg.data:
                    # TODO
                    self.error('SYN with data')
                    return
                if hasattr(self, '_pending_conns'):
                    # this is a listening socket
                    if self._pending_conns.qsize() > MaxSynBacklog:
                        self.warn('backlog queue overflow!')
                        return
                    newso = _Socket(self.blocking, self.timeout)
                    newso.la = self.la
                    newso._st = TCPS.LISTEN
                    newso._listenq = self._pending_conns
                    newso._inq = queue.Queue()
                    newso._txbuf = b''
                    newso._rtq = []
                    newso.fa = faddr
                    # _slock should be hold
                    _active_socks.append(newso)
                    newso._recv(from_ip, seg)
                    return
                self.irs = seg.seq
                self.rcv_nxt = seg.seq + 1
                if seg.ws:
                    self.peer_ws = 1 << seg.ws
                else:
                    self.peer_ws = 1
                self.snd_wnd = seg.wnd * self.peer_ws
                if seg.mss:
                    self.peer_mss = seg.mss
                else:
                    self.peer_mss = 576
                if self.la.ip == 0:
                    # update local
                    self.la = sockaddr(net.ip_hint(from_ip), self.la.port)
                    assert self.la.ip
                self.iss = gen_isn(self.la, self.fa)
                self.snd_nxt = self.iss + 1
                self.snd_una = self.iss
                self._send(None,
                           self.iss,
                           self.rcv_nxt, (MaxSynAckRetries, SynInterval),
                           SYN=1,
                           wnd=DefWindowSize,
                           mss=1460)
                self._trans(TCPS.SYN_RCVD)
            else:
                self.warn('bad segment from %s', faddr)
                self._send(None, seg.ack, None, None, RST=1)
            return

        if self._st == TCPS.SYN_SENT:
            if seg.ACK:
                if seg.ack != self.iss + 1:
                    self.warn('bad ACK, expecting %d, got %d', self.iss + 1,
                              seg.ack)
                    self._send(None, seg.ack, None, None, RST=1)
                elif seg.RST:
                    self.warn('Connection refused by %s', faddr)
                    # unblock connect()
                    self._connect_evt.set()
                    self._connect_status = 'Connection refused'
                    self._close('Connection refused')
                elif seg.SYN:
                    self.irs = seg.seq
                    self.rcv_nxt = seg.seq + 1
                    if seg.ws:
                        self.peer_ws = 1 << seg.ws
                    else:
                        self.peer_ws = 1
                    self.snd_wnd = seg.wnd * self.peer_ws
                    if seg.mss:
                        self.peer_mss = seg.mss
                    else:
                        self.peer_mss = 576
                    self._ack_received(seg)
                    self.log('connected with %s', faddr)
                    # unblock connect()
                    self._send(None, self.snd_nxt, self.rcv_nxt, None)
                    self._trans(TCPS.ESTABLISHED)
                    self._connect_evt.set()
                else:
                    # keep waiting
                    self.warn('ACK w/o SYN from %s', faddr)
            elif seg.RST:
                self.warn('receiving RST in SYN_SENT')
                return
            elif seg.SYN:
                # TODO
                self.warn('Simultaneous SYN')
            else:
                # TODO: send a RST?
                self.warn('bad segment from %s', faddr)
            return

        if self._st == TCPS.SYN_RCVD:
            if self._check_seq(seg):
                # keep waiting, will retransmit or timeout
                pass
            elif seg.RST:
                self._rtq.clear()
                if self._st_p == TCPS.LISTEN:
                    self.warn('RST segment, back to LISTEN')
                    self._trans(TCPS.LISTEN)
                else:
                    # connection was initiated with an active open
                    self.warn('RST segment, closing')
                    self._close('RST from peer in SYN_RCVD')
            elif seg.SYN:
                self.error('bad SYN, closing')
                self._rtq.clear()
                self._send(None, self.snd_nxt, None, None, RST=1)
                self._close('bad SYN from peer in SYN_RCVD')
            elif seg.FIN:
                self.warn('FIN in SYN_RCVD')
                self._rtq.clear()
                self.rcv_nxt = seg.seq + 1
                self._send(None, self.snd_nxt, self.rcv_nxt, None)
                self._trans(TCPS.CLOSE_WAIT)
            elif not seg.ACK:
                # keep waiting
                self.warn('expecting ACK')
            elif seg.ack != self.iss + 1:
                self.error('bad ACK, closing')
                self._send(None, self.snd_nxt, None, None, RST=1)
                self._close('bad ACK from peer in SYN_RCVD')
            else:
                self._ack_received(seg)
                if seg.data:
                    self.warn('ACK with data')
                    self._inq.put(bytes(seg.data))
                    self.rcv_nxt = seg.seq + len(seg.data)
                self.log('Connected with %s', faddr)
                self._trans(TCPS.ESTABLISHED)
                # ready to be accept()
                self._listenq.put(self)
                del self._listenq
            return

        # check incoming segment

        if self._check_seq(seg):
            if not seg.RST:
                self._send(None, self.snd_nxt, self.rcv_nxt, None)
                return

        if seg.RST:
            self.warn('RST segment, closing')
            self._close('RST from peer')
            return

        if seg.SYN:
            self.error('SYN segment, closing')
            self._send(None, self.snd_nxt, None, None, RST=1)
            self._close('bad SYN from peer')
            return

        if seg.ACK:
            if seg.ack > self.snd_nxt:
                self.warn('invalid ACK, ack (%d) > %d', seg.ack, self.snd_nxt)
                return
            elif seg.ack < self.snd_una:
                self.warn('obsolete ACK')
                return
            else:
                self._ack_received(seg)
        elif self._st not in (TCPS.FIN_WAIT_1, TCPS.FIN_WAIT_2):
            self.warn('ACK is off')
            return

        # data transfer

        if self._st == TCPS.ESTABLISHED:
            if seg.seq == self.rcv_nxt:
                if seg.data:
                    self._inq.put(bytes(seg.data))
                self.rcv_nxt = seg.seq + seg.len
                # TODO: delayed ACK instead of immediate ACK
                self._send(None, self.snd_nxt, self.rcv_nxt, None)
                if seg.FIN:
                    # passive close
                    self._inq.put(None)
                    self._trans(TCPS.CLOSE_WAIT)
            else:
                self.warn('OO segment')
            return

        # passive close

        if self._st == TCPS.CLOSE_WAIT:
            if seg.FIN:
                log.warn('FIN_ACK lost, resent')
                self._send(None, self.snd_nxt, self.rcv_nxt, None)
            return

        if self._st == TCPS.LAST_ACK:
            self.log('Connection closed (passive)')
            self._close('Connection closed (passive)')
            return

        # active close

        if self._st == TCPS.FIN_WAIT_1:
            if seg.seq == self.rcv_nxt:
                if seg.data:
                    self.warn('RECV data in FIN_WAIT_1')
                    self._inq.put(bytes(seg.data))
                self.rcv_nxt = seg.seq + seg.len
                if seg.ACK:
                    if seg.FIN:
                        self._send(None, self.snd_nxt, self.rcv_nxt, None)
                        self._tw_tmo = time.time() + MSL * 2
                        self._trans(TCPS.TIME_WAIT)
                    else:
                        self._trans(TCPS.FIN_WAIT_2)
                elif seg.FIN:
                    self.warn('simultaneous FIN')
                    self._send(None, self.snd_nxt, self.rcv_nxt, None)
                    self._trans(TCPS.CLOSING)
            else:
                self.warn('OO segment')
            return

        if self._st == TCPS.FIN_WAIT_2:
            if seg.seq == self.rcv_nxt:
                if seg.data:
                    self.warn('RECV data in FIN_WAIT_2')
                    self._inq.put(bytes(seg.data))
                self.rcv_nxt = seg.seq + seg.len
                if seg.FIN:
                    self._send(None, self.snd_nxt, self.rcv_nxt, None)
                    self._tw_tmo = time.time() + MSL * 2
                    self._trans(TCPS.TIME_WAIT)
            else:
                self.warn('OO segment')
            return

        if self._st == TCPS.CLOSING:
            self._tw_tmo = time.time() + MSL * 2
            self._trans(TCPS.TIME_WAIT)
            return

        # time wait

        if self._st == TCPS.TIME_WAIT:
            if seg.FIN:
                self.warn('lost FIN_ACK, resent')
                self._send(None, self.snd_nxt, seg.seq + 1, None)
                self._tw_tmo = time.time() + MSL * 2
            else:
                self.warn('spurious segment in TIME_WAIT')
            return

        # shouldn't reach here
        self.error('***unhandled segment!')
        assert 0