Пример #1
0
def awaiting_release_handler(circuit: NetRomCircuit, event: NetRomStateEvent,
                             netrom: NetRom,
                             logger: LoggingMixin) -> NetRomStateType:
    assert circuit.state == NetRomStateType.AwaitingRelease

    if event.event_type == NetRomEventType.NETROM_DISCONNECT_ACK:
        if event.packet.circuit_idx == circuit.circuit_idx and event.packet.circuit_id == circuit.circuit_id:
            netrom.nl_disconnect_indication(circuit.circuit_idx,
                                            circuit.circuit_id,
                                            circuit.remote_call,
                                            circuit.local_call)
            return NetRomStateType.Disconnected
        else:
            logger.debug("Invalid disconnect ack. Disconnecting anyways")
            return NetRomStateType.Disconnected
    elif event.event_type == NetRomEventType.NETROM_DISCONNECT:
        disc_ack = NetRomPacket(
            event.packet.source,
            event.packet.dest,
            7,  # TODO configure TTL
            event.packet.circuit_idx,
            event.packet.circuit_id,
            0,  # Our circuit idx
            0,  # Our circuit id
            OpType.DisconnectAcknowledge.as_op_byte(False, False, False))
        netrom.write_packet(disc_ack)
        netrom.nl_disconnect_indication(circuit.circuit_idx,
                                        circuit.circuit_id,
                                        circuit.remote_call,
                                        circuit.local_call)
        circuit.timer.cancel()
        return NetRomStateType.Disconnected
    else:
        # TODO handle any other cases differently?
        return NetRomStateType.AwaitingRelease
Пример #2
0
def awaiting_connection_handler(circuit: NetRomCircuit,
                                event: NetRomStateEvent, netrom: NetRom,
                                logger: LoggingMixin) -> NetRomStateType:
    assert circuit.state == NetRomStateType.AwaitingConnection

    if event.event_type == NetRomEventType.NETROM_CONNECT:
        return NetRomStateType.AwaitingConnection
    elif event.event_type == NetRomEventType.NETROM_CONNECT_ACK:
        ack = cast(NetRomConnectAck, event.packet)
        if ack.circuit_idx == circuit.circuit_idx and ack.circuit_id == circuit.circuit_id:
            circuit.remote_circuit_idx = ack.tx_seq_num
            circuit.remote_circuit_id = ack.rx_seq_num
            circuit.window_size = ack.accept_window_size
            netrom.nl_connect_indication(
                circuit.circuit_idx, circuit.circuit_id, circuit.remote_call,
                circuit.local_call, circuit.origin_node, circuit.origin_user)
            circuit.timer.reset()
            return NetRomStateType.Connected
        else:
            logger.debug("Unexpected circuit id in connection ack")
            return NetRomStateType.AwaitingConnection
    elif event.event_type in (NetRomEventType.NETROM_DISCONNECT,
                              NetRomEventType.NETROM_DISCONNECT_ACK,
                              NetRomEventType.NETROM_INFO,
                              NetRomEventType.NETROM_INFO_ACK):
        return NetRomStateType.AwaitingConnection
    elif event.event_type == NetRomEventType.NL_CONNECT:
        conn = NetRomConnectRequest(
            circuit.remote_call,
            circuit.local_call,
            7,  # TODO configure TTL
            circuit.circuit_idx,
            circuit.circuit_id,
            0,  # Unused
            0,  # Unused
            OpType.ConnectRequest.as_op_byte(False, False, False),
            2,  # TODO get this from config
            circuit.origin_user,  # Origin user
            circuit.origin_node  # Origin node
        )
        netrom.write_packet(conn)
        circuit.timer.reset()
        return NetRomStateType.AwaitingConnection
    elif event.event_type in (NetRomEventType.NL_DISCONNECT,
                              NetRomEventType.NL_DATA):
        return NetRomStateType.AwaitingConnection
