From 0cda05a0cbbe53f97f74062cbd3fedca7d602925 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 24 Oct 2016 20:16:19 +0000 Subject: [PATCH] working towards new NAT client library implementation --- src/nat/nat_api.c | 457 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 447 insertions(+), 10 deletions(-) diff --git a/src/nat/nat_api.c b/src/nat/nat_api.c index 6488fdf88..e7320eb50 100644 --- a/src/nat/nat_api.c +++ b/src/nat/nat_api.c @@ -28,6 +28,31 @@ */ #include "platform.h" #include "gnunet_nat_service.h" +#include "nat.h" +#include "nat_stun.h" + + +/** + * Entry in DLL of addresses of this peer. + */ +struct AddrEntry +{ + + /** + * DLL. + */ + struct AddrEntry *next; + + /** + * DLL. + */ + struct AddrEntry *prev; + + /** + * Number of bytes that follow. + */ + socklen_t addrlen; +}; /** @@ -51,6 +76,16 @@ struct GNUNET_NAT_Handle */ struct GNUNET_MessageHeader *reg; + /** + * Head of address DLL. + */ + struct AddrEntry *ae_head; + + /** + * Tail of address DLL. + */ + struct AddrEntry *ae_tail; + /** * Function to call when our addresses change. */ @@ -66,9 +101,162 @@ struct GNUNET_NAT_Handle */ void *callback_cls; + /** + * Task scheduled to reconnect to the service. + */ + struct GNUNET_SCHEDULER_Task *reconnect_task; + + /** + * How long to wait until we reconnect. + */ + struct GNUNET_TIME_Relative reconnect_delay; }; +/** + * Task to connect to the NAT service. + * + * @param cls our `struct GNUNET_NAT_Handle *` + */ +static void +do_connect (void *cls); + + +/** + * Task to connect to the NAT service. + * + * @param nh handle to reconnect + */ +static void +reconnect (struct GNUNET_NAT_Handle *nh) +{ + if (NULL != nh->mq) + { + GNUNET_MQ_destroy (nh->mq); + nh->mq = NULL; + } + nh->reconnect_delay + = GNUNET_TIME_STD_BACKOFF (nh->reconnect_delay); + nh->reconnect_task + = GNUNET_SCHEDULER_add_delayed (nh->reconnect_delay, + &do_connect, + nh); +} + + +/** + * Check connection reversal request. + * + * @param cls our `struct GNUNET_NAT_Handle` + * @param crm the message + * @return #GNUNET_OK if @a crm is well-formed + */ +static int +check_connection_reversal_request (void *cls, + const struct GNUNET_NAT_ConnectionReversalRequestedMessage *crm) +{ + GNUNET_break (0); + return GNUNET_SYSERR; +} + + +/** + * Handle connection reversal request. + * + * @param cls our `struct GNUNET_NAT_Handle` + * @param crm the message + */ +static void +handle_connection_reversal_request (void *cls, + const struct GNUNET_NAT_ConnectionReversalRequestedMessage *crm) +{ + // FIXME: parse + // FIXME: call callback! + GNUNET_break (0); +} + + +/** + * Check address change notification. + * + * @param cls our `struct GNUNET_NAT_Handle` + * @param acn the message + * @return #GNUNET_OK if @a crm is well-formed + */ +static int +check_address_change_notification (void *cls, + const struct GNUNET_NAT_AddressChangeNotificationMessage *acn) +{ + GNUNET_break (0); + return GNUNET_SYSERR; +} + + +/** + * Handle connection reversal request. + * + * @param cls our `struct GNUNET_NAT_Handle` + * @param acn the message + */ +static void +handle_address_change_notification (void *cls, + const struct GNUNET_NAT_AddressChangeNotificationMessage *acn) +{ + // FIXME: parse + // FIXME: update ae-DLL + // FIXME: call callback! + GNUNET_break (0); +} + + +/** + * Handle queue errors by reconnecting to NAT. + * + * @param cls the `struct GNUNET_NAT_Handle *` + * @param error details about the error + */ +static void +mq_error_handler (void *cls, + enum GNUNET_MQ_Error error) +{ + struct GNUNET_NAT_Handle *nh = cls; + + reconnect (nh); +} + + +/** + * Task to connect to the NAT service. + * + * @param cls our `struct GNUNET_NAT_Handle *` + */ +static void +do_connect (void *cls) +{ + struct GNUNET_NAT_Handle *nh = cls; + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_var_size (connection_reversal_request, + GNUNET_MESSAGE_TYPE_NAT_CONNECTION_REVERSAL_REQUESTED, + struct GNUNET_NAT_ConnectionReversalRequestedMessage, + nh), + GNUNET_MQ_hd_var_size (address_change_notification, + GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE, + struct GNUNET_NAT_AddressChangeNotificationMessage, + nh), + GNUNET_MQ_handler_end () + }; + + nh->reconnect_task = NULL; + nh->mq = GNUNET_CLIENT_connecT (nh->cfg, + "nat", + handlers, + &mq_error_handler, + nh); + if (NULL == nh->mq) + reconnect (nh); +} + + /** * Attempt to enable port redirection and detect public IP address * contacting UPnP or NAT-PMP routers on the local network. Use @a @@ -101,17 +289,142 @@ GNUNET_NAT_register (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_NAT_ReversalCallback reversal_callback, void *callback_cls) { - struct GNUNET_NAT_Handle *nh = GNUNET_new (struct GNUNET_NAT_Handle); - + struct GNUNET_NAT_Handle *nh; + struct GNUNET_NAT_RegisterMessage *rm; + size_t len; + char *off; + + len = 0; + for (unsigned int i=0;i GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (*rm)) || + (num_addrs > UINT16_MAX) ) + { + GNUNET_break (0); + return NULL; + } + rm = GNUNET_malloc (sizeof (*rm) + len); + rm->header.size = htons (sizeof (*rm) + len); + rm->header.type = htons (GNUNET_MESSAGE_TYPE_NAT_REGISTER); + rm->flags = GNUNET_NAT_RF_NONE; + if (NULL != address_callback) + rm->flags |= GNUNET_NAT_RF_ADDRESSES; + if (NULL != reversal_callback) + rm->flags |= GNUNET_NAT_RF_REVERSAL; + rm->proto = proto; + rm->adv_port = htons (adv_port); + rm->num_addrs = htons ((uint16_t) num_addrs); + off = (char *) &rm[1]; + for (unsigned int i=0;ireg = &rm->header; nh->cfg = cfg; nh->address_callback = address_callback; nh->reversal_callback = reversal_callback; nh->callback_cls = callback_cls; + do_connect (nh); GNUNET_break (0); return nh; } +/** + * Check if an incoming message is a STUN message. + * + * @param data the packet + * @param len the length of the packet in @a data + * @return #GNUNET_YES if @a data is a STUN packet, + * #GNUNET_NO if the packet is invalid (not a stun packet) + */ +static int +test_stun_packet (const void *data, + size_t len) +{ + const struct stun_header *hdr; + const struct stun_attr *attr; + uint32_t advertised_message_size; + uint32_t message_magic_cookie; + + /* On entry, 'len' is the length of the UDP payload. After the + * initial checks it becomes the size of unprocessed options, + * while 'data' is advanced accordingly. + */ + if (len < sizeof(struct stun_header)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "STUN packet too short (only %d, wanting at least %d)\n", + (int) len, + (int) sizeof (struct stun_header)); + return GNUNET_NO; + } + hdr = (const struct stun_header *) data; + /* Skip header as it is already in hdr */ + len -= sizeof (struct stun_header); + data += sizeof (struct stun_header); + + /* len as advertised in the message */ + advertised_message_size = ntohs (hdr->msglen); + + message_magic_cookie = ntohl (hdr->magic); + /* Compare if the cookie match */ + if (STUN_MAGIC_COOKIE != message_magic_cookie) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Invalid magic cookie for STUN\n"); + return GNUNET_NO; + } + + if (advertised_message_size > len) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Scrambled STUN packet length (got %d, expecting %d)\n", + advertised_message_size, + (int)len); + return GNUNET_NO; + } + len = advertised_message_size; + while (len > 0) + { + if (len < sizeof (struct stun_attr)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Attribute too short in STUN packet (got %d, expecting %d)\n", + (int) len, + (int) sizeof(struct stun_attr)); + return GNUNET_NO; + } + attr = (const struct stun_attr *) data; + + /* compute total attribute length */ + advertised_message_size = ntohs (attr->len) + sizeof(struct stun_attr); + + /* Check if we still have space in our buffer */ + if (advertised_message_size > len) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", + advertised_message_size, + (int) len); + return GNUNET_NO; + } + data += advertised_message_size; + len -= advertised_message_size; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "STUN Packet, msg %04x, length: %d\n", + ntohs (hdr->msgtype), + advertised_message_size); + return GNUNET_OK; +} + + /** * Handle an incoming STUN message. This function is useful as * some GNUnet service may be listening on a UDP port and might @@ -128,6 +441,7 @@ GNUNET_NAT_register (const struct GNUNET_CONFIGURATION_Handle *cfg, * * @param nh handle to the NAT service * @param sender_addr address from which we got @a data + * @param sender_addr_len number of bytes in @a sender_addr * @param data the packet * @param data_size number of bytes in @a data * @return #GNUNET_OK on success @@ -137,11 +451,36 @@ GNUNET_NAT_register (const struct GNUNET_CONFIGURATION_Handle *cfg, int GNUNET_NAT_stun_handle_packet (struct GNUNET_NAT_Handle *nh, const struct sockaddr *sender_addr, + size_t sender_addr_len, const void *data, size_t data_size) { - GNUNET_break (0); - return GNUNET_SYSERR; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_NAT_HandleStunMessage *hsn; + char *buf; + + if (GNUNET_YES != + test_stun_packet (data, + data_size)) + return GNUNET_NO; + if (NULL == nh->mq) + return GNUNET_SYSERR; + env = GNUNET_MQ_msg_extra (hsn, + data_size + sender_addr_len, + GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN); + hsn->sender_addr_size = htons ((uint16_t) sender_addr_len); + hsn->payload_size = htons ((uint16_t) data_size); + buf = (char *) &hsn[1]; + GNUNET_memcpy (buf, + sender_addr, + sender_addr_len); + buf += sender_addr_len; + GNUNET_memcpy (buf, + data, + data_size); + GNUNET_MQ_send (nh->mq, + env); + return GNUNET_OK; } @@ -163,8 +502,21 @@ GNUNET_NAT_test_address (struct GNUNET_NAT_Handle *nh, const void *addr, socklen_t addrlen) { - GNUNET_break (0); - return GNUNET_SYSERR; + struct AddrEntry *ae; + + if ( (addrlen != sizeof (struct sockaddr_in)) && + (addrlen != sizeof (struct sockaddr_in6)) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + for (ae = nh->ae_head; NULL != ae; ae = ae->next) + if ( (addrlen == ae->addrlen) && + (0 == memcmp (addr, + &ae[1], + addrlen)) ) + return GNUNET_YES; + return GNUNET_NO; } @@ -185,8 +537,28 @@ GNUNET_NAT_request_reversal (struct GNUNET_NAT_Handle *nh, const struct sockaddr_in *local_sa, const struct sockaddr_in *remote_sa) { - GNUNET_break (0); - return GNUNET_SYSERR; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_NAT_RequestConnectionReversalMessage *req; + char *buf; + + if (NULL == nh->mq) + return GNUNET_SYSERR; + env = GNUNET_MQ_msg_extra (req, + 2 * sizeof (struct sockaddr_in), + GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL); + req->local_addr_size = htons (sizeof (struct sockaddr_in)); + req->remote_addr_size = htons (sizeof (struct sockaddr_in)); + buf = (char *) &req[1]; + GNUNET_memcpy (buf, + local_sa, + sizeof (struct sockaddr_in)); + buf += sizeof (struct sockaddr_in); + GNUNET_memcpy (buf, + remote_sa, + sizeof (struct sockaddr_in)); + GNUNET_MQ_send (nh->mq, + env); + return GNUNET_OK; } @@ -236,6 +608,44 @@ struct GNUNET_NAT_Test }; +/** + * Handle result for a NAT test from the service. + * + * @param cls our `struct GNUNET_NAT_Test *` + * @param rm message with the result of the test + */ +static void +handle_test_result (void *cls, + const struct GNUNET_NAT_TestResultMessage *rm) +{ + struct GNUNET_NAT_Test *tst = cls; + enum GNUNET_NAT_StatusCode sc; + + sc = (enum GNUNET_NAT_StatusCode) ntohl (rm->status_code); + tst->cb (tst->cb_cls, + sc); + GNUNET_NAT_test_stop (tst); +} + + +/** + * Handle queue errors by reconnecting to NAT. + * + * @param cls the `struct GNUNET_NAT_Test *` + * @param error details about the error + */ +static void +tst_error_handler (void *cls, + enum GNUNET_MQ_Error error) +{ + struct GNUNET_NAT_Test *tst = cls; + + tst->cb (tst->cb_cls, + GNUNET_NAT_ERROR_IPC_FAILURE); + GNUNET_NAT_test_stop (tst); +} + + /** * Start testing if NAT traversal works using the given configuration * (IPv4-only). The transport adapters should be down while using @@ -262,10 +672,38 @@ GNUNET_NAT_test_start (const struct GNUNET_CONFIGURATION_Handle *cfg, void *report_cls) { struct GNUNET_NAT_Test *tst = GNUNET_new (struct GNUNET_NAT_Test); + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_fixed_size (test_result, + GNUNET_MESSAGE_TYPE_NAT_TEST_RESULT, + struct GNUNET_NAT_TestResultMessage, + tst), + GNUNET_MQ_handler_end () + }; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_NAT_RequestTestMessage *req; tst->cb = report; tst->cb_cls = report_cls; - GNUNET_break (0); + tst->mq = GNUNET_CLIENT_connecT (cfg, + "nat", + handlers, + &tst_error_handler, + tst); + if (NULL == tst->mq) + { + GNUNET_break (0); + GNUNET_free (tst); + return NULL; + } + env = GNUNET_MQ_msg (req, + GNUNET_MESSAGE_TYPE_NAT_REQUEST_TEST); + req->bind_port = htons (bnd_port); + req->extern_port = htons (extern_port); + req->bind_ip = bind_ip; + req->extern_ip = extern_ip; + req->proto = proto; + GNUNET_MQ_send (tst->mq, + env); return tst; } @@ -278,7 +716,6 @@ GNUNET_NAT_test_start (const struct GNUNET_CONFIGURATION_Handle *cfg, void GNUNET_NAT_test_stop (struct GNUNET_NAT_Test *tst) { - GNUNET_break (0); GNUNET_MQ_destroy (tst->mq); GNUNET_free (tst); } -- 2.25.1