Example #1
0
void handle_stream_packet(nabto_connect* con, nabto_packet_header* hdr,
                          uint8_t* start, uint16_t dlen,
                          uint8_t* payloadsStart, uint8_t* payloadsEnd,
                          void* userData) {
    
    // read the window and sack payloads
    struct unabto_payload_packet window;

    uint8_t* sackStart = 0;
    uint16_t sackLength = 0;
         
    NABTO_NOT_USED(userData);

    if(!unabto_find_payload(payloadsStart, payloadsEnd, NP_PAYLOAD_TYPE_WINDOW, &window)) {
        NABTO_LOG_ERROR(("Stream %i, Packet has no WINDOW payload!", hdr->tag));
        return;
    }

    {
        struct unabto_payload_packet sack;
        if (unabto_find_payload(payloadsStart, payloadsEnd, NP_PAYLOAD_TYPE_SACK, &sack)) {
            sackStart = (uint8_t*)sack.dataBegin;
            sackLength = sack.dataLength;
        }
    }

    NABTO_LOG_DEBUG(("(.%i.) STREAM EVENT, dlen: %i", hdr->nsi_sp, dlen));
    nabtoSetFutureStamp(&con->stamp, con->timeOut);
    nabto_stream_event(con, hdr, (uint8_t*)(window.dataBegin - SIZE_PAYLOAD_HEADER), start, dlen, sackStart, sackLength);
}
static void send_rendezvous_socket(nabto_socket_t socket, nabto_connect* con, uint16_t seq, nabto_endpoint* dest, nabto_endpoint *myAddress)
{
    uint8_t* ptr;
    uint8_t* buf = nabtoCommunicationBuffer;

    ptr = insert_header(buf, 0, con->spnsi, U_CONNECT, false, seq, 0, 0);
    ptr = insert_payload(ptr, NP_PAYLOAD_TYPE_EP, 0, 6);
    WRITE_U32(ptr, dest->addr); ptr += 4;
    WRITE_U16(ptr, dest->port); ptr += 2;
    if (seq > 0) {
        if (!myAddress) {
            NABTO_LOG_ERROR(("Send rendezvous called with an invalid address"));
            return;
        } else {
            ptr = insert_payload(ptr, NP_PAYLOAD_TYPE_EP, 0, 6);
            WRITE_U32(ptr, myAddress->addr); ptr += 4;
            WRITE_U16(ptr, myAddress->port); ptr += 2;
        }
    }
    {
        size_t len = ptr - buf;

        insert_length(buf, len);

        if (seq) {
            NABTO_LOG_DEBUG((PRInsi " RENDEZVOUS Send to " PRIep ": seq=%" PRIu16 "  " PRIep, MAKE_NSI_PRINTABLE(0, con->spnsi, 0), MAKE_EP_PRINTABLE(*dest), seq, MAKE_EP_PRINTABLE(*myAddress)));
        } else {
            NABTO_LOG_DEBUG((PRInsi " RENDEZVOUS Send to " PRIep ": seq=0", MAKE_NSI_PRINTABLE(0, con->spnsi, 0), MAKE_EP_PRINTABLE(*dest)));
        }
        if (dest->addr != 0 && dest->port != 0) {
            nabto_write(socket, buf, len, dest->addr, dest->port);
        } else {
            NABTO_LOG_TRACE(("invalid rendezvous packet thrown away"));
        }
    }
}
void nabto_release_connection_req(nabto_connect* con)
{
    NABTO_LOG_DEBUG((PRInsi " Release connection req", MAKE_NSI_PRINTABLE(0, con->spnsi, 0)));
    if (con->state != CS_CLOSE_REQUESTED) {
        uint32_t timeout = con->timeOut;
        if (timeout > MAX_TIMEOUT) {
            timeout = MAX_TIMEOUT;
        }
        con->state = CS_CLOSE_REQUESTED;
        nabtoSetFutureStamp(&con->stamp, timeout);
#if NABTO_ENABLE_STREAM
        nabto_stream_connection_closed(con);
#endif
        con->sendConnectionEndedStatistics = true;
    } else {
        NABTO_LOG_TRACE(("nabto_release_connection_req on closed connection"));
    }
}
Example #4
0
bool handle_debug_packet(message_event* event, nabto_packet_header* header) {
    
    uint8_t* buf = nabtoCommunicationBuffer;
    uint8_t* end = nabtoCommunicationBuffer+nabtoCommunicationBufferSize;
    struct unabto_payload_crypto crypto;

    buf += header->hlen;

    {
        struct unabto_payload_packet payload;
        if (!unabto_find_payload(buf, end, NP_PAYLOAD_TYPE_CRYPTO, &payload)) {
            NABTO_LOG_ERROR(("No crypto payload in debug packet."));
            return false;
        }

        if (!unabto_payload_read_crypto(&payload, &crypto)) {
            NABTO_LOG_ERROR(("Crypto packet too short."));
            return false;
        }
    }
    {
        uint16_t verifSize;
        if (!unabto_verify_integrity(nmc.context.cryptoConnect, crypto.code, nabtoCommunicationBuffer, header->len, &verifSize)) {
            NABTO_LOG_DEBUG(("U_DEBUG Integrity verification failed"));
            return false;
        }
    }

    {
        struct unabto_payload_packet payload;
        if (!unabto_find_payload(buf, end, NP_PAYLOAD_TYPE_SYSLOG_CONFIG, &payload)) {
            NABTO_LOG_ERROR(("No syslog config packet which is the only one understand at the moment."));
            return false;
        }
        
        if (!handle_syslog_config(&payload)) {
            return false;
        }
    }

    return true;
}
Example #5
0
void nabto_release_connection_req(nabto_connect* con)
{
    NABTO_LOG_DEBUG((PRInsi " Release connection req", MAKE_NSI_PRINTABLE(0, con->spnsi, 0)));
    if (con->state != CS_CLOSE_REQUESTED) {
        uint32_t timeout = con->timeOut;
        if (timeout > MAX_TIMEOUT) {
            timeout = MAX_TIMEOUT;
        }
        con->state = CS_CLOSE_REQUESTED;
        unabto_connection_set_future_stamp(&con->stamp, timeout);
#if NABTO_ENABLE_STREAM
        nabto_stream_connection_closed(con);
#endif
        con->sendConnectionEndedStatistics = true;
        // trigger recalculation of timeout such that statistics can be sent.
        connection_timeout_cache_cached = false;
    
    } else {
        NABTO_LOG_TRACE(("nabto_release_connection_req on closed connection"));
    }
}
void nabto_time_event_connection(void)
{
    nabto_connect* con;

    for (con = connections; con < connections + NABTO_CONNECTIONS_SIZE; ++con) {
        if (con->state != CS_IDLE) {
            rendezvous_time_event(con);
            statistics_time_event(con);
#if NABTO_ENABLE_TCP_FALLBACK
            unabto_tcp_fallback_event(con);
#endif

            if (nabto_connection_has_keep_alive(con) && nabtoIsStampPassed(&con->stamp)) {
                NABTO_LOG_DEBUG((PRInsi " Connection timeout", MAKE_NSI_PRINTABLE(0, con->spnsi, 0))); //, Stamp value is: %ul", con->spnsi, con->stamp));
                nabto_release_connection(con);
            }
        }
    }
#if NABTO_ENABLE_STREAM
    unabto_time_event_stream();
#endif
}
intptr_t tick_thread_func(void* args) {
    nabto_stamp_t ne;
    nabto_stamp_t shortTime;
    bool received = false;
        
    unabto_mutex_lock(&lock);
    unabto_next_event(&ne);
    unabto_mutex_unlock(&lock);

    nabtoSetFutureStamp(&shortTime, 10);

    while (running) {
        //struct epoll_event events[1];
        int timeout;
        nabto_stamp_t now;
        
        // updating next event is an expensive operation so only do it if 
        // the earlier update is expired.
        if (nabtoIsStampPassed(&ne) || (received && nabtoIsStampPassed(&shortTime))) {
            unabto_mutex_lock(&lock);
            unabto_time_event();
            unabto_next_event(&ne);
            unabto_mutex_unlock(&lock);
            nabtoSetFutureStamp(&shortTime, 10);
            received = false;
        }

        // the problem is that the timing is changed when packets is received.
        

        now = nabtoGetStamp();
        timeout = nabtoStampDiff2ms(nabtoStampDiff(&ne, &now));
        
        if (timeout < 0) {
            NABTO_LOG_DEBUG(("connection timestamp is in the past. setting timeout to 1ms, timeout=%i, this could be a problem.", timeout));
            timeout = 1;
        } else if (timeout == 0) {
            timeout = 1;
        }

        if (received) {
            if (timeout > 10) {
                timeout = 10;
            }
        } else {
            if (timeout > 100) {
                timeout = 100;
            }
        }
        
        nabto_socket_t readySockets[16];
        uint16_t nReady = nabto_read_events(readySockets, 16, timeout);
        uint16_t i;
        for (i = 0; i < nReady; i++) {
            unabto_mutex_lock(&lock);
            unabto_read_socket(readySockets[i]);
            unabto_mutex_unlock(&lock);
            received = true;
        }

        if (nReady == 0) {
            unabto_mutex_lock(&lock);
            unabto_time_event();
            unabto_mutex_unlock(&lock);
        }
    }
    return 0;
}
nabto_connect* nabto_init_connection(nabto_packet_header* hdr, uint32_t* nsi, uint32_t* ec, bool isLocal)
{
    uint8_t  type;
    uint8_t  flags;   /* NP_PAYLOAD_IPX_FLAG_* */
    nabto_connect* con;

    const uint8_t* end = nabtoCommunicationBuffer + hdr->len;
    uint8_t* ptr = nabtoCommunicationBuffer + hdr->hlen;
    uint16_t res;
    uint16_t ipxPayloadLength;
    
    ipxPayloadLength = nabto_rd_payload(ptr, end, &type); ptr += SIZE_PAYLOAD_HEADER;
    
    *nsi = 0;
    *ec  = 0;

    if ((ipxPayloadLength != IPX_PAYLOAD_LENGTH_WITHOUT_NSI) && 
        (ipxPayloadLength != IPX_PAYLOAD_LENGTH_WITH_NSI) && 
        (ipxPayloadLength != IPX_PAYLOAD_LENGTH_FULL_NSI)) {
        NABTO_LOG_TRACE(("Illegal payload size in U_CONNECT request from GSP: %" PRIu16 " %" PRIu8, ipxPayloadLength, type));
        return 0;
    }
    if (type != NP_PAYLOAD_TYPE_IPX) {
        NABTO_LOG_TRACE(("Illegal payload type in U_CONNECT request from GSP: %" PRIu16 " %" PRIu8, ipxPayloadLength, type));
        return 0;
    }
    
    if (ipxPayloadLength == IPX_PAYLOAD_LENGTH_WITH_NSI || 
        ipxPayloadLength == IPX_PAYLOAD_LENGTH_FULL_NSI) {
        READ_U32(*nsi, ptr + 13);
        NABTO_LOG_TRACE(("IPX payload with NSI (SPNSI=%" PRIu32 ")", *nsi));
    } else {
        *nsi = fresh_nsi();
        NABTO_LOG_TRACE(("IPX payload without NSI (fresh NSI=%" PRIu32 ")", *nsi));
    }

    if (*nsi == 0) {
        NABTO_LOG_ERROR(("Trying to create connection with spnsi == 0"));
        return 0;
    }

    if (nabto_find_connection(*nsi)) {
        NABTO_LOG_DEBUG((PRInsi " A connection already exists this is probably a retransmission", MAKE_NSI_PRINTABLE(0, *nsi, 0)));
        *ec = NOTIFY_CONNECT_OK;
        return 0;
    }

    con = nabto_init_connection_real(*nsi);

    if (con == 0) {
        if (nabto_find_connection(*nsi)) {
            NABTO_LOG_DEBUG((PRInsi " U_CONNECT: A connection resource is already pending new connection", MAKE_NSI_PRINTABLE(0, *nsi, 0)));
        } else {
            NABTO_LOG_INFO((PRInsi " U_CONNECT: No connection resources free for new connection", MAKE_NSI_PRINTABLE(0, *nsi, 0)));
#if NABTO_ENABLE_DEVICE_BUSY_AS_FATAL
            NABTO_LOG_FATAL((PRInsi " U_CONNECT: No free connections configured to be considered fatal", MAKE_NSI_PRINTABLE(0, *nsi, 0)));
#endif
        }
        *ec = NOTIFY_ERROR_BUSY_MICRO;
        return 0;
    }

    NABTO_LOG_DEBUG((PRInsi " U_CONNECT: Connecting using record %i", MAKE_NSI_PRINTABLE(0, *nsi, 0), nabto_connection_index(con)));
    READ_U32(con->cp.privateEndpoint.addr, ptr +  0);
    READ_U16(con->cp.privateEndpoint.port, ptr +  4);
    READ_U32(con->cp.globalEndpoint.addr, ptr +  6);
    READ_U16(con->cp.globalEndpoint.port, ptr + 10);
    READ_U8(flags,                ptr + 12); /* the final word (4 bytes) has been read already (nsi) */
    con->noRendezvous = (flags & NP_PAYLOAD_IPX_FLAG_NO_RENDEZVOUS) ? 1 : 0;
    con->cpEqual      = EP_EQUAL(con->cp.privateEndpoint, con->cp.globalEndpoint);
    con->cpAsync      = (flags & NP_PAYLOAD_IPX_FLAG_CP_ASYNC) ? 1 : 0;
    con->clientNatType      = (flags & NP_PAYLOAD_IPX_NAT_MASK);
    con->isLocal      = isLocal;
    NABTO_LOG_INFO((PRInsi " U_CONNECT: cp.private: " PRIep " cp.global: " PRIep ", noRdv=%" PRIu8 ", cpeq=%" PRIu8 ", asy=%" PRIu8 ", NATType: %" PRIu8 , MAKE_NSI_PRINTABLE(0, *nsi, 0), MAKE_EP_PRINTABLE(con->cp.privateEndpoint), MAKE_EP_PRINTABLE(con->cp.globalEndpoint), con->noRendezvous, con->cpEqual, con->cpAsync, con->clientNatType));

#if NABTO_ENABLE_TCP_FALLBACK
    if (ipxPayloadLength == IPX_PAYLOAD_LENGTH_FULL_NSI) {
        memcpy(con->consi, ptr+17, 8);
        con->nsico = con->consi;
        READ_U32(con->cpnsi, ptr+25);
    }
#endif

    ptr += ipxPayloadLength;  //IPX_PAYLOAD_LENGTH_WITHOUT_NSI or IPX_PAYLOAD_LENGTH_WITH_NSI
        
    con->clientId[0]  = 0;
    res = nabto_rd_payload(ptr, end, &type);
    if (type == NP_PAYLOAD_TYPE_CP_ID) {
        uint8_t idType;
        ptr += SIZE_PAYLOAD_HEADER;
        if (res > 0) {
            READ_U8(idType, ptr); ++ptr; --res;
            if (idType == 1) { // 1 == EMAIL
                size_t sz = res;
                if (sz >= sizeof(con->clientId)) {
                    if (sizeof(con->clientId) > 1) {
                        NABTO_LOG_WARN(("Client ID truncated"));
                    }
                    sz = sizeof(con->clientId) - 1;
                }
                if (sz) {
                    memcpy(con->clientId, (const void*) ptr, sz);
                }
                con->clientId[sz] = 0;
            }
        }
        NABTO_LOG_TRACE(("Connection opened from '%s' (to %s)", con->clientId, nmc.nabtoMainSetup.id));
        ptr += res;
    }
#if NABTO_ENABLE_CONNECTION_ESTABLISHMENT_ACL_CHECK
    if (!allow_client_access(con)) {
        *ec = NOTIFY_ERROR_CP_ACCESS;
        goto init_error;
    }
#endif

#if NABTO_ENABLE_TCP_FALLBACK
    {
        uint8_t* gatewayPtr = ptr;
        res = nabto_rd_payload(ptr, end, &type);
        if (type == NP_PAYLOAD_TYPE_GW && ipxPayloadLength == IPX_PAYLOAD_LENGTH_FULL_NSI) {
            uint8_t* gatewayEndPtr;
            size_t idLength;
            NABTO_LOG_TRACE(("The connect contains a gateway payload."));
            ptr += res + SIZE_PAYLOAD_HEADER;
            gatewayPtr += SIZE_PAYLOAD_HEADER;
            gatewayEndPtr = ptr;

            READ_U32(con->fallbackHost.addr, gatewayPtr); gatewayPtr+=4;
            READ_U16(con->fallbackHost.port, gatewayPtr); gatewayPtr+=2;
            // skip the nsi
            gatewayPtr+=4;
        
            idLength = gatewayEndPtr-gatewayPtr;
            if (idLength != 20) {
                NABTO_LOG_FATAL(("The id length should be 20 bytes. bytes=%" PRIsize, idLength));
                // todo
            } 
            memcpy(con->gatewayId, gatewayPtr, 20);
            con->hasTcpFallbackCapabilities = true;
        }
    }
#endif


#if NABTO_ENABLE_UCRYPTO
    if (nmc.context.nonceSize == NONCE_SIZE) {
        uint8_t* decryptedDataStart;
        uint16_t decryptedDataLength;
        if (!unabto_connection_verify_and_decrypt_connect_packet(hdr, &decryptedDataStart, &decryptedDataLength)) {
            NABTO_LOG_TRACE(("Failed to read crypto payload in U_CONNECT"));
        } else {
            unabto_crypto_reinit_d(&con->cryptoctx, nmc.nabtoMainSetup.cryptoSuite, decryptedDataStart, decryptedDataLength);
        }
    } else
#endif
    {
        NABTO_LOG_TRACE(("########    U_CONNECT without crypto payload"));
        unabto_crypto_reinit_d(&con->cryptoctx, CRYPT_W_NULL_DATA, 0, 0);
    }


    con->timeOut = CONNECTION_TIMEOUT;
    nabtoSetFutureStamp(&con->stamp, 20000); /* give much extra time during initialisation */

    if (!verify_connection_encryption(con)) {
        goto init_crypto_error;
    }

    if (con->cpEqual) {
        NABTO_LOG_DEBUG((PRInsi " U_CONNECT: addr:" PRIep " rendezvous:%" PRIu8, MAKE_NSI_PRINTABLE(0, *nsi, 0), MAKE_EP_PRINTABLE(con->cp.privateEndpoint), !con->noRendezvous));
    } else {
        NABTO_LOG_DEBUG((PRInsi " U_CONNECT: private:" PRIep ", global:" PRIep " rendezvous:%" PRIu8, MAKE_NSI_PRINTABLE(0, *nsi, 0), MAKE_EP_PRINTABLE(con->cp.privateEndpoint), MAKE_EP_PRINTABLE(con->cp.globalEndpoint), !con->noRendezvous));
    }

    NABTO_LOG_INFO(("Connection opened from '%s' (to %s). Encryption code %i", con->clientId, nmc.nabtoMainSetup.id, con->cryptoctx.code));

    return con;

init_crypto_error:
    *ec = NOTIFY_ERROR_ENCR_MISMATCH;

#if NABTO_ENABLE_CONNECTION_ESTABLISHMENT_ACL_CHECK
init_error:
    nabto_release_connection(con);
#endif

    return 0;
}
Example #9
0
/**
 * Close a socket.
 * Close can be called on already closed sockets. And should tolerate this behavior.
 *
 * @param socket the socket to be closed
 */