Пример #3
0
def connected_handler(circuit: NetRomCircuit, event: NetRomStateEvent,
                      netrom: NetRom, logger: LoggingMixin) -> NetRomStateType:
    assert circuit.state == NetRomStateType.Connected

    if event.event_type == NetRomEventType.NETROM_CONNECT:
        connect_req = cast(NetRomConnectRequest, event.packet)
        if connect_req.circuit_idx == circuit.circuit_idx and connect_req.circuit_id == circuit.circuit_id:
            # Treat this as a reconnect and ack it
            connect_ack = NetRomConnectAck(
                connect_req.source,
                connect_req.dest,
                7,  # TODO get TTL from config
                connect_req.circuit_idx,
                connect_req.circuit_id,
                circuit.circuit_idx,
                circuit.circuit_id,
                OpType.ConnectAcknowledge.as_op_byte(False, False, False),
                connect_req.proposed_window_size)
            netrom.write_packet(connect_ack)
            netrom.nl_connect_indication(
                circuit.circuit_idx, circuit.circuit_id, circuit.remote_call,
                circuit.local_call, circuit.origin_node, circuit.origin_user)
            circuit.timer.reset()
            return NetRomStateType.Connected
        else:
            # Reject this and disconnect
            logger.debug(
                "Rejecting connect request due to invalid circuit ID/IDX")
            connect_rej = NetRomConnectAck(
                connect_req.source,
                connect_req.dest,
                7,  # TODO get TTL from config
                connect_req.circuit_idx,
                connect_req.circuit_id,
                circuit.circuit_idx,
                circuit.circuit_id,
                OpType.ConnectAcknowledge.as_op_byte(True, False, False),
                connect_req.proposed_window_size)
            netrom.write_packet(connect_rej)
            netrom.nl_disconnect_indication(circuit.circuit_idx,
                                            circuit.circuit_id,
                                            circuit.remote_call,
                                            circuit.local_call)
            circuit.timer.cancel()
            return NetRomStateType.Disconnected
    elif event.event_type == NetRomEventType.NETROM_CONNECT_ACK:
        connect_ack = cast(NetRomConnectAck, event.packet)
        if connect_ack.tx_seq_num == circuit.remote_circuit_idx and \
                connect_ack.rx_seq_num == circuit.remote_circuit_id and \
                connect_ack.circuit_idx == circuit.circuit_idx and \
                connect_ack.circuit_id == circuit.circuit_id:
            netrom.nl_connect_indication(
                circuit.circuit_idx, circuit.circuit_id, circuit.remote_call,
                circuit.local_call, circuit.origin_node, circuit.origin_user)
            circuit.timer.reset()
            return NetRomStateType.Connected
        else:
            #  TODO what now?
            return NetRomStateType.Connected
    elif event.event_type == NetRomEventType.NETROM_DISCONNECT:
        disc_ack = NetRomPacket(
            event.packet.source,
            event.packet.dest,
            7,  # TODO configure TTL
            circuit.remote_circuit_id,
            circuit.remote_circuit_id,
            0,  # Not used
            0,  # Not used
            OpType.DisconnectAcknowledge.as_op_byte(False, False, False))
        netrom.write_packet(disc_ack)
        netrom.nl_disconnect_indication(circuit.circuit_idx,
                                        circuit.circuit_id,
                                        circuit.remote_call,
                                        circuit.local_call)
        circuit.timer.cancel()
        return NetRomStateType.Disconnected
    elif event.event_type == NetRomEventType.NETROM_DISCONNECT_ACK:
        logger.debug("Unexpected disconnect ack in connected state!")
        return NetRomStateType.Disconnected
    elif event.event_type == NetRomEventType.NETROM_INFO:
        """
        The TX number from the INFO packet is the current sequence number while the the RX number is the next
        expected sequence number on the other end of the circuit. This serves as a mechanism to acknowledge
        previous INFO without sending an explicit ACK
        """
        info = cast(NetRomInfo, event.packet)
        if info.tx_seq_num == circuit.recv_state():
            # We got the message number we expected
            circuit.inc_recv_state()
            circuit.enqueue_info_ack(netrom)
            circuit.more += info.info
            if not info.more_follows():
                # TODO expire old more-follows data
                netrom.nl_data_indication(circuit.circuit_idx,
                                          circuit.circuit_id,
                                          circuit.remote_call,
                                          circuit.local_call, circuit.more)
                circuit.more = bytearray()
        elif info.tx_seq_num < circuit.recv_state():
            # Possible retransmission of previous message, ignore?
            pass
        else:
            # Got a higher number than expected, we missed something, ask the sender to rewind
            # to our last confirmed state
            nak = NetRomPacket(
                info.source,
                info.dest,
                7,  # TODO config
                circuit.remote_circuit_idx,
                circuit.remote_circuit_id,
                0,  # Unused
                circuit.recv_state(),
                OpType.InformationAcknowledge.as_op_byte(False, True, False))
            netrom.write_packet(nak)

        # Handle the ack logic
        if info.rx_seq_num > circuit.hw:
            for seq in range(info.rx_seq_num, circuit.hw + 1):
                seq = (seq + 127) % 128
                circuit.info_futures[seq].set_result(True)
            circuit.hw = info.rx_seq_num
        else:
            # Out of sync, error
            pass

        if info.rx_seq_num == circuit.send_state():
            # We are in-sync, all is well
            pass
        elif info.rx_seq_num < circuit.send_state():
            # Other side is lagging
            pass
        else:
            # Other side has ack'd something out of range, raise an error
            pass

        # Handle the other flags
        if info.choke():
            # TODO stop sending until further notice
            pass
        if info.nak():
            seq_resend = event.packet.rx_seq_num
            logger.warning(f"Got Info NAK, rewinding to {seq_resend}")
            while seq_resend < circuit.send_state():
                info_to_resend = circuit.sent_info[seq_resend]
                info_to_resend.rx_seq_num = circuit.recv_state()
                netrom.write_packet(info_to_resend)
                seq_resend += 1

        return NetRomStateType.Connected
    elif event.event_type == NetRomEventType.NETROM_INFO_ACK:
        """
        If the choke flag is set (bit 7 of the opcode byte), it indicates that this node cannot accept any more 
        information messages until further notice. If the NAK flag is set (bit 6 of the opcode byte), it indicates that 
        a selective retransmission of the frame identified by the Rx Sequence Number is being requested.
        """
        ack = event.packet

        if ack.rx_seq_num > circuit.hw:
            for seq in range(ack.rx_seq_num, circuit.hw + 1):
                seq = (seq + 127) % 128
                fut = circuit.info_futures.get(seq)
                if fut:
                    fut.set_result(True)
            circuit.hw = ack.rx_seq_num
        else:
            # Out of sync, error
            pass

        if ack.rx_seq_num == circuit.send_state():
            seq = (ack.rx_seq_num + 127) % 128
            fut = circuit.info_futures.get(seq)
            if fut:
                fut.set_result(True)
        elif ack.rx_seq_num < circuit.send_state():
            # Lagging behind
            pass
        else:
            # Invalid state, error
            pass

        if ack.choke():
            logger.warning("Got Info Choke")
            # TODO stop sending until further notice
            pass
        if ack.nak():
            seq_resend = event.packet.rx_seq_num
            logger.warning(f"Got Info NAK, rewinding to {seq_resend}")
            while seq_resend < circuit.send_state():
                info_to_resend = circuit.sent_info[seq_resend]
                info_to_resend.rx_seq_num = circuit.recv_state()
                netrom.write_packet(info_to_resend)
                seq_resend += 1
        elif event.packet.rx_seq_num != circuit.send_state():
            logger.warning("Info sync error")
            # Out of sync, what now? Update circuit send state?
            pass
        return NetRomStateType.Connected
    elif event.event_type == NetRomEventType.NL_CONNECT:
        conn = NetRomConnectRequest(
            circuit.remote_call,
            circuit.local_call,
            7,  # TODO configure TTL
            circuit.circuit_idx,
            circuit.circuit_id,
            0,  # Unused
            0,  # Unused
            OpType.ConnectRequest.as_op_byte(False, False, False),
            2,  # TODO get this from config
            circuit.origin_user,  # Origin user
            circuit.origin_node,  # Origin node
        )
        netrom.write_packet(conn)
        circuit.timer.reset()
        return NetRomStateType.AwaitingConnection
    elif event.event_type == NetRomEventType.NL_DISCONNECT:
        disc = NetRomPacket(
            circuit.remote_call,
            circuit.local_call,
            7,  # TODO configure TTL
            circuit.remote_circuit_idx,
            circuit.remote_circuit_id,
            0,
            0,
            OpType.DisconnectRequest.as_op_byte(False, False, False))
        netrom.write_packet(disc)
        circuit.timer.cancel()
        return NetRomStateType.AwaitingRelease
    elif event.event_type == NetRomEventType.NL_DATA:
        if event.mtu > 0:
            fragments = list(chunks(event.data, event.mtu))
            for fragment in fragments[:-1]:
                info = NetRomInfo(
                    circuit.remote_call,
                    circuit.local_call,
                    7,  # TODO
                    circuit.remote_circuit_idx,
                    circuit.remote_circuit_id,
                    circuit.send_state(),
                    circuit.recv_state(),
                    OpType.Information.as_op_byte(False, False, True),
                    fragment)
                netrom.write_packet(info)
                circuit.sent_info[info.tx_seq_num] = info
                circuit.info_futures[info.tx_seq_num] = event.future
                circuit.inc_send_state()
            last = fragments[-1]
        else:
            last = event.data
        info = NetRomInfo(
            circuit.remote_call,
            circuit.local_call,
            7,  # TODO
            circuit.remote_circuit_idx,
            circuit.remote_circuit_id,
            circuit.send_state(),
            circuit.recv_state(),
            OpType.Information.as_op_byte(False, False, False),
            last)
        netrom.write_packet(info)
        circuit.sent_info[info.tx_seq_num] = info
        circuit.info_futures[info.tx_seq_num] = event.future
        circuit.inc_send_state()

        async def check_ack():
            # TODO need to implement timeout and retry
            await asyncio.sleep(10.000)
            if circuit.hw <= info.tx_seq_num:
                # Retransmit
                print(f"Retransmit {info}")

        #asyncio.create_task(check_ack())

        return NetRomStateType.Connected
