123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709 |
- /*
- This file is part of GNUnet.
- Copyright (C) 2007-2017 GNUnet e.V.
- GNUnet is free software: you can redistribute it and/or modify it
- under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License,
- or (at your option) any later version.
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- SPDX-License-Identifier: AGPL3.0-or-later
- */
- /**
- * @author Christian Grothoff
- * @author Milan Bouchet-Valat
- *
- * @file nat/nat_api.c
- * Service for handling UPnP and NAT-PMP port forwarding
- * and external IP address retrieval
- */
- #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;
- /**
- * Place where the application can store data (on add,
- * and retrieve on remove).
- */
- void *app_ctx;
- /**
- * Address class of the address.
- */
- enum GNUNET_NAT_AddressClass ac;
- /**
- * Number of bytes that follow.
- */
- socklen_t addrlen;
- };
- /**
- * Handle for active NAT registrations.
- */
- struct GNUNET_NAT_Handle
- {
- /**
- * Configuration we use.
- */
- const struct GNUNET_CONFIGURATION_Handle *cfg;
- /**
- * Message queue for communicating with the NAT service.
- */
- struct GNUNET_MQ_Handle *mq;
- /**
- * Our registration message.
- */
- 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.
- */
- GNUNET_NAT_AddressCallback address_callback;
- /**
- * Function to call when another peer requests connection reversal.
- */
- GNUNET_NAT_ReversalCallback reversal_callback;
- /**
- * Closure for the various callbacks.
- */
- 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)
- {
- struct AddrEntry *ae;
- if (NULL != nh->mq)
- {
- GNUNET_MQ_destroy (nh->mq);
- nh->mq = NULL;
- }
- while (NULL != (ae = nh->ae_head))
- {
- GNUNET_CONTAINER_DLL_remove (nh->ae_head, nh->ae_tail, ae);
- nh->address_callback (nh->callback_cls,
- &ae->app_ctx,
- GNUNET_NO,
- ae->ac,
- (const struct sockaddr *) &ae[1],
- ae->addrlen);
- GNUNET_free (ae);
- }
- 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)
- {
- if (ntohs (crm->header.size) != sizeof(*crm) + sizeof(struct sockaddr_in))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
- }
- /**
- * 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)
- {
- struct GNUNET_NAT_Handle *nh = cls;
- nh->reversal_callback (nh->callback_cls,
- (const struct sockaddr *) &crm[1],
- sizeof(struct sockaddr_in));
- }
- /**
- * 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)
- {
- size_t alen = ntohs (acn->header.size) - sizeof(*acn);
- switch (alen)
- {
- case sizeof(struct sockaddr_in): {
- const struct sockaddr_in *s4 = (const struct sockaddr_in *) &acn[1];
- if (AF_INET != s4->sin_family)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- }
- break;
- case sizeof(struct sockaddr_in6): {
- const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *) &acn[1];
- if (AF_INET6 != s6->sin6_family)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- }
- break;
- default:
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
- }
- /**
- * 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)
- {
- struct GNUNET_NAT_Handle *nh = cls;
- size_t alen = ntohs (acn->header.size) - sizeof(*acn);
- const struct sockaddr *sa = (const struct sockaddr *) &acn[1];
- enum GNUNET_NAT_AddressClass ac;
- struct AddrEntry *ae;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Received address change notification\n");
- ac = (enum GNUNET_NAT_AddressClass) ntohl (acn->addr_class);
- if (GNUNET_YES == ntohl (acn->add_remove))
- {
- ae = GNUNET_malloc (sizeof(*ae) + alen);
- ae->ac = ac;
- ae->addrlen = alen;
- GNUNET_memcpy (&ae[1], sa, alen);
- GNUNET_CONTAINER_DLL_insert (nh->ae_head, nh->ae_tail, ae);
- nh->address_callback (nh->callback_cls,
- &ae->app_ctx,
- ntohl (acn->add_remove),
- ac,
- sa,
- alen);
- }
- else
- {
- for (ae = nh->ae_head; NULL != ae; ae = ae->next)
- if ((ae->addrlen == alen) && (0 == memcmp (&ae[1], sa, alen)))
- break;
- if (NULL == ae)
- {
- GNUNET_break (0);
- reconnect (nh);
- return;
- }
- GNUNET_CONTAINER_DLL_remove (nh->ae_head, nh->ae_tail, ae);
- nh->address_callback (nh->callback_cls,
- &ae->app_ctx,
- ntohl (acn->add_remove),
- ac,
- sa,
- alen);
- GNUNET_free (ae);
- }
- }
- /**
- * 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 () };
- struct GNUNET_MQ_Envelope *env;
- nh->reconnect_task = NULL;
- nh->mq =
- GNUNET_CLIENT_connect (nh->cfg, "nat", handlers, &mq_error_handler, nh);
- if (NULL == nh->mq)
- {
- reconnect (nh);
- return;
- }
- env = GNUNET_MQ_msg_copy (nh->reg);
- GNUNET_MQ_send (nh->mq, env);
- }
- /**
- * Attempt to enable port redirection and detect public IP address
- * contacting UPnP or NAT-PMP routers on the local network. Use @a
- * addr to specify to which of the local host's addresses should the
- * external port be mapped. The port is taken from the corresponding
- * sockaddr_in[6] field. The NAT module should call the given @a
- * address_callback for any 'plausible' external address.
- *
- * @param cfg configuration to use
- * @param config_section name of the configuration section for optionsx
- * @param proto protocol this is about, IPPROTO_TCP or IPPROTO_UDP
- * @param num_addrs number of addresses in @a addrs
- * @param addrs list of local addresses packets should be redirected to
- * @param addrlens actual lengths of the addresses in @a addrs
- * @param address_callback function to call everytime the public IP address changes
- * @param reversal_callback function to call if someone wants connection reversal from us,
- * NULL if connection reversal is not supported
- * @param callback_cls closure for callbacks
- * @return NULL on error, otherwise handle that can be used to unregister
- */
- struct GNUNET_NAT_Handle *
- GNUNET_NAT_register (const struct GNUNET_CONFIGURATION_Handle *cfg,
- const char *config_section,
- uint8_t proto,
- unsigned int num_addrs,
- const struct sockaddr **addrs,
- const socklen_t *addrlens,
- GNUNET_NAT_AddressCallback address_callback,
- GNUNET_NAT_ReversalCallback reversal_callback,
- void *callback_cls)
- {
- struct GNUNET_NAT_Handle *nh;
- struct GNUNET_NAT_RegisterMessage *rm;
- size_t len;
- size_t str_len;
- char *off;
- len = 0;
- for (unsigned int i = 0; i < num_addrs; i++)
- len += addrlens[i];
- str_len = strlen (config_section) + 1;
- len += str_len;
- if ((len > GNUNET_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->str_len = htons (str_len);
- rm->num_addrs = htons ((uint16_t) num_addrs);
- off = (char *) &rm[1];
- for (unsigned int i = 0; i < num_addrs; i++)
- {
- switch (addrs[i]->sa_family)
- {
- case AF_INET:
- if (sizeof(struct sockaddr_in) != addrlens[i])
- {
- GNUNET_break (0);
- GNUNET_free (rm);
- return NULL;
- }
- break;
- case AF_INET6:
- if (sizeof(struct sockaddr_in6) != addrlens[i])
- {
- GNUNET_break (0);
- GNUNET_free (rm);
- return NULL;
- }
- break;
- #if AF_UNIX
- case AF_UNIX:
- if (sizeof(struct sockaddr_un) != addrlens[i])
- {
- GNUNET_break (0);
- GNUNET_free (rm);
- return NULL;
- }
- break;
- #endif
- default:
- GNUNET_break (0);
- GNUNET_free (rm);
- return NULL;
- }
- GNUNET_memcpy (off, addrs[i], addrlens[i]);
- off += addrlens[i];
- }
- GNUNET_memcpy (off, config_section, str_len);
- nh = GNUNET_new (struct GNUNET_NAT_Handle);
- nh->reg = &rm->header;
- nh->cfg = cfg;
- nh->address_callback = address_callback;
- nh->reversal_callback = reversal_callback;
- nh->callback_cls = callback_cls;
- do_connect (nh);
- 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
- * thus receive STUN messages while trying to receive other data.
- * In this case, this function can be used to process replies
- * to STUN requests.
- *
- * The function does some basic sanity checks on packet size and
- * content, try to extract a bit of information.
- *
- * At the moment this only processes BIND requests, and returns the
- * externally visible address of the request to the rest of the
- * NAT logic.
- *
- * @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
- * #GNUNET_NO if the packet is not a STUN packet
- * #GNUNET_SYSERR on internal error handling the packet
- */
- 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)
- {
- 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;
- }
- /**
- * Test if the given address is (currently) a plausible IP address for
- * this peer. Mostly a convenience function so that clients do not
- * have to explicitly track all IPs that the #GNUNET_NAT_AddressCallback
- * has returned so far.
- *
- * @param nh the handle returned by register
- * @param addr IP address to test (IPv4 or IPv6)
- * @param addrlen number of bytes in @a addr
- * @return #GNUNET_YES if the address is plausible,
- * #GNUNET_NO if the address is not plausible,
- * #GNUNET_SYSERR if the address is malformed
- */
- int
- GNUNET_NAT_test_address (struct GNUNET_NAT_Handle *nh,
- const void *addr,
- socklen_t addrlen)
- {
- 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;
- }
- /**
- * We learned about a peer (possibly behind NAT) so run the
- * gnunet-nat-client to send dummy ICMP responses to cause
- * that peer to connect to us (connection reversal).
- *
- * @param nh handle (used for configuration)
- * @param local_sa our local address of the peer (IPv4-only)
- * @param remote_sa the remote address of the peer (IPv4-only)
- * @return #GNUNET_SYSERR on error,
- * #GNUNET_NO if connection reversal is unavailable,
- * #GNUNET_OK otherwise (presumably in progress)
- */
- int
- GNUNET_NAT_request_reversal (struct GNUNET_NAT_Handle *nh,
- const struct sockaddr_in *local_sa,
- const struct sockaddr_in *remote_sa)
- {
- struct GNUNET_MQ_Envelope *env;
- struct GNUNET_NAT_RequestConnectionReversalMessage *req;
- char *buf;
- if (NULL == nh->mq)
- return GNUNET_SYSERR;
- GNUNET_break (AF_INET == local_sa->sin_family);
- GNUNET_break (AF_INET == remote_sa->sin_family);
- 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;
- }
- /**
- * Stop port redirection and public IP address detection for the given
- * handle. This frees the handle, after having sent the needed
- * commands to close open ports.
- *
- * @param nh the handle to stop
- */
- void
- GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *nh)
- {
- if (NULL != nh->mq)
- {
- GNUNET_MQ_destroy (nh->mq);
- nh->mq = NULL;
- }
- if (NULL != nh->reconnect_task)
- {
- GNUNET_SCHEDULER_cancel (nh->reconnect_task);
- nh->reconnect_task = NULL;
- }
- GNUNET_free (nh->reg);
- GNUNET_free (nh);
- }
- /* end of nat_api.c */
|