12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054 |
- /*
- This file is part of GNUnet.
- Copyright (C) 2016, 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
- */
- /**
- * @file nat/gnunet-service-nat.c
- * @brief network address translation traversal service
- * @author Christian Grothoff
- *
- * The purpose of this service is to enable transports to
- * traverse NAT routers, by providing traversal options and
- * knowledge about the local network topology.
- *
- * TODO:
- * - migrate test cases to new NAT service
- * - add new traceroute-based logic for external IP detection
- *
- * - implement & test STUN processing to classify NAT;
- * basically, open port & try different methods.
- */
- #include "platform.h"
- #include <math.h>
- #include "gnunet_util_lib.h"
- #include "gnunet_protocols.h"
- #include "gnunet_signatures.h"
- #include "gnunet_statistics_service.h"
- #include "gnunet_resolver_service.h"
- #include "gnunet_nat_service.h"
- #include "gnunet-service-nat.h"
- #include "gnunet-service-nat_externalip.h"
- #include "gnunet-service-nat_stun.h"
- #include "gnunet-service-nat_mini.h"
- #include "gnunet-service-nat_helper.h"
- #include "nat.h"
- #include <gcrypt.h>
- /**
- * How often should we ask the OS about a list of active
- * network interfaces?
- */
- #define SCAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
- /**
- * How long do we wait until we forcefully terminate autoconfiguration?
- */
- #define AUTOCONFIG_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
- /**
- * How often do we scan for changes in how our external (dyndns) hostname resolves?
- */
- #define DYNDNS_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 7)
- /**
- * Information we track per client address.
- */
- struct ClientAddress
- {
- /**
- * Network address used by the client.
- */
- struct sockaddr_storage ss;
- /**
- * Handle to active UPnP request where we asked upnpc to open
- * a port at the NAT. NULL if we do not have such a request
- * pending.
- */
- struct GNUNET_NAT_MiniHandle *mh;
- };
- /**
- * List of local addresses this system has.
- */
- struct LocalAddressList
- {
- /**
- * This is a linked list.
- */
- struct LocalAddressList *next;
- /**
- * Previous entry.
- */
- struct LocalAddressList *prev;
- /**
- * Context for a gnunet-helper-nat-server used to listen
- * for ICMP messages to this client for connection reversal.
- */
- struct HelperContext *hc;
- /**
- * The address itself (i.e. `struct sockaddr_in` or `struct
- * sockaddr_in6`, in the respective byte order).
- */
- struct sockaddr_storage addr;
- /**
- * Address family. (FIXME: redundant, addr.ss_family! Remove!?)
- */
- int af;
- /**
- * #GNUNET_YES if we saw this one in the previous iteration,
- * but not in the current iteration and thus might need to
- * remove it at the end.
- */
- int old;
- /**
- * What type of address is this?
- */
- enum GNUNET_NAT_AddressClass ac;
- };
- /**
- * Internal data structure we track for each of our clients.
- */
- struct ClientHandle
- {
- /**
- * Kept in a DLL.
- */
- struct ClientHandle *next;
- /**
- * Kept in a DLL.
- */
- struct ClientHandle *prev;
- /**
- * Underlying handle for this client with the service.
- */
- struct GNUNET_SERVICE_Client *client;
- /**
- * Message queue for communicating with the client.
- */
- struct GNUNET_MQ_Handle *mq;
- /**
- * Array of addresses used by the service.
- */
- struct ClientAddress *caddrs;
- /**
- * External DNS name and port given by user due to manual
- * hole punching. Special DNS name 'AUTO' is used to indicate
- * desire for automatic determination of the external IP
- * (instead of DNS or manual configuration, i.e. to be used
- * if the IP keeps changing and we have no DynDNS, but we do
- * have a hole punched).
- */
- char *hole_external;
- /**
- * Name of the configuration section this client cares about.
- */
- char *section_name;
- /**
- * Task for periodically re-running the @e ext_dns DNS lookup.
- */
- struct GNUNET_SCHEDULER_Task *ext_dns_task;
- /**
- * Handle for (DYN)DNS lookup of our external IP as given in
- * @e hole_external.
- */
- struct GNUNET_RESOLVER_RequestHandle *ext_dns;
- /**
- * Handle for monitoring external IP changes.
- */
- struct GN_ExternalIPMonitor *external_monitor;
- /**
- * DLL of external IP addresses as given in @e hole_external.
- */
- struct LocalAddressList *ext_addr_head;
- /**
- * DLL of external IP addresses as given in @e hole_external.
- */
- struct LocalAddressList *ext_addr_tail;
- /**
- * Port number we found in @e hole_external.
- */
- uint16_t ext_dns_port;
- /**
- * What does this client care about?
- */
- enum GNUNET_NAT_RegisterFlags flags;
- /**
- * Is any of the @e caddrs in a reserved subnet for NAT?
- */
- int natted_address;
- /**
- * Number of addresses that this service is bound to.
- * Length of the @e caddrs array.
- */
- uint16_t num_caddrs;
- /**
- * Client's IPPROTO, e.g. IPPROTO_UDP or IPPROTO_TCP.
- */
- uint8_t proto;
- };
- /**
- * External IP address as given to us via some STUN server.
- */
- struct StunExternalIP
- {
- /**
- * Kept in a DLL.
- */
- struct StunExternalIP *next;
- /**
- * Kept in a DLL.
- */
- struct StunExternalIP *prev;
- /**
- * Task we run to remove this entry when it is stale.
- */
- struct GNUNET_SCHEDULER_Task *timeout_task;
- /**
- * Our external IP address as reported by the
- * STUN server.
- */
- struct sockaddr_in external_addr;
- /**
- * Address of the reporting STUN server. Used to
- * detect when a STUN server changes its opinion
- * to more quickly remove stale results.
- */
- struct sockaddr_storage stun_server_addr;
- /**
- * Number of bytes used in @e stun_server_addr.
- */
- size_t stun_server_addr_len;
- };
- /**
- * Timeout to use when STUN data is considered stale.
- */
- static struct GNUNET_TIME_Relative stun_stale_timeout;
- /**
- * How often do we scan for changes in how our external (dyndns) hostname resolves?
- */
- static struct GNUNET_TIME_Relative dyndns_frequency;
- /**
- * Handle to our current configuration.
- */
- static const struct GNUNET_CONFIGURATION_Handle *cfg;
- /**
- * Handle to the statistics service.
- */
- static struct GNUNET_STATISTICS_Handle *stats;
- /**
- * Task scheduled to periodically scan our network interfaces.
- */
- static struct GNUNET_SCHEDULER_Task *scan_task;
- /**
- * Head of client DLL.
- */
- static struct ClientHandle *ch_head;
- /**
- * Tail of client DLL.
- */
- static struct ClientHandle *ch_tail;
- /**
- * Head of DLL of local addresses.
- */
- static struct LocalAddressList *lal_head;
- /**
- * Tail of DLL of local addresses.
- */
- static struct LocalAddressList *lal_tail;
- /**
- * Kept in a DLL.
- */
- static struct StunExternalIP *se_head;
- /**
- * Kept in a DLL.
- */
- static struct StunExternalIP *se_tail;
- /**
- * Is UPnP enabled? #GNUNET_YES if enabled, #GNUNET_NO if disabled,
- * #GNUNET_SYSERR if configuration enabled but binary is unavailable.
- */
- int enable_upnp;
- /**
- * Remove and free an entry from the #lal_head DLL.
- *
- * @param lal entry to free
- */
- static void
- free_lal (struct LocalAddressList *lal)
- {
- GNUNET_CONTAINER_DLL_remove (lal_head,
- lal_tail,
- lal);
- if (NULL != lal->hc)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
- "Lost NATed local address %s, stopping NAT server\n",
- GNUNET_a2s ((const struct sockaddr *) &lal->addr,
- sizeof (struct sockaddr_in)));
- GN_stop_gnunet_nat_server_ (lal->hc);
- lal->hc = NULL;
- }
- GNUNET_free (lal);
- }
- /**
- * Free the DLL starting at #lal_head.
- */
- static void
- destroy_lal ()
- {
- struct LocalAddressList *lal;
- while (NULL != (lal = lal_head))
- free_lal (lal);
- }
- /**
- * Check validity of #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from
- * client.
- *
- * @param cls client who sent the message
- * @param message the message received
- * @return #GNUNET_OK if message is well-formed
- */
- static int
- check_register (void *cls,
- const struct GNUNET_NAT_RegisterMessage *message)
- {
- uint16_t num_addrs = ntohs (message->num_addrs);
- const char *off = (const char *) &message[1];
- size_t left = ntohs (message->header.size) - sizeof (*message);
- for (unsigned int i=0;i<num_addrs;i++)
- {
- size_t alen;
- const struct sockaddr *sa = (const struct sockaddr *) off;
- if (sizeof (sa_family_t) > left)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- switch (sa->sa_family)
- {
- case AF_INET:
- alen = sizeof (struct sockaddr_in);
- break;
- case AF_INET6:
- alen = sizeof (struct sockaddr_in6);
- break;
- #if AF_UNIX
- case AF_UNIX:
- alen = sizeof (struct sockaddr_un);
- break;
- #endif
- default:
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (alen > left)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- off += alen;
- left -= alen;
- }
- if (left != ntohs (message->str_len))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
- }
- /**
- * Check if @a ip is in @a network with @a bits netmask.
- *
- * @param network to test
- * @param ip IP address to test
- * @param bits bitmask for the network
- * @return #GNUNET_YES if @a ip is in @a network
- */
- static int
- match_ipv4 (const char *network,
- const struct in_addr *ip,
- uint8_t bits)
- {
- struct in_addr net;
- if (0 == ip->s_addr)
- return GNUNET_YES;
- if (0 == bits)
- return GNUNET_YES;
- GNUNET_assert (1 == inet_pton (AF_INET,
- network,
- &net));
- return ! ((ip->s_addr ^ net.s_addr) & htonl (0xFFFFFFFFu << (32 - bits)));
- }
- /**
- * Check if @a ip is in @a network with @a bits netmask.
- *
- * @param network to test
- * @param ip IP address to test
- * @param bits bitmask for the network
- * @return #GNUNET_YES if @a ip is in @a network
- */
- static int
- match_ipv6 (const char *network,
- const struct in6_addr *ip,
- uint8_t bits)
- {
- struct in6_addr net;
- struct in6_addr mask;
- unsigned int off;
- if (0 == bits)
- return GNUNET_YES;
- GNUNET_assert (1 == inet_pton (AF_INET6,
- network,
- &net));
- memset (&mask, 0, sizeof (mask));
- if (0 == memcmp (&mask,
- ip,
- sizeof (mask)))
- return GNUNET_YES;
- off = 0;
- while (bits > 8)
- {
- mask.s6_addr[off++] = 0xFF;
- bits -= 8;
- }
- while (bits > 0)
- {
- mask.s6_addr[off] = (mask.s6_addr[off] >> 1) + 0x80;
- bits--;
- }
- for (unsigned j = 0; j < sizeof (struct in6_addr) / sizeof (uint32_t); j++)
- if (((((uint32_t *) ip)[j] & ((uint32_t *) &mask)[j])) !=
- (((uint32_t *) &net)[j] & ((int *) &mask)[j]))
- return GNUNET_NO;
- return GNUNET_YES;
- }
- /**
- * Test if the given IPv4 address is in a known range
- * for private networks.
- *
- * @param ip address to test
- * @return #GNUNET_YES if @a ip is in a NAT range
- */
- static int
- is_nat_v4 (const struct in_addr *ip)
- {
- return
- match_ipv4 ("10.0.0.0", ip, 8) || /* RFC 1918 */
- match_ipv4 ("100.64.0.0", ip, 10) || /* CG-NAT, RFC 6598 */
- match_ipv4 ("192.168.0.0", ip, 12) || /* RFC 1918 */
- match_ipv4 ("169.254.0.0", ip, 16) || /* AUTO, RFC 3927 */
- match_ipv4 ("172.16.0.0", ip, 16); /* RFC 1918 */
- }
- /**
- * Test if the given IPv6 address is in a known range
- * for private networks.
- *
- * @param ip address to test
- * @return #GNUNET_YES if @a ip is in a NAT range
- */
- static int
- is_nat_v6 (const struct in6_addr *ip)
- {
- return
- match_ipv6 ("fc00::", ip, 7) || /* RFC 4193 */
- match_ipv6 ("fec0::", ip, 10) || /* RFC 3879 */
- match_ipv6 ("fe80::", ip, 10); /* RFC 4291, link-local */
- }
- /**
- * Closure for #ifc_proc.
- */
- struct IfcProcContext
- {
- /**
- * Head of DLL of local addresses.
- */
- struct LocalAddressList *lal_head;
- /**
- * Tail of DLL of local addresses.
- */
- struct LocalAddressList *lal_tail;
- };
- /**
- * Callback function invoked for each interface found. Adds them
- * to our new address list.
- *
- * @param cls a `struct IfcProcContext *`
- * @param name name of the interface (can be NULL for unknown)
- * @param isDefault is this presumably the default interface
- * @param addr address of this interface (can be NULL for unknown or unassigned)
- * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
- * @param netmask the network mask (can be NULL for unknown or unassigned)
- * @param addrlen length of the address
- * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort
- */
- static int
- ifc_proc (void *cls,
- const char *name,
- int isDefault,
- const struct sockaddr *addr,
- const struct sockaddr *broadcast_addr,
- const struct sockaddr *netmask,
- socklen_t addrlen)
- {
- struct IfcProcContext *ifc_ctx = cls;
- struct LocalAddressList *lal;
- size_t alen;
- const struct in_addr *ip4;
- const struct in6_addr *ip6;
- enum GNUNET_NAT_AddressClass ac;
- switch (addr->sa_family)
- {
- case AF_INET:
- alen = sizeof (struct sockaddr_in);
- ip4 = &((const struct sockaddr_in *) addr)->sin_addr;
- if (match_ipv4 ("127.0.0.0", ip4, 8))
- ac = GNUNET_NAT_AC_LOOPBACK;
- else if (is_nat_v4 (ip4))
- ac = GNUNET_NAT_AC_LAN;
- else
- ac = GNUNET_NAT_AC_GLOBAL;
- break;
- case AF_INET6:
- alen = sizeof (struct sockaddr_in6);
- ip6 = &((const struct sockaddr_in6 *) addr)->sin6_addr;
- if (match_ipv6 ("::1", ip6, 128))
- ac = GNUNET_NAT_AC_LOOPBACK;
- else if (is_nat_v6 (ip6))
- ac = GNUNET_NAT_AC_LAN;
- else
- ac = GNUNET_NAT_AC_GLOBAL;
- if ( (ip6->s6_addr[11] == 0xFF) &&
- (ip6->s6_addr[12] == 0xFE) )
- {
- /* contains a MAC, be extra careful! */
- ac |= GNUNET_NAT_AC_PRIVATE;
- }
- break;
- #if AF_UNIX
- case AF_UNIX:
- GNUNET_break (0);
- return GNUNET_OK;
- #endif
- default:
- GNUNET_break (0);
- return GNUNET_OK;
- }
- lal = GNUNET_malloc (sizeof (*lal));
- lal->af = addr->sa_family;
- lal->ac = ac;
- GNUNET_memcpy (&lal->addr,
- addr,
- alen);
- GNUNET_CONTAINER_DLL_insert (ifc_ctx->lal_head,
- ifc_ctx->lal_tail,
- lal);
- return GNUNET_OK;
- }
- /**
- * Notify client about a change in the list of addresses this peer
- * has.
- *
- * @param ac address class of the entry in the list that changed
- * @param ch client to contact
- * @param add #GNUNET_YES to add, #GNUNET_NO to remove
- * @param addr the address that changed
- * @param addr_len number of bytes in @a addr
- */
- static void
- notify_client (enum GNUNET_NAT_AddressClass ac,
- struct ClientHandle *ch,
- int add,
- const void *addr,
- size_t addr_len)
- {
- struct GNUNET_MQ_Envelope *env;
- struct GNUNET_NAT_AddressChangeNotificationMessage *msg;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Notifying client about %s of IP %s\n",
- add ? "addition" : "removal",
- GNUNET_a2s (addr,
- addr_len));
- env = GNUNET_MQ_msg_extra (msg,
- addr_len,
- GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE);
- msg->add_remove = htonl (add);
- msg->addr_class = htonl (ac);
- GNUNET_memcpy (&msg[1],
- addr,
- addr_len);
- GNUNET_MQ_send (ch->mq,
- env);
- }
- /**
- * Check if we should bother to notify this client about this
- * address change, and if so, do it.
- *
- * @param delta the entry in the list that changed
- * @param ch client to check
- * @param add #GNUNET_YES to add, #GNUNET_NO to remove
- */
- static void
- check_notify_client (struct LocalAddressList *delta,
- struct ClientHandle *ch,
- int add)
- {
- size_t alen;
- struct sockaddr_in v4;
- struct sockaddr_in6 v6;
- if (0 == (ch->flags & GNUNET_NAT_RF_ADDRESSES))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Not notifying client as it does not care about addresses\n");
- return;
- }
- switch (delta->af)
- {
- case AF_INET:
- alen = sizeof (struct sockaddr_in);
- GNUNET_memcpy (&v4,
- &delta->addr,
- alen);
- /* Check for client notifications */
- for (unsigned int i=0;i<ch->num_caddrs;i++)
- {
- const struct sockaddr_in *c4;
- if (AF_INET != ch->caddrs[i].ss.ss_family)
- continue; /* IPv4 not relevant */
- c4 = (const struct sockaddr_in *) &ch->caddrs[i].ss;
- if ( match_ipv4 ("127.0.0.1", &c4->sin_addr, 8) &&
- (0 != c4->sin_addr.s_addr) &&
- (! match_ipv4 ("127.0.0.1", &v4.sin_addr, 8)) )
- continue; /* bound to loopback, but this is not loopback */
- if ( (! match_ipv4 ("127.0.0.1", &c4->sin_addr, 8) ) &&
- match_ipv4 ("127.0.0.1", &v4.sin_addr, 8) )
- continue; /* bound to non-loopback, but this is loopback */
- if ( (0 != (delta->ac & GNUNET_NAT_AC_EXTERN)) &&
- (0 != c4->sin_addr.s_addr) &&
- (! is_nat_v4 (&v4.sin_addr)) )
- continue; /* based on external-IP, but this IP is not
- from private address range. */
- if ( (0 != memcmp (&v4.sin_addr,
- &c4->sin_addr,
- sizeof (struct in_addr))) &&
- (0 != c4->sin_addr.s_addr) &&
- (! is_nat_v4 (&c4->sin_addr)) )
- continue; /* this IP is not from private address range,
- and IP does not match. */
- /* OK, IP seems relevant, notify client */
- if (0 == htons (v4.sin_port))
- v4.sin_port = c4->sin_port;
- notify_client (delta->ac,
- ch,
- add,
- &v4,
- alen);
- }
- break;
- case AF_INET6:
- alen = sizeof (struct sockaddr_in6);
- GNUNET_memcpy (&v6,
- &delta->addr,
- alen);
- for (unsigned int i=0;i<ch->num_caddrs;i++)
- {
- const struct sockaddr_in6 *c6;
- if (AF_INET6 != ch->caddrs[i].ss.ss_family)
- continue; /* IPv4 not relevant */
- c6 = (const struct sockaddr_in6 *) &ch->caddrs[i].ss;
- if ( match_ipv6 ("::1", &c6->sin6_addr, 128) &&
- (0 != memcmp (&c6->sin6_addr,
- &in6addr_any,
- sizeof (struct in6_addr))) &&
- (! match_ipv6 ("::1", &v6.sin6_addr, 128)) )
- continue; /* bound to loopback, but this is not loopback */
- if ( (! match_ipv6 ("::1", &c6->sin6_addr, 128) ) &&
- match_ipv6 ("::1", &v6.sin6_addr, 128) )
- continue; /* bound to non-loopback, but this is loopback */
- if ( (0 != (delta->ac & GNUNET_NAT_AC_EXTERN)) &&
- (0 != memcmp (&c6->sin6_addr,
- &in6addr_any,
- sizeof (struct in6_addr))) &&
- (! is_nat_v6 (&v6.sin6_addr)) )
- continue; /* based on external-IP, but this IP is not
- from private address range. */
- if ( (0 != memcmp (&v6.sin6_addr,
- &c6->sin6_addr,
- sizeof (struct in6_addr))) &&
- (0 != memcmp (&c6->sin6_addr,
- &in6addr_any,
- sizeof (struct in6_addr))) &&
- (! is_nat_v6 (&c6->sin6_addr)) )
- continue; /* this IP is not from private address range,
- and IP does not match. */
- if ( (match_ipv6 ("fe80::", &c6->sin6_addr, 10)) &&
- (0 != memcmp (&c6->sin6_addr,
- &in6addr_any,
- sizeof (struct in6_addr))) &&
- (0 != memcmp (&v6.sin6_addr,
- &c6->sin6_addr,
- sizeof (struct in6_addr))) &&
- (0 == (delta->ac & GNUNET_NAT_AC_EXTERN)) )
- continue; /* client bound to link-local, and the other address
- does not match and is not an external IP */
- /* OK, IP seems relevant, notify client */
- if (0 == htons (v6.sin6_port))
- v6.sin6_port = c6->sin6_port;
- notify_client (delta->ac,
- ch,
- add,
- &v6,
- alen);
- }
- break;
- default:
- GNUNET_break (0);
- return;
- }
- }
- /**
- * Notify all clients about a change in the list
- * of addresses this peer has.
- *
- * @param delta the entry in the list that changed
- * @param add #GNUNET_YES to add, #GNUNET_NO to remove
- */
- static void
- notify_clients (struct LocalAddressList *delta,
- int add)
- {
- for (struct ClientHandle *ch = ch_head;
- NULL != ch;
- ch = ch->next)
- check_notify_client (delta,
- ch,
- add);
- }
- /**
- * Tell relevant client about a change in our external
- * IPv4 address.
- *
- * @param cls client to check if it cares and possibly notify
- * @param v4 the external address that changed
- * @param add #GNUNET_YES to add, #GNUNET_NO to remove
- */
- static void
- notify_client_external_ipv4_change (void *cls,
- const struct in_addr *v4,
- int add)
- {
- struct ClientHandle *ch = cls;
- struct sockaddr_in sa;
- int have_v4;
- /* (0) check if this impacts 'hole_external' */
- if ( (NULL != ch->hole_external) &&
- (0 == strcasecmp (ch->hole_external,
- "AUTO")) )
- {
- struct LocalAddressList lal;
- struct sockaddr_in *s4;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Detected eternal IP, can now back-fill AUTO:%u in hole punching specification of `%s'\n",
- (unsigned int) ch->ext_dns_port,
- ch->section_name);
- memset (&lal, 0, sizeof (lal));
- s4 = (struct sockaddr_in *) &lal.addr;
- s4->sin_family = AF_INET;
- s4->sin_port = htons (ch->ext_dns_port);
- s4->sin_addr = *v4;
- lal.af = AF_INET;
- lal.ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL;
- check_notify_client (&lal,
- ch,
- add);
- }
- /* (1) check if client cares. */
- if (! ch->natted_address)
- return;
- have_v4 = GNUNET_NO;
- for (unsigned int i=0;i<ch->num_caddrs;i++)
- {
- const struct sockaddr_storage *ss = &ch->caddrs[i].ss;
- if (AF_INET != ss->ss_family)
- continue;
- have_v4 = GNUNET_YES;
- break;
- }
- if (GNUNET_NO == have_v4)
- return; /* IPv6-only */
- /* (2) build address info */
- memset (&sa,
- 0,
- sizeof (sa));
- sa.sin_family = AF_INET;
- sa.sin_addr = *v4;
- sa.sin_port = htons (0);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Detected eternal IP %s, notifying client of external IP (without port)\n",
- GNUNET_a2s ((const struct sockaddr *) &sa,
- sizeof (sa)));
- /* (3) notify client of change */
- notify_client (is_nat_v4 (v4)
- ? GNUNET_NAT_AC_EXTERN | GNUNET_NAT_AC_LAN
- : GNUNET_NAT_AC_EXTERN | GNUNET_NAT_AC_GLOBAL,
- ch,
- add,
- &sa,
- sizeof (sa));
- }
- /**
- * We got a connection reversal request from another peer.
- * Notify applicable clients.
- *
- * @param cls closure with the `struct LocalAddressList`
- * @param ra IP address of the peer who wants us to connect to it
- */
- static void
- reversal_callback (void *cls,
- const struct sockaddr_in *ra)
- {
- struct LocalAddressList *lal = cls;
- const struct sockaddr_in *l4;
- GNUNET_assert (AF_INET == lal->af);
- l4 = (const struct sockaddr_in *) &lal->addr;
- for (struct ClientHandle *ch = ch_head;
- NULL != ch;
- ch = ch->next)
- {
- struct GNUNET_NAT_ConnectionReversalRequestedMessage *crrm;
- struct GNUNET_MQ_Envelope *env;
- int match;
- /* Check if client is in applicable range for ICMP NAT traversal
- for this local address */
- if (! ch->natted_address)
- continue;
- match = GNUNET_NO;
- for (unsigned int i=0;i<ch->num_caddrs;i++)
- {
- struct ClientAddress *ca = &ch->caddrs[i];
- const struct sockaddr_in *c4;
- if (AF_INET != ca->ss.ss_family)
- continue;
- c4 = (const struct sockaddr_in *) &ca->ss;
- if ( (0 != c4->sin_addr.s_addr) &&
- (l4->sin_addr.s_addr != c4->sin_addr.s_addr) )
- continue;
- match = GNUNET_YES;
- break;
- }
- if (! match)
- continue;
- /* Notify applicable client about connection reversal request */
- env = GNUNET_MQ_msg_extra (crrm,
- sizeof (struct sockaddr_in),
- GNUNET_MESSAGE_TYPE_NAT_CONNECTION_REVERSAL_REQUESTED);
- GNUNET_memcpy (&crrm[1],
- ra,
- sizeof (struct sockaddr_in));
- GNUNET_MQ_send (ch->mq,
- env);
- }
- }
- /**
- * Task we run periodically to scan for network interfaces.
- *
- * @param cls NULL
- */
- static void
- run_scan (void *cls)
- {
- struct IfcProcContext ifc_ctx;
- int found;
- int have_nat;
- struct LocalAddressList *lnext;
- scan_task = GNUNET_SCHEDULER_add_delayed (SCAN_FREQ,
- &run_scan,
- NULL);
- memset (&ifc_ctx,
- 0,
- sizeof (ifc_ctx));
- GNUNET_OS_network_interfaces_list (&ifc_proc,
- &ifc_ctx);
- /* remove addresses that disappeared */
- for (struct LocalAddressList *lal = lal_head;
- NULL != lal;
- lal = lnext)
- {
- lnext = lal->next;
- found = GNUNET_NO;
- for (struct LocalAddressList *pos = ifc_ctx.lal_head;
- NULL != pos;
- pos = pos->next)
- {
- if ( (pos->af == lal->af) &&
- (0 == memcmp (&lal->addr,
- &pos->addr,
- (AF_INET == lal->af)
- ? sizeof (struct sockaddr_in)
- : sizeof (struct sockaddr_in6))) )
- {
- found = GNUNET_YES;
- }
- }
- if (GNUNET_NO == found)
- {
- notify_clients (lal,
- GNUNET_NO);
- free_lal (lal);
- }
- }
- /* add addresses that appeared */
- have_nat = GNUNET_NO;
- for (struct LocalAddressList *pos = ifc_ctx.lal_head;
- NULL != pos;
- pos = ifc_ctx.lal_head)
- {
- found = GNUNET_NO;
- if (GNUNET_NAT_AC_LAN == (GNUNET_NAT_AC_LAN & pos->ac))
- have_nat = GNUNET_YES;
- for (struct LocalAddressList *lal = lal_head;
- NULL != lal;
- lal = lal->next)
- {
- if ( (pos->af == lal->af) &&
- (0 == memcmp (&lal->addr,
- &pos->addr,
- (AF_INET == lal->af)
- ? sizeof (struct sockaddr_in)
- : sizeof (struct sockaddr_in6))) )
- found = GNUNET_YES;
- }
- GNUNET_CONTAINER_DLL_remove (ifc_ctx.lal_head,
- ifc_ctx.lal_tail,
- pos);
- if (GNUNET_YES == found)
- {
- GNUNET_free (pos);
- }
- else
- {
- notify_clients (pos,
- GNUNET_YES);
- GNUNET_CONTAINER_DLL_insert (lal_head,
- lal_tail,
- pos);
- if ( (AF_INET == pos->af) &&
- (NULL == pos->hc) &&
- (0 != (GNUNET_NAT_AC_LAN & pos->ac)) )
- {
- const struct sockaddr_in *s4
- = (const struct sockaddr_in *) &pos->addr;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Found NATed local address %s, starting NAT server\n",
- GNUNET_a2s ((const struct sockaddr *) &pos->addr,
- sizeof (*s4)));
- pos->hc = GN_start_gnunet_nat_server_ (&s4->sin_addr,
- &reversal_callback,
- pos);
- }
- }
- }
- GN_nat_status_changed (have_nat);
- }
- /**
- * Function called whenever our set of external addresses
- * as created by `upnpc` changes.
- *
- * @param cls closure with our `struct ClientHandle *`
- * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean
- * the previous (now invalid) one, #GNUNET_SYSERR indicates an error
- * @param addr either the previous or the new public IP address
- * @param addrlen actual length of the @a addr
- * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
- */
- static void
- upnp_addr_change_cb (void *cls,
- int add_remove,
- const struct sockaddr *addr,
- socklen_t addrlen,
- enum GNUNET_NAT_StatusCode result)
- {
- struct ClientHandle *ch = cls;
- enum GNUNET_NAT_AddressClass ac;
- switch (result)
- {
- case GNUNET_NAT_ERROR_SUCCESS:
- GNUNET_assert (NULL != addr);
- break;
- case GNUNET_NAT_ERROR_UPNPC_FAILED:
- case GNUNET_NAT_ERROR_UPNPC_TIMEOUT:
- case GNUNET_NAT_ERROR_IPC_FAILURE:
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Running upnpc failed: %d\n",
- result);
- return;
- case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND:
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "external-ip binary not found\n");
- return;
- case GNUNET_NAT_ERROR_UPNPC_NOT_FOUND:
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "upnpc binary not found\n");
- return;
- case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED:
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "external-ip binary could not be run\n");
- return;
- case GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED:
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "upnpc failed to create port mapping\n");
- return;
- case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID:
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Invalid output from upnpc\n");
- return;
- case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID:
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Invalid address returned by upnpc\n");
- return;
- default:
- GNUNET_break (0); /* should not be possible */
- return;
- }
- switch (addr->sa_family)
- {
- case AF_INET:
- ac = is_nat_v4 (&((const struct sockaddr_in *) addr)->sin_addr)
- ? GNUNET_NAT_AC_LAN
- : GNUNET_NAT_AC_EXTERN;
- break;
- case AF_INET6:
- ac = is_nat_v6 (&((const struct sockaddr_in6 *) addr)->sin6_addr)
- ? GNUNET_NAT_AC_LAN
- : GNUNET_NAT_AC_EXTERN;
- break;
- default:
- GNUNET_break (0);
- return;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "upnp external address %s: %s\n",
- add_remove ? "added" : "removed",
- GNUNET_a2s (addr,
- addrlen));
- notify_client (ac,
- ch,
- add_remove,
- addr,
- addrlen);
- }
- /**
- * Resolve the `hole_external` name to figure out our
- * external address from a manually punched hole. The
- * port number has already been parsed, this task is
- * responsible for periodically doing a DNS lookup.
- *
- * @param ch client handle to act upon
- */
- static void
- dyndns_lookup (void *cls);
- /**
- * Our (external) hostname was resolved. Update lists of
- * current external IPs (note that DNS may return multiple
- * addresses!) and notify client accordingly.
- *
- * @param cls the `struct ClientHandle`
- * @param addr NULL on error, otherwise result of DNS lookup
- * @param addrlen number of bytes in @a addr
- */
- static void
- process_external_ip (void *cls,
- const struct sockaddr *addr,
- socklen_t addrlen)
- {
- struct ClientHandle *ch = cls;
- struct LocalAddressList *lal;
- struct sockaddr_storage ss;
- struct sockaddr_in *v4;
- struct sockaddr_in6 *v6;
- if (NULL == addr)
- {
- struct LocalAddressList *laln;
- ch->ext_dns = NULL;
- ch->ext_dns_task
- = GNUNET_SCHEDULER_add_delayed (dyndns_frequency,
- &dyndns_lookup,
- ch);
- /* Current iteration is over, remove 'old' IPs now */
- for (lal = ch->ext_addr_head; NULL != lal; lal = laln)
- {
- laln = lal->next;
- if (GNUNET_YES == lal->old)
- {
- GNUNET_CONTAINER_DLL_remove (ch->ext_addr_head,
- ch->ext_addr_tail,
- lal);
- check_notify_client (lal,
- ch,
- GNUNET_NO);
- GNUNET_free (lal);
- }
- }
- return;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got IP `%s' for external address `%s'\n",
- GNUNET_a2s (addr,
- addrlen),
- ch->hole_external);
- /* build sockaddr storage with port number */
- memset (&ss,
- 0,
- sizeof (ss));
- GNUNET_memcpy (&ss,
- addr,
- addrlen);
- switch (addr->sa_family)
- {
- case AF_INET:
- v4 = (struct sockaddr_in *) &ss;
- v4->sin_port = htons (ch->ext_dns_port);
- break;
- case AF_INET6:
- v6 = (struct sockaddr_in6 *) &ss;
- v6->sin6_port = htons (ch->ext_dns_port);
- break;
- default:
- GNUNET_break (0);
- return;
- }
- /* See if 'ss' matches any of our known addresses */
- for (lal = ch->ext_addr_head; NULL != lal; lal = lal->next)
- {
- if (GNUNET_NO == lal->old)
- continue; /* already processed, skip */
- if ( (addr->sa_family == lal->addr.ss_family) &&
- (0 == memcmp (&ss,
- &lal->addr,
- addrlen)) )
- {
- /* Address unchanged, remember so we do not remove */
- lal->old = GNUNET_NO;
- return; /* done here */
- }
- }
- /* notify client, and remember IP for later removal! */
- lal = GNUNET_new (struct LocalAddressList);
- lal->addr = ss;
- lal->af = ss.ss_family;
- lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL;
- GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head,
- ch->ext_addr_tail,
- lal);
- check_notify_client (lal,
- ch,
- GNUNET_YES);
- }
- /**
- * Resolve the `hole_external` name to figure out our
- * external address from a manually punched hole. The
- * port number has already been parsed, this task is
- * responsible for periodically doing a DNS lookup.
- *
- * @param ch client handle to act upon
- */
- static void
- dyndns_lookup (void *cls)
- {
- struct ClientHandle *ch = cls;
- struct LocalAddressList *lal;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Performing DNS lookup for punched hole given for `%s' as `%s:%u'\n",
- ch->section_name,
- ch->hole_external,
- (unsigned int) ch->ext_dns_port);
- for (lal = ch->ext_addr_head; NULL != lal; lal = lal->next)
- lal->old = GNUNET_YES;
- ch->ext_dns_task = NULL;
- ch->ext_dns = GNUNET_RESOLVER_ip_get (ch->hole_external,
- AF_UNSPEC,
- GNUNET_TIME_UNIT_MINUTES,
- &process_external_ip,
- ch);
- }
- /**
- * Resolve the `hole_external` name to figure out our
- * external address from a manually punched hole. The
- * given name may be "AUTO" in which case we should use
- * the IP address(es) we have from upnpc or other methods.
- * The name can also be an IP address, in which case we
- * do not need to do DNS resolution. Finally, we also
- * need to parse the port number.
- *
- * @param ch client handle to act upon
- */
- static void
- lookup_hole_external (struct ClientHandle *ch)
- {
- char *port;
- unsigned int pnum;
- struct sockaddr_in *s4;
- struct LocalAddressList *lal;
- port = strrchr (ch->hole_external, ':');
- if (NULL == port)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- _("Malformed punched hole specification `%s' (lacks port)\n"),
- ch->hole_external);
- return;
- }
- if ( (1 != sscanf (port + 1,
- "%u",
- &pnum)) ||
- (pnum > 65535) )
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- _("Invalid port number in punched hole specification `%s' (lacks port)\n"),
- port + 1);
- return;
- }
- ch->ext_dns_port = (uint16_t) pnum;
- *port = '\0';
- lal = GNUNET_new (struct LocalAddressList);
- if ('[' == *ch->hole_external)
- {
- struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &lal->addr;
- s6->sin6_family = AF_INET6;
- if (']' != (ch->hole_external[strlen(ch->hole_external)-1]))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- _("Malformed punched hole specification `%s' (lacks `]')\n"),
- ch->hole_external);
- GNUNET_free (lal);
- return;
- }
- ch->hole_external[strlen(ch->hole_external)-1] = '\0';
- if (1 != inet_pton (AF_INET6,
- ch->hole_external + 1,
- &s6->sin6_addr))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- _("Malformed punched hole specification `%s' (IPv6 address invalid)"),
- ch->hole_external + 1);
- GNUNET_free (lal);
- return;
- }
- s6->sin6_port = htons (ch->ext_dns_port);
- lal->af = AF_INET6;
- lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL;
- GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head,
- ch->ext_addr_tail,
- lal);
- check_notify_client (lal,
- ch,
- GNUNET_YES);
- return;
- }
- s4 = (struct sockaddr_in *) &lal->addr;
- s4->sin_family = AF_INET;
- if (1 == inet_pton (AF_INET,
- ch->hole_external,
- &s4->sin_addr))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "IPv4 punched hole given for `%s' via `%s:%u'\n",
- ch->section_name,
- ch->hole_external,
- (unsigned int) ch->ext_dns_port);
- s4->sin_port = htons (ch->ext_dns_port);
- lal->af = AF_INET;
- lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL;
- GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head,
- ch->ext_addr_tail,
- lal);
- check_notify_client (lal,
- ch,
- GNUNET_YES);
- return;
- }
- if (0 == strcasecmp (ch->hole_external,
- "AUTO"))
- {
- /* handled in #notify_client_external_ipv4_change() */
- GNUNET_free (lal);
- return;
- }
- /* got a DNS name, trigger lookup! */
- GNUNET_free (lal);
- ch->ext_dns_task
- = GNUNET_SCHEDULER_add_now (&dyndns_lookup,
- ch);
- }
- /**
- * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client.
- * We remember the client for updates upon future NAT events.
- *
- * @param cls client who sent the message
- * @param message the message received
- */
- static void
- handle_register (void *cls,
- const struct GNUNET_NAT_RegisterMessage *message)
- {
- struct ClientHandle *ch = cls;
- const char *off;
- size_t left;
- if ( (0 != ch->proto) ||
- (NULL != ch->caddrs) )
- {
- /* double registration not allowed */
- GNUNET_break (0);
- GNUNET_SERVICE_client_drop (ch->client);
- return;
- }
- ch->flags = message->flags;
- ch->proto = message->proto;
- ch->num_caddrs = ntohs (message->num_addrs);
- ch->caddrs = GNUNET_new_array (ch->num_caddrs,
- struct ClientAddress);
- left = ntohs (message->header.size) - sizeof (*message);
- off = (const char *) &message[1];
- for (unsigned int i=0;i<ch->num_caddrs;i++)
- {
- const struct sockaddr *sa = (const struct sockaddr *) off;
- size_t alen;
- uint16_t port;
- int is_nat;
- if (sizeof (sa_family_t) > left)
- {
- GNUNET_break (0);
- GNUNET_SERVICE_client_drop (ch->client);
- return;
- }
- is_nat = GNUNET_NO;
- switch (sa->sa_family)
- {
- case AF_INET:
- {
- struct sockaddr_in s4;
- GNUNET_memcpy (&s4,
- off,
- sizeof (struct sockaddr_in));
- alen = sizeof (struct sockaddr_in);
- if (is_nat_v4 (&s4.sin_addr))
- is_nat = GNUNET_YES;
- port = ntohs (s4.sin_port);
- }
- break;
- case AF_INET6:
- {
- struct sockaddr_in6 s6;
- GNUNET_memcpy (&s6,
- off,
- sizeof (struct sockaddr_in6));
- alen = sizeof (struct sockaddr_in6);
- if (is_nat_v6 (&s6.sin6_addr))
- is_nat = GNUNET_YES;
- port = ntohs (s6.sin6_port);
- }
- break;
- #if AF_UNIX
- case AF_UNIX:
- alen = sizeof (struct sockaddr_un);
- port = 0;
- break;
- #endif
- default:
- GNUNET_break (0);
- GNUNET_SERVICE_client_drop (ch->client);
- return;
- }
- /* store address */
- GNUNET_assert (alen <= left);
- GNUNET_assert (alen <= sizeof (struct sockaddr_storage));
- GNUNET_memcpy (&ch->caddrs[i].ss,
- off,
- alen);
- /* If applicable, try UPNPC NAT punching */
- if ( (is_nat) &&
- (enable_upnp) &&
- ( (IPPROTO_TCP == ch->proto) ||
- (IPPROTO_UDP == ch->proto) ) )
- {
- ch->natted_address = GNUNET_YES;
- ch->caddrs[i].mh
- = GNUNET_NAT_mini_map_start (port,
- IPPROTO_TCP == ch->proto,
- &upnp_addr_change_cb,
- ch);
- }
- off += alen;
- }
- ch->section_name
- = GNUNET_strndup (off,
- ntohs (message->str_len));
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Received REGISTER message from client for subsystem `%s'\n",
- ch->section_name);
- if (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_string (cfg,
- ch->section_name,
- "HOLE_EXTERNAL",
- &ch->hole_external))
- lookup_hole_external (ch);
- /* Actually send IP address list to client */
- for (struct LocalAddressList *lal = lal_head;
- NULL != lal;
- lal = lal->next)
- {
- check_notify_client (lal,
- ch,
- GNUNET_YES);
- }
- /* Also consider IPv4 determined by `external-ip` */
- ch->external_monitor
- = GN_external_ipv4_monitor_start (¬ify_client_external_ipv4_change,
- ch);
- GNUNET_SERVICE_client_continue (ch->client);
- }
- /**
- * Check validity of #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN message from
- * client.
- *
- * @param cls client who sent the message
- * @param message the message received
- * @return #GNUNET_OK if message is well-formed
- */
- static int
- check_stun (void *cls,
- const struct GNUNET_NAT_HandleStunMessage *message)
- {
- size_t sa_len = ntohs (message->sender_addr_size);
- size_t expect = sa_len + ntohs (message->payload_size);
- if (ntohs (message->header.size) - sizeof (*message) != expect)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (sa_len < sizeof (sa_family_t))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
- }
- /**
- * Notify all clients about our external IP address
- * as reported by the STUN server.
- *
- * @param ip the external IP
- * @param add #GNUNET_YES to add, #GNUNET_NO to remove
- */
- static void
- notify_clients_stun_change (const struct sockaddr_in *ip,
- int add)
- {
- for (struct ClientHandle *ch = ch_head;
- NULL != ch;
- ch = ch->next)
- {
- struct sockaddr_in v4;
- struct GNUNET_NAT_AddressChangeNotificationMessage *msg;
- struct GNUNET_MQ_Envelope *env;
- if (! ch->natted_address)
- continue;
- v4 = *ip;
- v4.sin_port = htons (0);
- env = GNUNET_MQ_msg_extra (msg,
- sizeof (v4),
- GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE);
- msg->add_remove = htonl ((int32_t) add);
- msg->addr_class = htonl (GNUNET_NAT_AC_EXTERN |
- GNUNET_NAT_AC_GLOBAL);
- GNUNET_memcpy (&msg[1],
- &v4,
- sizeof (v4));
- GNUNET_MQ_send (ch->mq,
- env);
- }
- }
- /**
- * Function to be called when we decide that an
- * external IP address as told to us by a STUN
- * server has gone stale.
- *
- * @param cls the `struct StunExternalIP` to drop
- */
- static void
- stun_ip_timeout (void *cls)
- {
- struct StunExternalIP *se = cls;
- se->timeout_task = NULL;
- notify_clients_stun_change (&se->external_addr,
- GNUNET_NO);
- GNUNET_CONTAINER_DLL_remove (se_head,
- se_tail,
- se);
- GNUNET_free (se);
- }
- /**
- * Handler for #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN message from
- * client.
- *
- * @param cls client who sent the message
- * @param message the message received
- */
- static void
- handle_stun (void *cls,
- const struct GNUNET_NAT_HandleStunMessage *message)
- {
- struct ClientHandle *ch = cls;
- const char *buf = (const char *) &message[1];
- const struct sockaddr *sa;
- const void *payload;
- size_t sa_len;
- size_t payload_size;
- struct sockaddr_in external_addr;
- sa_len = ntohs (message->sender_addr_size);
- payload_size = ntohs (message->payload_size);
- sa = (const struct sockaddr *) &buf[0];
- payload = (const struct sockaddr *) &buf[sa_len];
- switch (sa->sa_family)
- {
- case AF_INET:
- if (sa_len != sizeof (struct sockaddr_in))
- {
- GNUNET_break (0);
- GNUNET_SERVICE_client_drop (ch->client);
- return;
- }
- break;
- case AF_INET6:
- if (sa_len != sizeof (struct sockaddr_in6))
- {
- GNUNET_break (0);
- GNUNET_SERVICE_client_drop (ch->client);
- return;
- }
- break;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Received HANDLE_STUN message from client\n");
- if (GNUNET_OK ==
- GNUNET_NAT_stun_handle_packet_ (payload,
- payload_size,
- &external_addr))
- {
- /* We now know that a server at "sa" claims that
- we are visible at IP "external_addr".
- We should (for some fixed period of time) tell
- all of our clients that listen to a NAT'ed address
- that they might want to consider the given 'external_ip'
- as their public IP address (this includes TCP and UDP
- clients, even if only UDP sends STUN requests).
- If we do not get a renewal, the "external_addr" should be
- removed again. The timeout frequency should be configurable
- (with a sane default), so that the UDP plugin can tell how
- often to re-request STUN.
- */
- struct StunExternalIP *se;
- /* Check if we had a prior response from this STUN server */
- for (se = se_head; NULL != se; se = se->next)
- {
- if ( (se->stun_server_addr_len != sa_len) ||
- (0 != memcmp (sa,
- &se->stun_server_addr,
- sa_len)) )
- continue; /* different STUN server */
- if (0 != memcmp (&external_addr,
- &se->external_addr,
- sizeof (struct sockaddr_in)))
- {
- /* external IP changed, update! */
- notify_clients_stun_change (&se->external_addr,
- GNUNET_NO);
- se->external_addr = external_addr;
- notify_clients_stun_change (&se->external_addr,
- GNUNET_YES);
- }
- /* update timeout */
- GNUNET_SCHEDULER_cancel (se->timeout_task);
- se->timeout_task
- = GNUNET_SCHEDULER_add_delayed (stun_stale_timeout,
- &stun_ip_timeout,
- se);
- return;
- }
- /* STUN server is completely new, create fresh entry */
- se = GNUNET_new (struct StunExternalIP);
- se->external_addr = external_addr;
- GNUNET_memcpy (&se->stun_server_addr,
- sa,
- sa_len);
- se->stun_server_addr_len = sa_len;
- se->timeout_task = GNUNET_SCHEDULER_add_delayed (stun_stale_timeout,
- &stun_ip_timeout,
- se);
- GNUNET_CONTAINER_DLL_insert (se_head,
- se_tail,
- se);
- notify_clients_stun_change (&se->external_addr,
- GNUNET_NO);
- }
- GNUNET_SERVICE_client_continue (ch->client);
- }
- /**
- * Check validity of
- * #GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL message from
- * client.
- *
- * @param cls client who sent the message
- * @param message the message received
- * @return #GNUNET_OK if message is well-formed
- */
- static int
- check_request_connection_reversal (void *cls,
- const struct GNUNET_NAT_RequestConnectionReversalMessage *message)
- {
- size_t expect;
- expect = ntohs (message->local_addr_size)
- + ntohs (message->remote_addr_size);
- if (ntohs (message->header.size) - sizeof (*message) != expect)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
- }
- /**
- * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL
- * message from client.
- *
- * @param cls client who sent the message
- * @param message the message received
- */
- static void
- handle_request_connection_reversal (void *cls,
- const struct GNUNET_NAT_RequestConnectionReversalMessage *message)
- {
- struct ClientHandle *ch = cls;
- const char *buf = (const char *) &message[1];
- size_t local_sa_len = ntohs (message->local_addr_size);
- size_t remote_sa_len = ntohs (message->remote_addr_size);
- struct sockaddr_in l4;
- struct sockaddr_in r4;
- int ret;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Received REQUEST CONNECTION REVERSAL message from client\n");
- if (local_sa_len != sizeof (struct sockaddr_in))
- {
- GNUNET_break_op (0);
- GNUNET_SERVICE_client_drop (ch->client);
- return;
- }
- if (remote_sa_len != sizeof (struct sockaddr_in))
- {
- GNUNET_break_op (0);
- GNUNET_SERVICE_client_drop (ch->client);
- return;
- }
- GNUNET_memcpy (&l4,
- buf,
- sizeof (struct sockaddr_in));
- GNUNET_break_op (AF_INET == l4.sin_family);
- buf += sizeof (struct sockaddr_in);
- GNUNET_memcpy (&r4,
- buf,
- sizeof (struct sockaddr_in));
- GNUNET_break_op (AF_INET == r4.sin_family);
- ret = GN_request_connection_reversal (&l4.sin_addr,
- ntohs (l4.sin_port),
- &r4.sin_addr);
- if (GNUNET_OK != ret)
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- _("Connection reversal request failed\n"));
- GNUNET_SERVICE_client_continue (ch->client);
- }
- /**
- * Task run during shutdown.
- *
- * @param cls unused
- */
- static void
- shutdown_task (void *cls)
- {
- struct StunExternalIP *se;
- while (NULL != (se = se_head))
- {
- GNUNET_CONTAINER_DLL_remove (se_head,
- se_tail,
- se);
- GNUNET_SCHEDULER_cancel (se->timeout_task);
- GNUNET_free (se);
- }
- GN_nat_status_changed (GNUNET_NO);
- if (NULL != scan_task)
- {
- GNUNET_SCHEDULER_cancel (scan_task);
- scan_task = NULL;
- }
- if (NULL != stats)
- {
- GNUNET_STATISTICS_destroy (stats,
- GNUNET_NO);
- stats = NULL;
- }
- destroy_lal ();
- }
- /**
- * Setup NAT service.
- *
- * @param cls closure
- * @param c configuration to use
- * @param service the initialized service
- */
- static void
- run (void *cls,
- const struct GNUNET_CONFIGURATION_Handle *c,
- struct GNUNET_SERVICE_Handle *service)
- {
- cfg = c;
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (cfg,
- "NAT",
- "STUN_STALE",
- &stun_stale_timeout))
- stun_stale_timeout = GNUNET_TIME_UNIT_HOURS;
- /* Check for UPnP */
- enable_upnp
- = GNUNET_CONFIGURATION_get_value_yesno (cfg,
- "NAT",
- "ENABLE_UPNP");
- if (GNUNET_YES == enable_upnp)
- {
- /* check if it works */
- if (GNUNET_SYSERR ==
- GNUNET_OS_check_helper_binary ("upnpc",
- GNUNET_NO,
- NULL))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("UPnP enabled in configuration, but UPnP client `upnpc` command not found, disabling UPnP\n"));
- enable_upnp = GNUNET_SYSERR;
- }
- }
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (cfg,
- "nat",
- "DYNDNS_FREQUENCY",
- &dyndns_frequency))
- dyndns_frequency = DYNDNS_FREQUENCY;
- GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
- NULL);
- stats = GNUNET_STATISTICS_create ("nat",
- cfg);
- scan_task = GNUNET_SCHEDULER_add_now (&run_scan,
- NULL);
- }
- /**
- * Callback called when a client connects to the service.
- *
- * @param cls closure for the service
- * @param c the new client that connected to the service
- * @param mq the message queue used to send messages to the client
- * @return a `struct ClientHandle`
- */
- static void *
- client_connect_cb (void *cls,
- struct GNUNET_SERVICE_Client *c,
- struct GNUNET_MQ_Handle *mq)
- {
- struct ClientHandle *ch;
- ch = GNUNET_new (struct ClientHandle);
- ch->mq = mq;
- ch->client = c;
- GNUNET_CONTAINER_DLL_insert (ch_head,
- ch_tail,
- ch);
- return ch;
- }
- /**
- * Callback called when a client disconnected from the service
- *
- * @param cls closure for the service
- * @param c the client that disconnected
- * @param internal_cls a `struct ClientHandle *`
- */
- static void
- client_disconnect_cb (void *cls,
- struct GNUNET_SERVICE_Client *c,
- void *internal_cls)
- {
- struct ClientHandle *ch = internal_cls;
- struct LocalAddressList *lal;
- GNUNET_CONTAINER_DLL_remove (ch_head,
- ch_tail,
- ch);
- for (unsigned int i=0;i<ch->num_caddrs;i++)
- {
- if (NULL != ch->caddrs[i].mh)
- {
- GNUNET_NAT_mini_map_stop (ch->caddrs[i].mh);
- ch->caddrs[i].mh = NULL;
- }
- }
- GNUNET_free_non_null (ch->caddrs);
- while (NULL != (lal = ch->ext_addr_head))
- {
- GNUNET_CONTAINER_DLL_remove (ch->ext_addr_head,
- ch->ext_addr_tail,
- lal);
- GNUNET_free (lal);
- }
- if (NULL != ch->ext_dns_task)
- {
- GNUNET_SCHEDULER_cancel (ch->ext_dns_task);
- ch->ext_dns_task = NULL;
- }
- if (NULL != ch->external_monitor)
- {
- GN_external_ipv4_monitor_stop (ch->external_monitor);
- ch->external_monitor = NULL;
- }
- if (NULL != ch->ext_dns)
- {
- GNUNET_RESOLVER_request_cancel (ch->ext_dns);
- ch->ext_dns = NULL;
- }
- GNUNET_free_non_null (ch->hole_external);
- GNUNET_free_non_null (ch->section_name);
- GNUNET_free (ch);
- }
- /**
- * Define "main" method using service macro.
- */
- GNUNET_SERVICE_MAIN
- ("nat",
- GNUNET_SERVICE_OPTION_NONE,
- &run,
- &client_connect_cb,
- &client_disconnect_cb,
- NULL,
- GNUNET_MQ_hd_var_size (register,
- GNUNET_MESSAGE_TYPE_NAT_REGISTER,
- struct GNUNET_NAT_RegisterMessage,
- NULL),
- GNUNET_MQ_hd_var_size (stun,
- GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN,
- struct GNUNET_NAT_HandleStunMessage,
- NULL),
- GNUNET_MQ_hd_var_size (request_connection_reversal,
- GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL,
- struct GNUNET_NAT_RequestConnectionReversalMessage,
- NULL),
- GNUNET_MQ_handler_end ());
- #if defined(LINUX) && defined(__GLIBC__)
- #include <malloc.h>
- /**
- * MINIMIZE heap size (way below 128k) since this process doesn't need much.
- */
- void __attribute__ ((constructor))
- GNUNET_ARM_memory_init ()
- {
- mallopt (M_TRIM_THRESHOLD, 4 * 1024);
- mallopt (M_TOP_PAD, 1 * 1024);
- malloc_trim (0);
- }
- #endif
- /* end of gnunet-service-nat.c */
|