Пример #4
0
def disconnected_handler(circuit: NetRomCircuit, event: NetRomStateEvent,
                         netrom: NetRom,
                         logger: LoggingMixin) -> NetRomStateType:
    assert circuit.state == NetRomStateType.Disconnected

    if event.event_type == NetRomEventType.NETROM_CONNECT:
        connect_req = cast(NetRomConnectRequest, event.packet)
        connect_ack = NetRomConnectAck(
            connect_req.source,
            connect_req.dest,
            7,  # TODO get TTL from config
            connect_req.circuit_idx,
            connect_req.circuit_id,
            circuit.circuit_idx,
            circuit.circuit_id,
            OpType.ConnectAcknowledge.as_op_byte(False, False, False),
            connect_req.proposed_window_size)
        circuit.remote_circuit_id = connect_req.circuit_id
        circuit.remote_circuit_idx = connect_req.circuit_idx
        if netrom.write_packet(connect_ack):
            netrom.nl_connect_indication(
                circuit.circuit_idx, circuit.circuit_id, circuit.remote_call,
                circuit.local_call, circuit.origin_node, circuit.origin_user)
            circuit.timer.reset()
            return NetRomStateType.Connected
        else:
            return NetRomStateType.Disconnected
    elif event.event_type in (NetRomEventType.NETROM_CONNECT_ACK,
                              NetRomEventType.NETROM_INFO,
                              NetRomEventType.NETROM_INFO_ACK):
        # If we're disconnected, we don't have the remote circuit's ID/IDX, so we can't really do
        # much here besides try to re-connect
        logger.debug(
            f"Got unexpected packet {event.packet}. Attempting to reconnect")
        disc = NetRomPacket(
            circuit.remote_call,
            circuit.local_call,
            7,  # TODO configure TTL
            circuit.remote_circuit_idx,
            circuit.remote_circuit_id,
            0,
            0,
            OpType.DisconnectRequest.as_op_byte(False, False, False))
        if netrom.write_packet(disc):
            return NetRomStateType.AwaitingRelease
        else:
            return NetRomStateType.Disconnected
    elif event.event_type == NetRomEventType.NETROM_DISCONNECT_ACK:
        # We are already disconnected, nothing to do here
        return NetRomStateType.Disconnected
    elif event.event_type == NetRomEventType.NETROM_DISCONNECT:
        # Ack this even though we're not connected
        disc_ack = NetRomPacket(
            event.packet.source,
            event.packet.dest,
            7,  # TODO configure TTL
            0,  # Don't know the remote circuit idx
            0,  # Don't know the remote circuit id
            0,  # Our circuit idx
            0,  # Our circuit id
            OpType.DisconnectAcknowledge.as_op_byte(False, False, False))
        netrom.write_packet(disc_ack)
        netrom.nl_disconnect_indication(circuit.circuit_idx,
                                        circuit.circuit_id,
                                        circuit.remote_call,
                                        circuit.local_call)
        circuit.timer.cancel()
        return NetRomStateType.Disconnected
    elif event.event_type == NetRomEventType.NL_CONNECT:
        conn = NetRomConnectRequest(
            circuit.remote_call,
            circuit.local_call,
            7,  # TODO configure TTL
            circuit.circuit_idx,
            circuit.circuit_id,
            0,  # Send no circuit idx
            0,  # Send no circuit id
            OpType.ConnectRequest.as_op_byte(False, False, False),
            2,  # Proposed window size (TODO get this from config)
            circuit.origin_user,  # Origin user
            circuit.origin_node,  # Origin node
        )
        if netrom.write_packet(conn):
            circuit.timer.start()
            return NetRomStateType.AwaitingConnection
        else:
            return NetRomStateType.Disconnected
    elif event.event_type == NetRomEventType.NL_DISCONNECT:
        return NetRomStateType.Disconnected
    elif event.event_type == NetRomEventType.NL_DATA:
        logger.debug("Ignoring unexpected NL_DATA event in disconnected state")
        return NetRomStateType.Disconnected