void nabto_close_socket(nabto_socket_t* socket)
{
    NABTO_LOG_DEBUG(("nabto_close_socket %u", *socket));
    sockets[*socket].isOpen = false;
}
Example #10
0
void nabto_stream_event(nabto_connect*       con,
                        nabto_packet_header* hdr,
                        uint8_t*             info, //WINDOW payload with payload header
                        uint8_t*             start,
                        int                  dlen,
                        uint8_t*             sackStart,
                        uint16_t             sackLength)
{
    struct nabto_win_info  win;
    struct nabto_stream_s* stream;
    struct nabto_stream_sack_data sackData;
    uint16_t len;

    // We must have a WINDOW payload to continue.
    if (!info) {
        NABTO_LOG_ERROR(("Stream %i, Packet has no WINDOW payload!", hdr->tag));
        return;
    }
    
    READ_U16(len, info + 2);
    if (!nabto_stream_read_window(info + SIZE_PAYLOAD_HEADER, len - SIZE_PAYLOAD_HEADER, &win)) {
        NABTO_LOG_DEBUG(("ReadWin failure"));
        return;
    }

    {
        text msg;
        switch (win.type) {
        case NP_PAYLOAD_WINDOW_FLAG_SYN                             : msg = "SYN";     break;
        case NP_PAYLOAD_WINDOW_FLAG_SYN | NP_PAYLOAD_WINDOW_FLAG_ACK: msg = "SYN|ACK"; break;
        case NP_PAYLOAD_WINDOW_FLAG_FIN | NP_PAYLOAD_WINDOW_FLAG_ACK: msg = "FIN|ACK"; break;
        case NP_PAYLOAD_WINDOW_FLAG_RST                             : msg = "RST";     break;
        case NP_PAYLOAD_WINDOW_FLAG_ACK                             : msg = "DATA";    break;
        default       : msg = "?"; NABTO_LOG_TRACE(("Type?: %" PRIu8, win.type)); break;

        }
        NABTO_NOT_USED(msg);
        NABTO_LOG_DEBUG(("%" PRIu16 " --> [%" PRIu32 ",%" PRIu32 "] %" PRItext ", %d bytes", hdr->tag, win.seq, win.ack, msg, dlen));
    }

    stream = find_stream(hdr->tag, con);
    if (stream == NULL) {
        if (win.type == NP_PAYLOAD_WINDOW_FLAG_SYN) {
            stream = find_free_stream(hdr->tag, con);
            if (stream == NULL) {
                NABTO_LOG_DEBUG(("Stream with tag %i not accepted", hdr->tag));
            }
        } else {
            NABTO_LOG_DEBUG(("Received non syn packet for stream which is not available tag %i", hdr->tag));
        }
    }

    if (stream == NULL) {
        if (! (win.type & NP_PAYLOAD_WINDOW_FLAG_RST)) {
            build_and_send_rst_packet(con, hdr->tag, &win);
        }
        return;
    }
    
    if (!nabto_stream_validate_win(&win, stream)) {
        NABTO_LOG_ERROR(("Cannot validate received stream window."));
        return;
    }

    NABTO_LOG_TRACE(("(.%i.) Stream with tag %i accepted, slot=%i", con->spnsi, hdr->tag, unabto_stream_index(stream)));

    stream->stats.receivedPackets++;
 
    memset(&sackData, 0, sizeof(sackData));
    {
        uint8_t* ptr = sackStart;
        while(sackLength >= 8 && sackData.nPairs < NP_PAYLOAD_SACK_MAX_PAIRS) {
            uint32_t sackSeqStart; // start of sack 
            uint32_t sackSeqEnd; // end of sack one larger than actual acked window.
            READ_FORWARD_U32(sackSeqStart, ptr);
            READ_FORWARD_U32(sackSeqEnd, ptr);
            sackLength -= 8;
            
            sackData.pairs[sackData.nPairs].start = sackSeqStart;
            sackData.pairs[sackData.nPairs].end = sackSeqEnd;
            sackData.nPairs++;
        }
    }

    nabto_stream_tcb_event(stream, &win, start, dlen, &sackData);
}