/** Enumerates all entries in queue in order (from first to last item). * Returns false immediately iff proc returns false. * Returns true if every call to proc returned true. */ bool queue_enum(queue_enum_proc proc, void *data) { #if NABTO_APPREQ_QUEUE_SIZE > 1 queue_entry* entry; if (queueLastAdd) { //At least one record in queue. //queue may be full. queue_first_used == queue_next_free means full. UNABTO_ASSERT(!queue_empty()); UNABTO_ASSERT(queue_first_used->state != APPREQ_FREE); entry = queue_first_used; do { // If queue->state == APPREQ_FREE, we have an empty (already answered) slot // in the queue. Don't use (requests are inserted in strict order to start // treatment in the same order). if (entry->state != APPREQ_FREE) { // Let caller determine whether to proceed. if (!(*proc)(&entry->data, data)) { return false; } } queue_inc(entry); } while (entry != queue_next_free); } else { //queue may be empty. queue_first_used == queue_next_free means empty. UNABTO_ASSERT(!queue_full()); entry = queue_first_used; while (entry != queue_next_free) { // If queue->state == APPREQ_FREE, we have an empty (already answered) slot // in the queue. Don't use (requests are inserted in strict order to start // treatment in the same order). if (entry->state != APPREQ_FREE) { // Let caller determine whether to proceed. if (!(*proc)(&entry->data, data)) { return false; } } queue_inc(entry); } } #else //NABTO_APPREQ_QUEUE_SIZE == 1 if (!queue_empty()) { // Let caller determine whether to proceed. if (!(*proc)(&queue[0].data, data)) { return false; } } #endif return true; }
void unabto_tcp_fallback_handle_write(nabto_connect* con) { ssize_t status; int dataToSend; unabto_tcp_fallback_connection* fbConn = &fbConns[nabto_connection_index(con)]; UNABTO_ASSERT(fbConn->sendBufferSent <= fbConn->sendBufferLength); dataToSend = fbConn->sendBufferLength - fbConn->sendBufferSent; status = send(fbConn->socket, fbConn->sendBuffer + fbConn->sendBufferSent, dataToSend, MSG_NOSIGNAL); if (status > 0) { fbConn->sendBufferSent += status; } else if (status < 0) { int err = errno; if ((err == EAGAIN) || err == EWOULDBLOCK) { } else { NABTO_LOG_ERROR((PRI_tcp_fb "Send of tcp packet failed", TCP_FB_ARGS(con))); close_tcp_socket(con); return; } } if (fbConn->sendBufferSent > fbConn->sendBufferLength) { NABTO_LOG_FATAL(("fbConn->sendBufferSent(%" PRIsize ") > fbConn->sendBufferLength(%" PRIsize "), that should not be possible", fbConn->sendBufferSent, fbConn->sendBufferLength)); } if (fbConn->sendBufferSent == fbConn->sendBufferLength) { fbConn->sendBufferLength = 0; } }
uint8_t* insert_connection_stats_payload(uint8_t* ptr, uint8_t* end, nabto_connect* con) { nabto_stamp_t now; uint32_t connectionAge; UNABTO_ASSERT(ptr <= end); if (end-ptr < NP_PAYLOAD_CONNECTION_STATS_BYTELENGTH) { return NULL; } ptr = insert_payload(ptr, NP_PAYLOAD_TYPE_CONNECTION_STATS, 0, 21); WRITE_FORWARD_U8(ptr, NP_PAYLOAD_CONNECTION_STATS_VERSION); now = nabtoGetStamp(); connectionAge = nabtoStampDiff2ms(nabtoStampDiff(&now, &con->stats.connectionStart)); WRITE_FORWARD_U32(ptr, connectionAge); WRITE_FORWARD_U32(ptr, con->stats.packetsReceived); WRITE_FORWARD_U32(ptr, con->stats.packetsSent); WRITE_FORWARD_U32(ptr, con->stats.bytesReceived); WRITE_FORWARD_U32(ptr, con->stats.bytesSent); return ptr; }
/* Release the event handle returned by framework_event_query. * In the ASYNC model we must remove the event handle from the FIFO request * queue. */ void framework_release_handle(naf_handle handle) { queue_entry* entry; if (!handle) { return; } if (queue_empty()) { /* Why do the application try to release a handle on an empty queue? */ NABTO_LOG_FATAL(("SW error: Calling framework_release_handle on an empty queue")); return; } /* Find the entry containing the handle */ entry = queue_find_entry(handle); /* The given handle must belong to the queue */ UNABTO_ASSERT(entry); entry->state = APPREQ_FREE; LOG_APPREQ_WHERE("framework_release_handle", entry); /* Remove top entry from FIFO queue - and remove all consecutive * entries that have expired/finished in the mean time. */ while (!queue_empty()) { if (queue_top()->state != APPREQ_FREE) break; queue_pop(); } LOG_APPREQ_QUEUE(); }
/* Ask for an event handle corresponding to the given client request (in * ASYNC mode). The event handle must be released again using the * framework_release_handle function. * The return value is NAF_QUERY_NEW if this is the first time the client * request is seen (i.e. a new event handle is created). In this case * the buffer pointed to by handle will be assigned the new event handle. * Note that this handle will never be 0 (to distinguish the case when * calling this function in the SYNC model). * The return value is NAF_QUERY_QUEUED if the client request is already * known (and is pending to be processed). In this case the buffer pointed * to by handle is left unctouched. * The return value is NAF_QUERY_OUT_OF_RESOURCES if no more memory is * available for a new client request. The buffer pointed to by handle is * left unchanged. */ naf_query framework_event_query(const char* clientId, uint16_t reqId, naf_handle* handle) { client_req_query query; queue_entry* entry; NABTO_LOG_TRACE(("APPREQ framework_event_query: client=" PRI_client_id2, CLIENT_ID_ARGS2(clientId, reqId))); /* First look for the request in the queue of pending requests. */ query.clientId = clientId; query.reqId = reqId; query.found = NULL; queue_enum(is_client_request_cb, &query); if (query.found) { //The client request was found in the queue LOG_APPREQ_STATE("framework_event_query", "QUEUED", query.found); LOG_APPREQ_QUEUE(); return NAF_QUERY_QUEUED; } /* Now the request was not found. * Reserve the next free entry in the queue. */ entry = queue_alloc(); if (!entry) { //The queue is full LOG_APPREQ_ERROR("framework_event_query", "OUT_OF_RESOURCES"); LOG_APPREQ_QUEUE(); return NAF_QUERY_OUT_OF_RESOURCES; } UNABTO_ASSERT(entry->state == APPREQ_FREE); if (entry->state != APPREQ_FREE) { //Hmmm - that's strange!. The new entry should have been free. if (clientId == entry->data.applicationRequest.clientId && reqId == entry->data.header.seq) { // - and it seems to be ourself!! LOG_APPREQ_STATE("framework_event_query", "QUEUED?", entry); LOG_APPREQ_QUEUE(); return NAF_QUERY_QUEUED; } // The new entry belongs to someone else. The queue must be full !? LOG_APPREQ_ERROR("framework_event_query", "OUT_OF_RESOURCES?"); LOG_APPREQ_QUEUE(); return NAF_QUERY_OUT_OF_RESOURCES; } // Now we have a new request // Be sure we "own" the entry - advance to next free entry in the queue entry->state = APPREQ_WAITING; queue_push(); // *handle cannot be initialized yet, as the received packet has not // yet been decrypted. // See framework_event() for initialization of handle. *handle = &entry->data; LOG_APPREQ_STATE("framework_event_query", "NEW", entry); LOG_APPREQ_QUEUE(); return NAF_QUERY_NEW; }
application_event_result application_poll(application_request* request, buffer_read_t* r_b, buffer_write_t* w_b) { application_event_result res = AER_REQ_SYSTEM_ERROR; if (saved_app_req == 0) { NABTO_LOG_FATAL(("No queued request")); } else if (request != saved_app_req) { NABTO_LOG_FATAL(("queued request and parameters doesn't match")); } else { UNABTO_ASSERT(r_b == 0); r_b = &saved_params.r_b; UNABTO_ASSERT(r_b != 0); res = weather_station_application(saved_app_req, r_b, w_b); PGR_LOG_APPREQ_RES(application_poll, (int)res); // hand the saved application request to the caller with the resulting response saved_app_req = 0; } return res; }
void unabto_tunnel_stream_accept(unabto_stream* stream) { tunnel* t = &tunnels[unabto_stream_index(stream)]; NABTO_LOG_TRACE(("Accepting stream and assigning it to tunnel %i", t)); UNABTO_ASSERT(t->state == TS_IDLE); unabto_tunnel_reset_tunnel_struct(t); t->stream = stream; t->state = TS_READ_COMMAND; t->tunnelId = tunnelCounter++; }
/** Search for the first running or first pending requrest in the requesst * queue. Returns the first running requrest if found. Otherwise the first * pending request is returned. If neither is found NULL is returned. */ queue_entry* find_any_request_in_queue(void) { #if NABTO_APPREQ_QUEUE_SIZE > 1 queue_entry* first_pending; queue_entry* first_running; queue_entry* entry; //At least one record must be present in the queue (for loop to be //finite). //queue may be full. queue_first_used == queue_next_free means full. UNABTO_ASSERT(!queue_empty()); first_running = NULL; first_pending = NULL; entry = queue_first_used; do { if (!first_pending && entry->state == APPREQ_WAITING) { first_pending = entry; if (first_running) break; } if (!first_running && entry->state == APPREQ_IN_APP) { first_running = entry; if (first_pending) break; } queue_inc(entry); } while (entry != queue_next_free); if (first_running) return first_running; if (first_pending) return first_pending; return NULL; #else //NABTO_APPREQ_QUEUE_SIZE == 1 UNABTO_ASSERT(!queue_empty()); return queue_top(); #endif }
uint8_t* insert_rendezvous_stats_payload(uint8_t* ptr, uint8_t* end, nabto_connect* con) { UNABTO_ASSERT(ptr <= end); if (end-ptr < NP_PAYLOAD_RENDEZVOUS_STATS_BYTELENGTH) { return NULL; } ptr = insert_payload(ptr, NP_PAYLOAD_TYPE_RENDEZVOUS_STATS, 0, 7); WRITE_FORWARD_U8(ptr, NP_PAYLOAD_RENDEZVOUS_STATS_VERSION); WRITE_FORWARD_U8(ptr, con->clientNatType); WRITE_FORWARD_U8(ptr, nmc.context.natType); WRITE_FORWARD_U16(ptr, con->rendezvousConnectState.portsOpened); WRITE_FORWARD_U16(ptr, con->rendezvousConnectState.socketsOpened); return ptr; }
uint8_t* insert_cp_id_payload(uint8_t* ptr, uint8_t* end, nabto_connect* con) { size_t cpIdLength = strlen(con->clientId); UNABTO_ASSERT(ptr <= end); if ((size_t)(end - ptr) < NP_PAYLOAD_CP_ID_BYTELENGTH + cpIdLength) { return NULL; } ptr = insert_payload(ptr, NP_PAYLOAD_TYPE_CP_ID, 0, 1+cpIdLength); WRITE_FORWARD_U8(ptr, NP_PAYLOAD_CP_ID_TYPE_MAIL); memcpy(ptr, (const void*) con->clientId, cpIdLength); ptr += cpIdLength; return ptr; }
bool unabto_tcp_fallback_handle_write(nabto_connect* con) { ssize_t status; int dataToSend; bool canMaybeSendMoreData = false; unabto_tcp_fallback_connection* fbConn = &fbConns[nabto_connection_index(con)]; UNABTO_ASSERT(fbConn->sendBufferSent <= fbConn->sendBufferLength); dataToSend = fbConn->sendBufferLength - fbConn->sendBufferSent; if (dataToSend == 0) { return false; } NABTO_LOG_TRACE(("data to send %i, sendBufferLength %i, sendBufferSent %i", dataToSend, fbConn->sendBufferLength, fbConn->sendBufferSent)); status = send(fbConn->socket, fbConn->sendBuffer + fbConn->sendBufferSent, dataToSend, MSG_NOSIGNAL); NABTO_LOG_TRACE(("tcp send status: %i", status)); if (status > 0) { fbConn->sendBufferSent += status; canMaybeSendMoreData = true; } else if (status < 0) { int err = errno; if ((err == EAGAIN) || err == EWOULDBLOCK) { canMaybeSendMoreData = false; } else { NABTO_LOG_ERROR((PRI_tcp_fb "Send of tcp packet failed", TCP_FB_ARGS(con))); close_tcp_socket(con); canMaybeSendMoreData = false; return canMaybeSendMoreData; } } if (fbConn->sendBufferSent > fbConn->sendBufferLength) { NABTO_LOG_FATAL(("fbConn->sendBufferSent(%" PRIsize ") > fbConn->sendBufferLength(%" PRIsize "), that should not be possible", fbConn->sendBufferSent, fbConn->sendBufferLength)); } if (fbConn->sendBufferSent == fbConn->sendBufferLength) { fbConn->sendBufferLength = 0; fbConn->sendBufferSent = 0; canMaybeSendMoreData = false; } NABTO_LOG_TRACE(("state after send, sendBufferLength %i, sendBufferSent %i", fbConn->sendBufferLength, fbConn->sendBufferSent)); return canMaybeSendMoreData; }
/* Handle application event in SYNC model. * Call application_event. */ application_event_result framework_event(naf_handle handle, uint8_t* iobuf, uint16_t size, uint16_t ilen, uint16_t* olen, nabto_connect* con, nabto_packet_header* hdr) { application_request req; unabto_buffer w_buf; unabto_query_response w_b; unabto_buffer r_buf; unabto_query_request r_b; application_event_result ret; /* framework_event_query always returns a zero handle in SYNC mode */ UNABTO_ASSERT(handle == 0); // When trying to respond immediately, the position to write the response is exactly // the same as the position of the request (we use the same buffer, i.e. iobuf). unabto_buffer_init(&w_buf, iobuf, size); unabto_query_response_init(&w_b, &w_buf); unabto_buffer_init(&r_buf, iobuf, ilen); unabto_query_request_init(&r_b, &r_buf); ret = framework_first_event(&req, &r_b, &w_b, con, hdr); if (ret != AER_REQ_RESPONSE_READY) { // pass the result to caller as an exception packet unabto_query_response_init(&w_b, &w_buf); // reset the response unabto_query_write_uint32(&w_b, ret); hdr->flags |= NP_PACKET_HDR_FLAG_EXCEPTION; // caller copies flags to send buffer! NABTO_LOG_TRACE(("Inserting EXCEPTION %i in buffer: %" PRItext, ret, result_s(ret))); } *olen = unabto_query_response_used(&w_b); NABTO_LOG_TRACE(("APPREQ framework_event: returns %" PRItext, result_s(ret))); return ret; }
uint8_t* insert_connect_stats_payload(uint8_t* ptr, uint8_t* end, nabto_connect* con) { uint8_t connectionType; UNABTO_ASSERT(ptr <= end); if (end-ptr < NP_PAYLOAD_CONNECT_STATS_BYTELENGTH) { return NULL; } ptr = insert_payload(ptr, NP_PAYLOAD_TYPE_CONNECT_STATS, 0, 2); switch (get_connection_type(con)) { case NCT_LOCAL: connectionType = NP_PAYLOAD_CONNECT_STATS_CONNECTION_TYPE_LOCAL; break; case NCT_CONNECTING: connectionType = NP_PAYLOAD_CONNECT_STATS_CONNECTION_TYPE_CONNECTING; break; case NCT_REMOTE_RELAY: connectionType = NP_PAYLOAD_CONNECT_STATS_CONNECTION_TYPE_UDP_RELAY; break; case NCT_REMOTE_RELAY_MICRO: connectionType = NP_PAYLOAD_CONNECT_STATS_CONNECTION_TYPE_TCP_RELAY; break; case NCT_REMOTE_P2P: connectionType = NP_PAYLOAD_CONNECT_STATS_CONNECTION_TYPE_P2P; break; } WRITE_FORWARD_U8(ptr, NP_PAYLOAD_CONNECT_STATS_VERSION); WRITE_FORWARD_U8(ptr, connectionType); return ptr; }
void unabto_hmac_sha256_buffers(const buffer_t keys[], uint8_t keys_size, const buffer_t messages[], uint8_t messages_size, uint8_t *mac, uint16_t mac_size) { uint16_t fill = 0; uint16_t num; const uint8_t *key_used; uint8_t i; uint8_t key_temp[SHA256_BLOCK_LENGTH]; uint8_t digest_temp[SHA256_DIGEST_LENGTH]; uint16_t key_size = 0; for (i = 0; i < keys_size; i++) { key_size += keys[i].size; } if (key_size <= SHA256_BLOCK_LENGTH) { uint8_t* ptr = key_temp; for (i = 0; i < keys_size; i++) { memcpy(ptr, (const void*)keys[i].data, keys[i].size); ptr += keys[i].size; } key_used = (const uint8_t*) key_temp; num = key_size; } else { // key_size > SHA256_BLOCK_LENGTH num = SHA256_DIGEST_LENGTH; //NABTO_LOG_TRACE(("key size %i", key_size)); //NABTO_LOG_BUFFER(key, key_size); unabto_sha256_init(&sha_ctx); for (i = 0; i < keys_size; i++) { unabto_sha256_update(&sha_ctx, keys[i].data, keys[i].size); } unabto_sha256_final(&sha_ctx,key_temp); //sha256(key, key_size, key_temp); //NABTO_LOG_BUFFER(key_temp, SHA256_DIGEST_LENGTH); key_used = (const unsigned char*)key_temp; } fill = SHA256_BLOCK_LENGTH - num; memset(block_pad + num, 0x36, fill); for (i = 0; i < num; i++) { block_pad[i] = key_used[i] ^ 0x36; } // print_sha256_ctx(sha_ctx); unabto_sha256_init(&sha_ctx); // print_sha256_ctx(sha_ctx); unabto_sha256_update(&sha_ctx, block_pad, SHA256_BLOCK_LENGTH); // print_sha256_ctx(sha_ctx); for (i = 0; i < messages_size; i++) { unabto_sha256_update(&sha_ctx, messages[i].data, messages[i].size); } // print_sha256_ctx(sha_ctx); unabto_sha256_final(&sha_ctx, digest_temp); // print_sha256_ctx(sha_ctx); memset(block_pad + num, 0x5c, fill); for (i = 0; i < num; i++) { block_pad[i] = key_used[i] ^ 0x5c; } unabto_sha256_init(&sha_ctx); // print_sha256_ctx(sha_ctx); unabto_sha256_update(&sha_ctx, block_pad, SHA256_BLOCK_LENGTH); // print_sha256_ctx(sha_ctx); unabto_sha256_update(&sha_ctx, digest_temp, SHA256_DIGEST_LENGTH); // print_sha256_ctx(sha_ctx); unabto_sha256_final(&sha_ctx, digest_temp); UNABTO_ASSERT(mac_size <= 32 ); memcpy(mac, (void*)digest_temp, mac_size); }
/* Poll a pending event. * Returns true if a response is ready (from any event in the queue). * If this function fails (returns false) all buffers are unmodified. */ bool framework_event_poll(uint8_t* buf, uint16_t size, uint16_t* olen, nabto_connect** con) { queue_entry *entry; application_event_result ret; application_request *req; struct naf_handle_s *handle; request_query find; if (queue_empty()) { // no requests queued in this framework, // hence no requests pending in application return false; } //Loop through the queue and find the running (IN_APP) request, //if any - and find the first waiting request. Then do the below. //OLD: entry = queue_top(); entry = find_any_request_in_queue(); if (!entry) { return false; } switch (entry->state) { case APPREQ_FREE: // first request has been released, // hence no requests pending in application return false; case APPREQ_IN_APP: // A request is pending in the application, continue break; default: // Unknown state - ??? UNABTO_ASSERT(0); return false; } //The application is waiting for this framework to call it. //Ask for the request in the application req = NULL; if (!application_poll_query(&req)) { // The application isn't ready with a response yet, poll again later return false; } //If application_poll_query returns true - it must return a request handle if (!req) { LOG_APPERR_0(entry, "Response dropped because the application returned a NULL request !?"); goto drop_poll; } //Find the handle holding the given request find.req = (const application_request*) req; find.found = NULL; queue_enum(find_request_cb, &find); handle = find.found; if (!handle) { LOG_APPERR_0(entry, "Response dropped because the application returned a request that isn't known by the framework"); goto drop_poll; } UNABTO_ASSERT(req == &handle->applicationRequest); entry = queue_find_entry(handle); //The request must be part of a handle in the queue if (!entry) { LOG_APPERR_0(entry, "Response dropped because the application returned a request that isn't known by the framework queue"); goto drop_poll; } UNABTO_ASSERT(handle == &entry->data); if (handle->connection->spnsi != handle->spnsi) { // the connection has changed, the request/response is dropped NABTO_LOG_TRACE(("Response dropped because the connection (" PRI_client_id ") is gone", CLIENT_ID_ARGH(*handle))); goto drop_poll; } NABTO_LOG_TRACE(("APPREQ: polling=%" PRI_index, queue_index(entry))); ret = framework_poll_event(&entry->data, buf, size, olen); if (ret != AER_REQ_RESPONSE_READY) { // pass the result to caller to become an exception if (!framework_write_exception(ret, &entry->data.header, buf, size, olen)) { LOG_APPERR_1(entry, "Response dropped because an exception packet with error code %i couldn't be generated", (int)ret); goto drop_poll; } } *con = entry->data.connection; framework_release_handle(handle); LOG_APPREQ_BYTES("framework_event_poll", "true", entry, *olen); LOG_APPREQ_QUEUE(); return true; drop_poll: application_poll_drop(req); framework_release_handle(&entry->data); LOG_APPREQ_BYTES("framework_event_poll", "false", entry, 0); LOG_APPREQ_QUEUE(); return false; }
/* Handle application event in ASYNC model. * Initialize the handle and call applicaion_event. */ application_event_result framework_event(naf_handle handle, uint8_t* iobuf, uint16_t size, uint16_t ilen, uint16_t* olen, nabto_connect* con, nabto_packet_header* hdr) { queue_entry* entry; unabto_buffer w_buf; unabto_query_response w_b; application_event_result ret; /* Find the entry containing the handle */ entry = queue_find_entry(handle); /* The given handle must belong to the queue */ UNABTO_ASSERT(entry); /* Handle must have been returned by framework_event_query(). */ if (entry->state != APPREQ_WAITING) { LOG_APPREQ_QUEUE(); LOG_APPREQ_EWAIT("framework_event", entry); } /* Initialize entry in queue. */ entry->data.connection = con; if (con && hdr) { memcpy(&entry->data.header, (const void*)hdr, sizeof(entry->data.header)); entry->data.spnsi = con->spnsi; } else { memset(&entry->data.header, 0, sizeof(entry->data.header)); entry->data.spnsi = 0; } /* Set up a write buffer to write into iobuf. */ unabto_buffer_init(&w_buf, iobuf, size); unabto_query_response_init(&w_b, &w_buf); /* Set up a read buffer that reads from local space or iobuf. */ unabto_buffer_init(&entry->data.applicationRequestBuffer, iobuf, ilen); unabto_query_request_init(&entry->data.readBuffer, &entry->data.applicationRequestBuffer); ret = framework_try_event(entry, &entry->data.readBuffer, &w_b, con, hdr); /* Handle errors */ switch (ret) { case AER_REQ_RESPONSE_READY: //ok - do nothing break; case AER_REQ_ACCEPTED: LOG_APPREQ_LEAVE("framework_event", ret, handle); LOG_APPREQ_QUEUE(); return ret; default: // pass the result to caller as an exception unabto_query_response_init(&w_b, &w_buf); // reset the response unabto_query_write_uint32(&w_b, ret); hdr->flags |= NP_PACKET_HDR_FLAG_EXCEPTION; // caller copies flags to send buffer! NABTO_LOG_TRACE(("Inserting EXCEPTION %i in buffer: %" PRItext, ret, result_s(ret))); break; } *olen = unabto_query_response_used(&w_b); LOG_APPREQ_LEAVE("framework_event", ret, handle); LOG_APPREQ_QUEUE(); return ret; }