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")); } }
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; }
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; }
/** * 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; }
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); }