From: Christian Grothoff Date: Fri, 6 Jan 2017 12:26:38 +0000 (+0100) Subject: separate service for autoconfiguration from NAT traversal X-Git-Tag: taler-0.2.1~523 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=80e1315b56f559db60499f5373e90c293c5ab065;p=oweals%2Fgnunet.git separate service for autoconfiguration from NAT traversal --- diff --git a/configure.ac b/configure.ac index c41bcc4b5..dade7d211 100644 --- a/configure.ac +++ b/configure.ac @@ -1596,6 +1596,8 @@ src/namestore/Makefile src/namestore/namestore.conf src/nat/Makefile src/nat/nat.conf +src/nat-auto/Makefile +src/nat-auto/nat-auto.conf src/nse/Makefile src/nse/nse.conf src/peerinfo/Makefile diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am index ff1e462b7..c40363b59 100644 --- a/doc/man/Makefile.am +++ b/doc/man/Makefile.am @@ -25,6 +25,7 @@ man_MANS = \ gnunet-namestore.1 \ gnunet-namestore-fcfsd.1 \ gnunet-nat.1 \ + gnunet-nat-auto.1 \ gnunet-nat-server.1 \ gnunet-peerinfo.1 \ gnunet-publish.1 \ diff --git a/doc/man/gnunet-nat-auto.1 b/doc/man/gnunet-nat-auto.1 new file mode 100644 index 000000000..310f14d43 --- /dev/null +++ b/doc/man/gnunet-nat-auto.1 @@ -0,0 +1,59 @@ +.TH GNUNET\-NAT\-AUTO 1 "6 Jan 2017" "GNUnet" + +.SH NAME +gnunet\-nat\-auto \- autoconfigure and test NAT traversal + +.SH SYNOPSIS +.B gnunet\-nat\-auto +.RI [ options ] +.br + +.SH DESCRIPTION + +This tool allows testing various NAT traversal functions, as well +as attempting auto\-configuration. + +.SH OPTIONS +.B +.IP "\-a, \-\-auto" +Attempt auto\-configuration for NAT traversal. + +.B +.IP "\-b ADDRESS, \-\-bind=ADDRESS" +Assume that the service is (locally) bound to ADDRESS. + +.B +.IP "\-c FILENAME, \-\-config=FILENAME" +Use the configuration file FILENAME. + +.B +.IP "\-e ADDRESS, \-\-external=ADDRESS" +Assume that ADDRESS is the globally visible address of the peer. + +.IP "\-t, \-\-tcp" +Use TCP. + +.B +.IP "\-u, \-\-udp" +Use UDP. + +.B +.IP "\-w, \-\-write" +Write configuration to configuration file, useful in combination with autoconfiguration (\-a). + +.SH EXAMPLES +.PP + +\fBAutomatic configuration:\fR + +Probe and write result to configuration: + + # gnunet\-nat\-auto \-aw + + +.SH BUGS +Report bugs by using Mantis or by sending electronic mail to + +.SH SEE ALSO +gnunet\-transport(1) gnunet\-nat(1) + diff --git a/doc/man/gnunet-nat.1 b/doc/man/gnunet-nat.1 index 01e0267a4..a8a82883b 100644 --- a/doc/man/gnunet-nat.1 +++ b/doc/man/gnunet-nat.1 @@ -14,9 +14,6 @@ This tool allows testing various NAT traversal functions, as well as attempting auto\-configuration. .SH OPTIONS -.B -.IP "\-a, \-\-auto" -Attempt auto\-configuration for NAT traversal. .B .IP "\-b ADDRESS, \-\-bind=ADDRESS" @@ -54,10 +51,6 @@ Use TCP. .IP "\-u, \-\-udp" Use UDP. -.B -.IP "\-w, \-\-write" -Write configuration to configuration file, useful in combination with autoconfiguration (\-a). - .B .IP "\-W, \-\-watch" Watch for connection reversal requests. @@ -103,12 +96,6 @@ XXX: # gnunet-nat FIXME -s -\fBAutomatic configuration:\fR - -Probe and write result to configuration: - - # gnunet-nat -aw - .SH BUGS Report bugs by using Mantis or by sending electronic mail to diff --git a/po/POTFILES.in b/po/POTFILES.in index 56af200ff..e1c3ec697 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -237,19 +237,23 @@ src/namestore/plugin_namestore_flat.c src/namestore/plugin_namestore_postgres.c src/namestore/plugin_namestore_sqlite.c src/namestore/plugin_rest_namestore.c +src/nat-auto/gnunet-nat-auto.c +src/nat-auto/gnunet-nat-server.c +src/nat-auto/gnunet-service-nat-auto.c +src/nat-auto/nat_auto_api.c +src/nat-auto/nat_auto_api_test.c src/nat/gnunet-helper-nat-client.c src/nat/gnunet-helper-nat-client-windows.c src/nat/gnunet-helper-nat-server.c src/nat/gnunet-helper-nat-server-windows.c src/nat/gnunet-nat.c -src/nat/gnunet-nat-server.c src/nat/gnunet-service-nat.c +src/nat/gnunet-service-nat_externalip.c src/nat/gnunet-service-nat_helper.c src/nat/gnunet-service-nat_mini.c src/nat/gnunet-service-nat_stun.c src/nat/nat_api.c src/nat/nat_api_stun.c -src/nat/nat_api_test.c src/nat/nat_auto.c src/nat/nat.c src/nat/nat_mini.c diff --git a/src/Makefile.am b/src/Makefile.am index 6925788d5..d04cba9dd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -85,6 +85,7 @@ SUBDIRS = \ template \ ats \ nat \ + nat-auto \ fragmentation \ transport \ ats-tool \ diff --git a/src/include/Makefile.am b/src/include/Makefile.am index f27fd6e36..639d3bfd7 100644 --- a/src/include/Makefile.am +++ b/src/include/Makefile.am @@ -79,6 +79,7 @@ gnunetinclude_HEADERS = \ gnunet_namestore_plugin.h \ gnunet_namestore_service.h \ gnunet_nat_lib.h \ + gnunet_nat_auto_service.h \ gnunet_nat_service.h \ gnunet_nc_lib.h \ gnunet_network_lib.h \ diff --git a/src/include/gnunet_nat_auto_service.h b/src/include/gnunet_nat_auto_service.h new file mode 100644 index 000000000..90115ff8c --- /dev/null +++ b/src/include/gnunet_nat_auto_service.h @@ -0,0 +1,224 @@ +/* + 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 General Public License as published + by the Free Software Foundation; either version 3, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * @author Christian Grothoff + * @author Milan Bouchet-Valat + * + * @file + * Service for testing and autoconfiguration of + * NAT traversal functionality + * + * @defgroup nat NAT testing library + * + * @{ + */ + +#ifndef GNUNET_NAT_AUTO_SERVICE_H +#define GNUNET_NAT_AUTO_SERVICE_H + +#include "gnunet_util_lib.h" + + +/** + * Handle to a NAT test. + */ +struct GNUNET_NAT_Test; + + +/** + * Function called to report success or failure for + * NAT configuration test. + * + * @param cls closure + * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code + */ +typedef void +(*GNUNET_NAT_TestCallback) (void *cls, + enum GNUNET_NAT_StatusCode result); + + +/** + * 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); + + +/** + * Handle to a request given to the resolver. Can be used to cancel + * the request prior to the timeout or successful execution. Also + * used to track our internal state for the request. + */ +struct GNUNET_NAT_STUN_Handle; + + +/** + * Make Generic STUN request. Sends a generic stun request to the + * server specified using the specified socket. If we do this, + * we need to watch for possible responses and call + * #GNUNET_NAT_stun_handle_packet() on incoming packets. + * + * @param server the address of the stun server + * @param port port of the stun server, in host byte order + * @param sock the socket used to send the request, must be a + * UDP socket + * @param cb callback in case of error + * @param cb_cls closure for @a cb + * @return NULL on error + */ +struct GNUNET_NAT_STUN_Handle * +GNUNET_NAT_stun_make_request (const char *server, + uint16_t port, + struct GNUNET_NETWORK_Handle *sock, + GNUNET_NAT_TestCallback cb, + void *cb_cls); + + +/** + * Cancel active STUN request. Frees associated resources + * and ensures that the callback is no longer invoked. + * + * @param rh request to cancel + */ +void +GNUNET_NAT_stun_make_request_cancel (struct GNUNET_NAT_STUN_Handle *rh); + + +/** + * Start testing if NAT traversal works using the given configuration + * (IPv4-only). The transport adapters should be down while using + * this function. + * + * @param cfg configuration for the NAT traversal + * @param proto protocol to test, i.e. IPPROTO_TCP or IPPROTO_UDP + * @param bind_ip IPv4 address to bind to + * @param bnd_port port to bind to, 0 to test connection reversal + * @param extern_ip IPv4 address to externally advertise + * @param extern_port externally advertised port to use + * @param report function to call with the result of the test + * @param report_cls closure for @a report + * @return handle to cancel NAT test + */ +struct GNUNET_NAT_Test * +GNUNET_NAT_test_start (const struct GNUNET_CONFIGURATION_Handle *cfg, + uint8_t proto, + struct in_addr bind_ip, + uint16_t bnd_port, + struct in_addr extern_ip, + uint16_t extern_port, + GNUNET_NAT_TestCallback report, + void *report_cls); + + +/** + * Stop an active NAT test. + * + * @param tst test to stop. + */ +void +GNUNET_NAT_test_stop (struct GNUNET_NAT_Test *tst); + + +/** + * Handle to auto-configuration in progress. + */ +struct GNUNET_NAT_AutoHandle; + + +/** + * Converts `enum GNUNET_NAT_StatusCode` to string + * + * @param err error code to resolve to a string + * @return point to a static string containing the error code + */ +const char * +GNUNET_NAT_status2string (enum GNUNET_NAT_StatusCode err); + + +/** + * Function called with the result from the autoconfiguration. + * + * @param cls closure + * @param diff minimal suggested changes to the original configuration + * to make it work (as best as we can) + * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code + * @param type what the situation of the NAT + */ +typedef void +(*GNUNET_NAT_AutoResultCallback)(void *cls, + const struct GNUNET_CONFIGURATION_Handle *diff, + enum GNUNET_NAT_StatusCode result, + enum GNUNET_NAT_Type type); + + +/** + * Start auto-configuration routine. The transport adapters should + * be stopped while this function is called. + * + * @param cfg initial configuration + * @param cb function to call with autoconfiguration result + * @param cb_cls closure for @a cb + * @return handle to cancel operation + */ +struct GNUNET_NAT_AutoHandle * +GNUNET_NAT_autoconfig_start (const struct GNUNET_CONFIGURATION_Handle *cfg, + GNUNET_NAT_AutoResultCallback cb, + void *cb_cls); + + +/** + * Abort autoconfiguration. + * + * @param ah handle for operation to abort + */ +void +GNUNET_NAT_autoconfig_cancel (struct GNUNET_NAT_AutoHandle *ah); + + +#endif + +/** @} */ /* end of group */ + +/* end of gnunet_nat_auto_service.h */ diff --git a/src/include/gnunet_nat_service.h b/src/include/gnunet_nat_service.h index 6b9650306..94f8a8555 100644 --- a/src/include/gnunet_nat_service.h +++ b/src/include/gnunet_nat_service.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2007-2016 GNUnet e.V. + 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 General Public License as published @@ -136,132 +136,6 @@ enum GNUNET_NAT_AddressClass }; -/** - * Signature of the callback passed to #GNUNET_NAT_register() for - * a function to call whenever our set of 'valid' addresses changes. - * - * @param cls closure - * @param add_remove #GNUNET_YES to add a new public IP address, - * #GNUNET_NO to remove a previous (now invalid) one - * @param ac address class the address belongs to - * @param addr either the previous or the new public IP address - * @param addrlen actual length of the @a addr - */ -typedef void -(*GNUNET_NAT_AddressCallback) (void *cls, - int add_remove, - enum GNUNET_NAT_AddressClass ac, - const struct sockaddr *addr, - socklen_t addrlen); - - -/** - * Signature of the callback passed to #GNUNET_NAT_register(). - * for a function to call whenever someone asks us to do connection - * reversal. - * - * @param cls closure - * @param remote_addr public IP address of the other peer - * @param remote_addrlen actual length of the @a remote_addr - */ -typedef void -(*GNUNET_NAT_ReversalCallback) (void *cls, - const struct sockaddr *remote_addr, - socklen_t remote_addrlen); - - -/** - * Handle for active NAT registrations. - */ -struct GNUNET_NAT_Handle; - - -/** - * 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); - - -/** - * 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); - - -/** - * 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); - - -/** - * 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 unregister - */ -void -GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *nh); - - -/** - * Handle to a NAT test. - */ -struct GNUNET_NAT_Test; - - /** * Error Types for the NAT subsystem (which can then later be converted/resolved to a string) */ @@ -365,129 +239,6 @@ enum GNUNET_NAT_StatusCode }; -/** - * Function called to report success or failure for - * NAT configuration test. - * - * @param cls closure - * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code - */ -typedef void -(*GNUNET_NAT_TestCallback) (void *cls, - enum GNUNET_NAT_StatusCode result); - - -/** - * 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); - - -/** - * Handle to a request given to the resolver. Can be used to cancel - * the request prior to the timeout or successful execution. Also - * used to track our internal state for the request. - */ -struct GNUNET_NAT_STUN_Handle; - - -/** - * Make Generic STUN request. Sends a generic stun request to the - * server specified using the specified socket. If we do this, - * we need to watch for possible responses and call - * #GNUNET_NAT_stun_handle_packet() on incoming packets. - * - * @param server the address of the stun server - * @param port port of the stun server, in host byte order - * @param sock the socket used to send the request, must be a - * UDP socket - * @param cb callback in case of error - * @param cb_cls closure for @a cb - * @return NULL on error - */ -struct GNUNET_NAT_STUN_Handle * -GNUNET_NAT_stun_make_request (const char *server, - uint16_t port, - struct GNUNET_NETWORK_Handle *sock, - GNUNET_NAT_TestCallback cb, - void *cb_cls); - - -/** - * Cancel active STUN request. Frees associated resources - * and ensures that the callback is no longer invoked. - * - * @param rh request to cancel - */ -void -GNUNET_NAT_stun_make_request_cancel (struct GNUNET_NAT_STUN_Handle *rh); - - -/** - * Start testing if NAT traversal works using the given configuration - * (IPv4-only). The transport adapters should be down while using - * this function. - * - * @param cfg configuration for the NAT traversal - * @param proto protocol to test, i.e. IPPROTO_TCP or IPPROTO_UDP - * @param bind_ip IPv4 address to bind to - * @param bnd_port port to bind to, 0 to test connection reversal - * @param extern_ip IPv4 address to externally advertise - * @param extern_port externally advertised port to use - * @param report function to call with the result of the test - * @param report_cls closure for @a report - * @return handle to cancel NAT test - */ -struct GNUNET_NAT_Test * -GNUNET_NAT_test_start (const struct GNUNET_CONFIGURATION_Handle *cfg, - uint8_t proto, - struct in_addr bind_ip, - uint16_t bnd_port, - struct in_addr extern_ip, - uint16_t extern_port, - GNUNET_NAT_TestCallback report, - void *report_cls); - - -/** - * Stop an active NAT test. - * - * @param tst test to stop. - */ -void -GNUNET_NAT_test_stop (struct GNUNET_NAT_Test *tst); - - -/** - * Handle to auto-configuration in progress. - */ -struct GNUNET_NAT_AutoHandle; - /** * What the situation of the NAT connectivity @@ -522,54 +273,156 @@ enum GNUNET_NAT_Type }; + /** - * Converts `enum GNUNET_NAT_StatusCode` to string + * Signature of the callback passed to #GNUNET_NAT_register() for + * a function to call whenever our set of 'valid' addresses changes. * - * @param err error code to resolve to a string - * @return point to a static string containing the error code + * @param cls closure + * @param add_remove #GNUNET_YES to add a new public IP address, + * #GNUNET_NO to remove a previous (now invalid) one + * @param ac address class the address belongs to + * @param addr either the previous or the new public IP address + * @param addrlen actual length of the @a addr */ -const char * -GNUNET_NAT_status2string (enum GNUNET_NAT_StatusCode err); +typedef void +(*GNUNET_NAT_AddressCallback) (void *cls, + int add_remove, + enum GNUNET_NAT_AddressClass ac, + const struct sockaddr *addr, + socklen_t addrlen); /** - * Function called with the result from the autoconfiguration. + * Signature of the callback passed to #GNUNET_NAT_register(). + * for a function to call whenever someone asks us to do connection + * reversal. * * @param cls closure - * @param diff minimal suggested changes to the original configuration - * to make it work (as best as we can) - * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code - * @param type what the situation of the NAT + * @param remote_addr public IP address of the other peer + * @param remote_addrlen actual length of the @a remote_addr */ typedef void -(*GNUNET_NAT_AutoResultCallback)(void *cls, - const struct GNUNET_CONFIGURATION_Handle *diff, - enum GNUNET_NAT_StatusCode result, - enum GNUNET_NAT_Type type); +(*GNUNET_NAT_ReversalCallback) (void *cls, + const struct sockaddr *remote_addr, + socklen_t remote_addrlen); + + +/** + * Handle for active NAT registrations. + */ +struct GNUNET_NAT_Handle; /** - * Start auto-configuration routine. The transport adapters should - * be stopped while this function is called. + * 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 initial configuration - * @param cb function to call with autoconfiguration result - * @param cb_cls closure for @a cb - * @return handle to cancel operation + * @param cfg configuration to use + * @param config_section name of the configuration section for options + * @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_AutoHandle * -GNUNET_NAT_autoconfig_start (const struct GNUNET_CONFIGURATION_Handle *cfg, - GNUNET_NAT_AutoResultCallback cb, - void *cb_cls); +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); + + +/** + * 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); /** - * Abort autoconfiguration. + * 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 ah handle for operation to abort + * @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); + + +/** + * 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 unregister */ void -GNUNET_NAT_autoconfig_cancel (struct GNUNET_NAT_AutoHandle *ah); +GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *nh); + + +/** + * 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); #endif diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h index f9b7d3cb8..fdabfee18 100644 --- a/src/include/gnunet_protocols.h +++ b/src/include/gnunet_protocols.h @@ -2820,7 +2820,7 @@ extern "C" /** * Message to ask NAT service to request autoconfiguration. */ -#define GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG 1067 +#define GNUNET_MESSAGE_TYPE_NAT_AUTO_REQUEST_CFG 1067 /** * Message from NAT service with the autoconfiguration result. diff --git a/src/nat-auto/.gitignore b/src/nat-auto/.gitignore new file mode 100644 index 000000000..6ba53d72f --- /dev/null +++ b/src/nat-auto/.gitignore @@ -0,0 +1,3 @@ +gnunet-service-nat-auto +gnunet-nat-auto +gnunet-nat-server diff --git a/src/nat-auto/Makefile.am b/src/nat-auto/Makefile.am new file mode 100644 index 000000000..dbe910306 --- /dev/null +++ b/src/nat-auto/Makefile.am @@ -0,0 +1,58 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +libexecdir= $(pkglibdir)/libexec/ + +pkgcfgdir= $(pkgdatadir)/config.d/ + +pkgcfg_DATA = \ + nat-auto.conf + +bin_PROGRAMS = \ + gnunet-nat-auto \ + gnunet-nat-server + +libexec_PROGRAMS = \ + gnunet-service-nat-auto + +gnunet_nat_server_SOURCES = \ + gnunet-nat-server.c nat-auto.h +gnunet_nat_server_LDADD = \ + $(top_builddir)/src/nat/libgnunetnat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +gnunet_nat_auto_SOURCES = \ + gnunet-nat-auto.c nat-auto.h +gnunet_nat_auto_LDADD = \ + libgnunetnatauto.la \ + $(top_builddir)/src/util/libgnunetutil.la + + +if USE_COVERAGE + AM_CFLAGS = -fprofile-arcs -ftest-coverage +endif + +lib_LTLIBRARIES = \ + libgnunetnatauto.la + +libgnunetnatauto_la_SOURCES = \ + nat_auto_api.c \ + nat_auto_api_test.c +libgnunetnatauto_la_LIBADD = \ + $(top_builddir)/src/nat/libgnunetnatnew.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) @EXT_LIBS@ +libgnunetnatauto_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 0:0:0 + +gnunet_service_nat_auto_SOURCES = \ + gnunet-service-nat-auto.c +gnunet_service_nat_auto_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/nat/libgnunetnatnew.la \ + $(LIBGCRYPT_LIBS) \ + -lgcrypt \ + $(GN_LIBINTL) + diff --git a/src/nat-auto/gnunet-nat-auto.c b/src/nat-auto/gnunet-nat-auto.c new file mode 100644 index 000000000..3b9a5fa94 --- /dev/null +++ b/src/nat-auto/gnunet-nat-auto.c @@ -0,0 +1,428 @@ +/* + This file is part of GNUnet. + Copyright (C) 2015, 2016, 2017 GNUnet e.V. + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * @file src/nat/gnunet-nat-auto.c + * @brief Command-line tool for testing and autoconfiguration of NAT traversal + * @author Christian Grothoff + * @author Bruno Cabral + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_nat_service.h" +#include "gnunet_nat_auto_service.h" + +/** + * Value to return from #main(). + */ +static int global_ret; + +/** + * Handle to ongoing autoconfiguration. + */ +static struct GNUNET_NAT_AutoHandle *ah; + +/** + * If we do auto-configuration, should we write the result + * to a file? + */ +static int write_cfg; + +/** + * Configuration filename. + */ +static const char *cfg_file; + +/** + * Original configuration. + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Address we are bound to (in test), or should bind to + * (if #do_stun is set). + */ +static char *bind_addr; + +/** + * External IP address and port to use for the test. + * If not set, use #bind_addr. + */ +static char *extern_addr; + +/** + * Should we run autoconfiguration? + */ +static unsigned int do_auto; + +/** + * Handle to a NAT test operation. + */ +static struct GNUNET_NAT_Test *nt; + +/** + * Flag set to 1 if we use IPPROTO_UDP. + */ +static int use_udp; + +/** + * Flag set to 1 if we use IPPROTO_TCP. + */ +static int use_tcp; + +/** + * Protocol to use. + */ +static uint8_t proto; + +/** + * Test if all activities have finished, and if so, + * terminate. + */ +static void +test_finished () +{ + if (NULL != ah) + return; + if (NULL != nt) + return; + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Function to iterate over sugested changes options + * + * @param cls closure + * @param section name of the section + * @param option name of the option + * @param value value of the option + */ +static void +auto_conf_iter (void *cls, + const char *section, + const char *option, + const char *value) +{ + struct GNUNET_CONFIGURATION_Handle *new_cfg = cls; + + PRINTF ("%s: %s\n", + option, + value); + if (NULL != new_cfg) + GNUNET_CONFIGURATION_set_value_string (new_cfg, + section, + option, + value); +} + + +/** + * Function called with the result from the autoconfiguration. + * + * @param cls closure + * @param diff minimal suggested changes to the original configuration + * to make it work (as best as we can) + * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code + * @param type what the situation of the NAT + */ +static void +auto_config_cb (void *cls, + const struct GNUNET_CONFIGURATION_Handle *diff, + enum GNUNET_NAT_StatusCode result, + enum GNUNET_NAT_Type type) +{ + const char *nat_type; + char unknown_type[64]; + struct GNUNET_CONFIGURATION_Handle *new_cfg; + + ah = NULL; + switch (type) + { + case GNUNET_NAT_TYPE_NO_NAT: + nat_type = "NO NAT"; + break; + case GNUNET_NAT_TYPE_UNREACHABLE_NAT: + nat_type = "NAT but we can traverse"; + break; + case GNUNET_NAT_TYPE_STUN_PUNCHED_NAT: + nat_type = "NAT but STUN is able to identify the correct information"; + break; + case GNUNET_NAT_TYPE_UPNP_NAT: + nat_type = "NAT but UPNP opened the ports"; + break; + default: + SPRINTF (unknown_type, + "NAT unknown, type %u", + type); + nat_type = unknown_type; + break; + } + + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "NAT status: %s/%s\n", + GNUNET_NAT_status2string (result), + nat_type); + + /* Shortcut: if there are no changes suggested, bail out early. */ + if (GNUNET_NO == + GNUNET_CONFIGURATION_is_dirty (diff)) + { + test_finished (); + return; + } + + /* Apply diff to original configuration and show changes + to the user */ + new_cfg = write_cfg ? GNUNET_CONFIGURATION_dup (cfg) : NULL; + + if (NULL != diff) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + _("Suggested configuration changes:\n")); + GNUNET_CONFIGURATION_iterate_section_values (diff, + "nat", + &auto_conf_iter, + new_cfg); + } + + /* If desired, write configuration to file; we write only the + changes to the defaults to keep things compact. */ + if ( (write_cfg) && + (NULL != diff) ) + { + struct GNUNET_CONFIGURATION_Handle *def_cfg; + + GNUNET_CONFIGURATION_set_value_string (new_cfg, + "ARM", + "CONFIG", + NULL); + def_cfg = GNUNET_CONFIGURATION_create (); + GNUNET_break (GNUNET_OK == + GNUNET_CONFIGURATION_load (def_cfg, + NULL)); + if (GNUNET_OK != + GNUNET_CONFIGURATION_write_diffs (def_cfg, + new_cfg, + cfg_file)) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + _("Failed to write configuration to `%s'\n"), + cfg_file); + global_ret = 1; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + _("Wrote updated configuration to `%s'\n"), + cfg_file); + } + GNUNET_CONFIGURATION_destroy (def_cfg); + } + + if (NULL != new_cfg) + GNUNET_CONFIGURATION_destroy (new_cfg); + test_finished (); +} + + +/** + * Function called to report success or failure for + * NAT configuration test. + * + * @param cls closure + * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code + */ +static void +test_report_cb (void *cls, + enum GNUNET_NAT_StatusCode result) +{ + nt = NULL; + PRINTF ("NAT test result: %s\n", + GNUNET_NAT_status2string (result)); + test_finished (); +} + + +/** + * Task run on shutdown. + * + * @param cls NULL + */ +static void +do_shutdown (void *cls) +{ + if (NULL != ah) + { + GNUNET_NAT_autoconfig_cancel (ah); + ah = NULL; + } + if (NULL != nt) + { + GNUNET_NAT_test_stop (nt); + nt = NULL; + } +} + + +/** + * Main function that will be run. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param c configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + struct sockaddr_in bind_sa; + struct sockaddr_in extern_sa; + + cfg_file = cfgfile; + cfg = c; + + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, + NULL); + + if (do_auto) + { + ah = GNUNET_NAT_autoconfig_start (c, + &auto_config_cb, + NULL); + } + + if (use_tcp && use_udp) + { + if (do_auto) + return; + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Cannot use TCP and UDP\n"); + global_ret = 1; + return; + } + proto = 0; + if (use_tcp) + proto = IPPROTO_TCP; + if (use_udp) + proto = IPPROTO_UDP; + + if (NULL != bind_addr) + { + if (GNUNET_OK != + GNUNET_STRINGS_to_address_ipv4 (bind_addr, + strlen (bind_addr), + &bind_sa)) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Invalid socket address `%s'\n", + bind_addr); + global_ret = 1; + return; + } + } + if (NULL != extern_addr) + { + if (GNUNET_OK != + GNUNET_STRINGS_to_address_ipv4 (extern_addr, + strlen (extern_addr), + &extern_sa)) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Invalid socket address `%s'\n", + extern_addr); + global_ret = 1; + return; + } + } + + if (NULL != bind_addr) + { + if (NULL == extern_addr) + extern_sa = bind_sa; + nt = GNUNET_NAT_test_start (c, + proto, + bind_sa.sin_addr, + ntohs (bind_sa.sin_port), + extern_sa.sin_addr, + ntohs (extern_sa.sin_port), + &test_report_cb, + NULL); + } + test_finished (); +} + + +/** + * Main function of gnunet-nat + * + * @param argc number of command-line arguments + * @param argv command line + * @return 0 on success, -1 on error + */ +int +main (int argc, + char *const argv[]) +{ + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + {'a', "auto", NULL, + gettext_noop ("run autoconfiguration"), + GNUNET_NO, &GNUNET_GETOPT_set_one, &do_auto }, + {'b', "bind", "ADDRESS", + gettext_noop ("which IP and port are we bound to"), + GNUNET_YES, &GNUNET_GETOPT_set_string, &bind_addr }, + {'e', "external", "ADDRESS", + gettext_noop ("which external IP and port should be used to test"), + GNUNET_YES, &GNUNET_GETOPT_set_string, &extern_addr }, + {'t', "tcp", NULL, + gettext_noop ("use TCP"), + GNUNET_NO, &GNUNET_GETOPT_set_one, &use_tcp }, + {'u', "udp", NULL, + gettext_noop ("use UDP"), + GNUNET_NO, &GNUNET_GETOPT_set_one, &use_udp }, + {'w', "write", NULL, + gettext_noop ("write configuration file (for autoconfiguration)"), + GNUNET_NO, &GNUNET_GETOPT_set_one, &write_cfg }, + GNUNET_GETOPT_OPTION_END + }; + + if (GNUNET_OK != + GNUNET_STRINGS_get_utf8_args (argc, argv, + &argc, &argv)) + return 2; + if (GNUNET_OK != + GNUNET_PROGRAM_run (argc, argv, + "gnunet-nat-auto [options]", + _("GNUnet NAT traversal autoconfiguration"), + options, + &run, + NULL)) + { + global_ret = 1; + } + GNUNET_free ((void*) argv); + return global_ret; +} + + +/* end of gnunet-nat-auto.c */ diff --git a/src/nat-auto/gnunet-nat-server.c b/src/nat-auto/gnunet-nat-server.c new file mode 100644 index 000000000..93352f5f0 --- /dev/null +++ b/src/nat-auto/gnunet-nat-server.c @@ -0,0 +1,384 @@ +/* + This file is part of GNUnet. + Copyright (C) 2011 GNUnet e.V. + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * @file src/nat/gnunet-nat-server.c + * @brief Daemon to run on 'gnunet.org' to help test NAT traversal code + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_nat_lib.h" +#include "gnunet_protocols.h" +#include "nat-auto.h" + + +/** + * Our server. + */ +static struct GNUNET_SERVER_Handle *server; + +/** + * Our configuration. + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + + +/** + * Try contacting the peer using autonomous NAT traveral method. + * + * @param dst_ipv4 IPv4 address to send the fake ICMP message + * @param dport destination port to include in ICMP message + * @param is_tcp mark for TCP (#GNUNET_YES) or UDP (#GNUNET_NO) + */ +static void +try_anat (uint32_t dst_ipv4, + uint16_t dport, + int is_tcp) +{ + struct GNUNET_NAT_Handle *h; + struct sockaddr_in sa; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asking for connection reversal with %x and code %u\n", + (unsigned int) dst_ipv4, + (unsigned int) dport); + h = GNUNET_NAT_register (cfg, + is_tcp, + dport, + 0, + NULL, NULL, NULL, NULL, NULL, NULL); + memset (&sa, 0, sizeof (sa)); + sa.sin_family = AF_INET; +#if HAVE_SOCKADDR_IN_SIN_LEN + sa.sin_len = sizeof (sa); +#endif + sa.sin_addr.s_addr = dst_ipv4; + GNUNET_NAT_run_client (h, &sa); + GNUNET_NAT_unregister (h); +} + + +/** + * Closure for #tcp_send. + */ +struct TcpContext +{ + /** + * TCP socket. + */ + struct GNUNET_NETWORK_Handle *s; + + /** + * Data to transmit. + */ + uint16_t data; +}; + + +/** + * Task called by the scheduler once we can do the TCP send + * (or once we failed to connect...). + * + * @param cls the `struct TcpContext` + */ +static void +tcp_send (void *cls) +{ + struct TcpContext *ctx = cls; + const struct GNUNET_SCHEDULER_TaskContext *tc; + + tc = GNUNET_SCHEDULER_get_task_context (); + if ((NULL != tc->write_ready) && + (GNUNET_NETWORK_fdset_isset (tc->write_ready, ctx->s))) + { + if (-1 == + GNUNET_NETWORK_socket_send (ctx->s, &ctx->data, sizeof (ctx->data))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "send"); + } + GNUNET_NETWORK_socket_shutdown (ctx->s, SHUT_RDWR); + } + GNUNET_NETWORK_socket_close (ctx->s); + GNUNET_free (ctx); +} + + +/** + * Try to send @a data to the + * IP @a dst_ipv4' at port @a dport via TCP. + * + * @param dst_ipv4 target IP + * @param dport target port + * @param data data to send + */ +static void +try_send_tcp (uint32_t dst_ipv4, + uint16_t dport, + uint16_t data) +{ + struct GNUNET_NETWORK_Handle *s; + struct sockaddr_in sa; + struct TcpContext *ctx; + + s = GNUNET_NETWORK_socket_create (AF_INET, + SOCK_STREAM, + 0); + if (NULL == s) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "socket"); + return; + } + memset (&sa, 0, sizeof (sa)); + sa.sin_family = AF_INET; +#if HAVE_SOCKADDR_IN_SIN_LEN + sa.sin_len = sizeof (sa); +#endif + sa.sin_addr.s_addr = dst_ipv4; + sa.sin_port = htons (dport); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending TCP message to `%s'\n", + GNUNET_a2s ((struct sockaddr *) &sa, + sizeof (sa))); + if ( (GNUNET_OK != + GNUNET_NETWORK_socket_connect (s, + (const struct sockaddr *) &sa, + sizeof (sa))) && + (errno != EINPROGRESS) ) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "connect"); + GNUNET_NETWORK_socket_close (s); + return; + } + ctx = GNUNET_new (struct TcpContext); + ctx->s = s; + ctx->data = data; + GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_SECONDS, + s, + &tcp_send, + ctx); +} + + +/** + * Try to send @a data to the + * IP @a dst_ipv4 at port @a dport via UDP. + * + * @param dst_ipv4 target IP + * @param dport target port + * @param data data to send + */ +static void +try_send_udp (uint32_t dst_ipv4, + uint16_t dport, + uint16_t data) +{ + struct GNUNET_NETWORK_Handle *s; + struct sockaddr_in sa; + + s = GNUNET_NETWORK_socket_create (AF_INET, + SOCK_DGRAM, + 0); + if (NULL == s) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "socket"); + return; + } + memset (&sa, 0, sizeof (sa)); + sa.sin_family = AF_INET; +#if HAVE_SOCKADDR_IN_SIN_LEN + sa.sin_len = sizeof (sa); +#endif + sa.sin_addr.s_addr = dst_ipv4; + sa.sin_port = htons (dport); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending UDP packet to `%s'\n", + GNUNET_a2s ((struct sockaddr *) &sa, + sizeof (sa))); + if (-1 == + GNUNET_NETWORK_socket_sendto (s, + &data, + sizeof (data), + (const struct sockaddr *) &sa, + sizeof (sa))) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "sendto"); + GNUNET_NETWORK_socket_close (s); +} + + +/** + * We've received a request to probe a NAT + * traversal. Do it. + * + * @param cls unused + * @param client handle to client (we always close) + * @param msg message with details about what to test + */ +static void +test (void *cls, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *msg) +{ + const struct GNUNET_NAT_TestMessage *tm; + uint16_t dport; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received test request\n"); + tm = (const struct GNUNET_NAT_TestMessage *) msg; + dport = ntohs (tm->dport); + if (0 == dport) + try_anat (tm->dst_ipv4, + ntohs (tm->data), + (int) ntohl (tm->is_tcp)); + else if (GNUNET_YES == ntohl (tm->is_tcp)) + try_send_tcp (tm->dst_ipv4, + dport, + tm->data); + else + try_send_udp (tm->dst_ipv4, + dport, + tm->data); + GNUNET_SERVER_receive_done (client, + GNUNET_NO); +} + + +/** + * Task run during shutdown. + * + * @param cls unused + */ +static void +shutdown_task (void *cls) +{ + GNUNET_SERVER_destroy (server); + server = NULL; +} + + +/** + * Main function that will be run. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param c configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + static const struct GNUNET_SERVER_MessageHandler handlers[] = { + {&test, NULL, GNUNET_MESSAGE_TYPE_NAT_TEST, + sizeof (struct GNUNET_NAT_TestMessage)}, + {NULL, NULL, 0, 0} + }; + unsigned int port; + struct sockaddr_in in4; + struct sockaddr_in6 in6; + + socklen_t slen[] = { + sizeof (in4), + sizeof (in6), + 0 + }; + struct sockaddr *sa[] = { + (struct sockaddr *) &in4, + (struct sockaddr *) &in6, + NULL + }; + + cfg = c; + if ( (NULL == args[0]) || + (1 != SSCANF (args[0], "%u", &port)) || + (0 == port) || + (65536 <= port) ) + { + FPRINTF (stderr, + _("Please pass valid port number as the first argument! (got `%s')\n"), + args[0]); + return; + } + memset (&in4, 0, sizeof (in4)); + memset (&in6, 0, sizeof (in6)); + in4.sin_family = AF_INET; + in4.sin_port = htons ((uint16_t) port); + in6.sin6_family = AF_INET6; + in6.sin6_port = htons ((uint16_t) port); +#if HAVE_SOCKADDR_IN_SIN_LEN + in4.sin_len = sizeof (in4); + in6.sin6_len = sizeof (in6); +#endif + server = GNUNET_SERVER_create (NULL, + NULL, + (struct sockaddr * const *) sa, + slen, + GNUNET_TIME_UNIT_SECONDS, + GNUNET_YES); + GNUNET_SERVER_add_handlers (server, + handlers); + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, + NULL); +} + + +/** + * Main function of gnunet-nat-server. + * + * @param argc number of command-line arguments + * @param argv command line + * @return 0 on success, -1 on error + */ +int +main (int argc, char *const argv[]) +{ + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + + if (GNUNET_OK != + GNUNET_STRINGS_get_utf8_args (argc, argv, + &argc, &argv)) + return 2; + + if (GNUNET_OK != + GNUNET_PROGRAM_run (argc, + argv, + "gnunet-nat-server [options] PORT", + _("GNUnet NAT traversal test helper daemon"), + options, + &run, + NULL)) + { + GNUNET_free ((void*) argv); + return 1; + } + GNUNET_free ((void*) argv); + return 0; +} + + +/* end of gnunet-nat-server.c */ diff --git a/src/nat-auto/gnunet-service-nat-auto.c b/src/nat-auto/gnunet-service-nat-auto.c new file mode 100644 index 000000000..897d6feb2 --- /dev/null +++ b/src/nat-auto/gnunet-service-nat-auto.c @@ -0,0 +1,481 @@ +/* + 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 General Public License as published + by the Free Software Foundation; either version 3, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +/** + * @file nat-auto/gnunet-service-nat-auto.c + * @brief NAT autoconfiguration service + * @author Christian Grothoff + * + * TODO: + * - merge client handle and autoconfig context + * - implement "more" autoconfig: + * + re-work gnunet-nat-server & integrate! + * + test manually punched NAT (how?) + */ +#include "platform.h" +#include +#include "gnunet_util_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_signatures.h" +#include "gnunet_nat_service.h" +#include "gnunet_statistics_service.h" +#include "gnunet_resolver_service.h" +#include "nat-auto.h" +#include + + +/** + * How long do we wait until we forcefully terminate autoconfiguration? + */ +#define AUTOCONFIG_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + + +/** + * 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; +}; + + +/** + * Context for autoconfiguration operations. + */ +struct AutoconfigContext +{ + /** + * Kept in a DLL. + */ + struct AutoconfigContext *prev; + + /** + * Kept in a DLL. + */ + struct AutoconfigContext *next; + + /** + * Which client asked the question. + */ + struct ClientHandle *ch; + + /** + * Configuration we are creating. + */ + struct GNUNET_CONFIGURATION_Handle *c; + + /** + * Original configuration (for diffing). + */ + struct GNUNET_CONFIGURATION_Handle *orig; + + /** + * Timeout task to force termination. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * #GNUNET_YES if upnpc should be used, + * #GNUNET_NO if upnpc should not be used, + * #GNUNET_SYSERR if we should simply not change the option. + */ + int enable_upnpc; + + /** + * Status code to return to the client. + */ + enum GNUNET_NAT_StatusCode status_code; + + /** + * NAT type to return to the client. + */ + enum GNUNET_NAT_Type type; +}; + + +/** + * Head of client DLL. + */ +static struct ClientHandle *ch_head; + +/** + * Tail of client DLL. + */ +static struct ClientHandle *ch_tail; + +/** + * DLL of our autoconfiguration operations. + */ +static struct AutoconfigContext *ac_head; + +/** + * DLL of our autoconfiguration operations. + */ +static struct AutoconfigContext *ac_tail; + +/** + * Handle to our current configuration. + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Handle to the statistics service. + */ +static struct GNUNET_STATISTICS_Handle *stats; + + +/** + * Check validity of #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG 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_autoconfig_request (void *cls, + const struct GNUNET_NAT_AutoconfigRequestMessage *message) +{ + return GNUNET_OK; /* checked later */ +} + + +/** + * Stop all pending activities with respect to the @a ac + * + * @param ac autoconfiguration to terminate activities for + */ +static void +terminate_ac_activities (struct AutoconfigContext *ac) +{ + if (NULL != ac->timeout_task) + { + GNUNET_SCHEDULER_cancel (ac->timeout_task); + ac->timeout_task = NULL; + } +} + + +/** + * Finish handling the autoconfiguration request and send + * the response to the client. + * + * @param cls the `struct AutoconfigContext` to conclude + */ +static void +conclude_autoconfig_request (void *cls) +{ + struct AutoconfigContext *ac = cls; + struct ClientHandle *ch = ac->ch; + struct GNUNET_NAT_AutoconfigResultMessage *arm; + struct GNUNET_MQ_Envelope *env; + size_t c_size; + char *buf; + struct GNUNET_CONFIGURATION_Handle *diff; + + ac->timeout_task = NULL; + terminate_ac_activities (ac); + + /* Send back response */ + diff = GNUNET_CONFIGURATION_get_diff (ac->orig, + ac->c); + buf = GNUNET_CONFIGURATION_serialize (diff, + &c_size); + GNUNET_CONFIGURATION_destroy (diff); + env = GNUNET_MQ_msg_extra (arm, + c_size, + GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT); + arm->status_code = htonl ((uint32_t) ac->status_code); + arm->type = htonl ((uint32_t) ac->type); + GNUNET_memcpy (&arm[1], + buf, + c_size); + GNUNET_free (buf); + GNUNET_MQ_send (ch->mq, + env); + + /* clean up */ + GNUNET_CONFIGURATION_destroy (ac->orig); + GNUNET_CONFIGURATION_destroy (ac->c); + GNUNET_CONTAINER_DLL_remove (ac_head, + ac_tail, + ac); + GNUNET_free (ac); + GNUNET_SERVICE_client_continue (ch->client); +} + + +/** + * Check if all autoconfiguration operations have concluded, + * and if they have, send the result back to the client. + * + * @param ac autoconfiguation context to check + */ +static void +check_autoconfig_finished (struct AutoconfigContext *ac) +{ + GNUNET_SCHEDULER_cancel (ac->timeout_task); + ac->timeout_task + = GNUNET_SCHEDULER_add_now (&conclude_autoconfig_request, + ac); +} + + +/** + * Update ENABLE_UPNPC configuration option. + * + * @param ac autoconfiguration to update + */ +static void +update_enable_upnpc_option (struct AutoconfigContext *ac) +{ + switch (ac->enable_upnpc) + { + case GNUNET_YES: + GNUNET_CONFIGURATION_set_value_string (ac->c, + "NAT", + "ENABLE_UPNP", + "YES"); + break; + case GNUNET_NO: + GNUNET_CONFIGURATION_set_value_string (ac->c, + "NAT", + "ENABLE_UPNP", + "NO"); + break; + case GNUNET_SYSERR: + /* We are unsure, do not change option */ + break; + } +} + + +/** + * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message from + * client. + * + * @param cls client who sent the message + * @param message the message received + */ +static void +handle_autoconfig_request (void *cls, + const struct GNUNET_NAT_AutoconfigRequestMessage *message) +{ + struct ClientHandle *ch = cls; + size_t left = ntohs (message->header.size) - sizeof (*message); + struct AutoconfigContext *ac; + + ac = GNUNET_new (struct AutoconfigContext); + ac->status_code = GNUNET_NAT_ERROR_SUCCESS; + ac->ch = ch; + ac->c = GNUNET_CONFIGURATION_create (); + if (GNUNET_OK != + GNUNET_CONFIGURATION_deserialize (ac->c, + (const char *) &message[1], + left, + GNUNET_NO)) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (ch->client); + GNUNET_CONFIGURATION_destroy (ac->c); + GNUNET_free (ac); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received REQUEST_AUTO_CONFIG message from client\n"); + + GNUNET_CONTAINER_DLL_insert (ac_head, + ac_tail, + ac); + ac->orig + = GNUNET_CONFIGURATION_dup (ac->c); + ac->timeout_task + = GNUNET_SCHEDULER_add_delayed (AUTOCONFIG_TIMEOUT, + &conclude_autoconfig_request, + ac); + ac->enable_upnpc = GNUNET_SYSERR; /* undecided */ + + /* Probe for upnpc */ + if (GNUNET_SYSERR == + GNUNET_OS_check_helper_binary ("upnpc", + GNUNET_NO, + NULL)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("UPnP client `upnpc` command not found, disabling UPnP\n")); + ac->enable_upnpc = GNUNET_NO; + } + else + { + /* We might at some point be behind NAT, try upnpc */ + ac->enable_upnpc = GNUNET_YES; + } + update_enable_upnpc_option (ac); + + /* Finally, check if we are already done */ + check_autoconfig_finished (ac); +} + + +/** + * Task run during shutdown. + * + * @param cls unused + */ +static void +shutdown_task (void *cls) +{ + struct AutoconfigContext *ac; + + while (NULL != (ac = ac_head)) + { + GNUNET_CONTAINER_DLL_remove (ac_head, + ac_tail, + ac); + terminate_ac_activities (ac); + GNUNET_free (ac); + } + if (NULL != stats) + { + GNUNET_STATISTICS_destroy (stats, + GNUNET_NO); + stats = NULL; + } +} + + +/** + * 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; + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, + NULL); + stats = GNUNET_STATISTICS_create ("nat-auto", + cfg); +} + + +/** + * 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; + + GNUNET_CONTAINER_DLL_remove (ch_head, + ch_tail, + ch); + 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 (autoconfig_request, + GNUNET_MESSAGE_TYPE_NAT_AUTO_REQUEST_CFG, + struct GNUNET_NAT_AutoconfigRequestMessage, + NULL), + GNUNET_MQ_handler_end ()); + + +#if defined(LINUX) && defined(__GLIBC__) +#include + +/** + * 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 */ diff --git a/src/nat-auto/nat-auto.conf.in b/src/nat-auto/nat-auto.conf.in new file mode 100644 index 000000000..daa3e389d --- /dev/null +++ b/src/nat-auto/nat-auto.conf.in @@ -0,0 +1,15 @@ +[nat] +AUTOSTART = @AUTOSTART@ +@UNIXONLY@ PORT = 2124 +HOSTNAME = localhost +BINARY = gnunet-service-nat-auto +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-nat-auto.sock +UNIX_MATCH_UID = YES +UNIX_MATCH_GID = YES + +[gnunet-nat-server] +HOSTNAME = gnunet.org +PORT = 5724 +NOARMBIND = YES diff --git a/src/nat-auto/nat-auto.h b/src/nat-auto/nat-auto.h new file mode 100644 index 000000000..150dc32c2 --- /dev/null +++ b/src/nat-auto/nat-auto.h @@ -0,0 +1,110 @@ +/* + This file is part of GNUnet. + Copyright (C) 2011, 2016, 2017 GNUnet e.V. + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * @file src/nat-auto/nat-auto.h + * @brief Messages for interaction with gnunet-nat-auto-service + * @author Christian Grothoff + * + */ +#ifndef NAT_AUTO_H +#define NAT_AUTO_H +#include "gnunet_util_lib.h" + + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Request to test NAT traversal, sent to the gnunet-nat-server + * (not the service!). + */ +struct GNUNET_NAT_TestMessage +{ + /** + * Header with type #GNUNET_MESSAGE_TYPE_NAT_TEST + */ + struct GNUNET_MessageHeader header; + + /** + * IPv4 target IP address + */ + uint32_t dst_ipv4; + + /** + * Port to use, 0 to send dummy ICMP response. + */ + uint16_t dport; + + /** + * Data to send OR advertised-port (in NBO) to use for dummy ICMP. + */ + uint16_t data; + + /** + * #GNUNET_YES for TCP, #GNUNET_NO for UDP. + */ + int32_t is_tcp; + +}; + + +/** + * Client requesting automatic configuration. + */ +struct GNUNET_NAT_AutoconfigRequestMessage +{ + /** + * Header with type #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG + */ + struct GNUNET_MessageHeader header; + + /* Followed by configuration (diff, serialized, compressed) */ + +}; + + +/** + * Service responding with proposed configuration. + */ +struct GNUNET_NAT_AutoconfigResultMessage +{ + /** + * Header with type #GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT + */ + struct GNUNET_MessageHeader header; + + /** + * An `enum GNUNET_NAT_StatusCode` in NBO. + */ + int32_t status_code GNUNET_PACKED; + + /** + * An `enum GNUNET_NAT_Type` in NBO. + */ + int32_t type GNUNET_PACKED; + + /* Followed by configuration (diff, serialized, compressed) */ +}; + + +GNUNET_NETWORK_STRUCT_END + +#endif diff --git a/src/nat-auto/nat_auto_api.c b/src/nat-auto/nat_auto_api.c new file mode 100644 index 000000000..e6b0512c6 --- /dev/null +++ b/src/nat-auto/nat_auto_api.c @@ -0,0 +1,274 @@ + +/* + This file is part of GNUnet. + Copyright (C) 2007-2016 GNUnet e.V. + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * @author Christian Grothoff + * @author Milan Bouchet-Valat + * + * @file nat/nat_auto_api.c + * Routines for NAT auto configuration. + */ +#include "platform.h" +#include "gnunet_nat_service.h" +#include "gnunet_nat_auto_service.h" +#include "nat-auto.h" + + + +/** + * Handle to auto-configuration in progress. + */ +struct GNUNET_NAT_AutoHandle +{ + + /** + * Configuration we use. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Message queue for communicating with the NAT service. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Function called with the result from the autoconfiguration. + */ + GNUNET_NAT_AutoResultCallback arc; + + /** + * Closure for @e arc. + */ + void *arc_cls; + +}; + + +/** + * Converts `enum GNUNET_NAT_StatusCode` to string + * + * @param err error code to resolve to a string + * @return point to a static string containing the error code + */ +const char * +GNUNET_NAT_status2string (enum GNUNET_NAT_StatusCode err) +{ + switch (err) + { + case GNUNET_NAT_ERROR_SUCCESS: + return _ ("Operation Successful"); + case GNUNET_NAT_ERROR_IPC_FAILURE: + return _ ("IPC failure"); + case GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR: + return _ ("Failure in network subsystem, check permissions."); + case GNUNET_NAT_ERROR_TIMEOUT: + return _ ("Encountered timeout while performing operation"); + case GNUNET_NAT_ERROR_NOT_ONLINE: + return _ ("detected that we are offline"); + case GNUNET_NAT_ERROR_UPNPC_NOT_FOUND: + return _ ("`upnpc` command not found"); + case GNUNET_NAT_ERROR_UPNPC_FAILED: + return _ ("Failed to run `upnpc` command"); + case GNUNET_NAT_ERROR_UPNPC_TIMEOUT: + return _ ("`upnpc' command took too long, process killed"); + case GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED: + return _ ("`upnpc' command failed to establish port mapping"); + case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND: + return _ ("`external-ip' command not found"); + case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED: + return _ ("Failed to run `external-ip` command"); + case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID: + return _ ("`external-ip' command output invalid"); + case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID: + return _ ("no valid address was returned by `external-ip'"); + case GNUNET_NAT_ERROR_NO_VALID_IF_IP_COMBO: + return _ ("Could not determine interface with internal/local network address"); + case GNUNET_NAT_ERROR_HELPER_NAT_SERVER_NOT_FOUND: + return _ ("No functioning gnunet-helper-nat-server installation found"); + case GNUNET_NAT_ERROR_NAT_TEST_START_FAILED: + return _ ("NAT test could not be initialized"); + case GNUNET_NAT_ERROR_NAT_TEST_TIMEOUT: + return _ ("NAT test timeout reached"); + case GNUNET_NAT_ERROR_NAT_REGISTER_FAILED: + return _ ("could not register NAT"); + case GNUNET_NAT_ERROR_HELPER_NAT_CLIENT_NOT_FOUND: + return _ ("No working gnunet-helper-nat-client installation found"); + default: + return "unknown status code"; + } +} + + +/** + * Check result from autoconfiguration attempt. + * + * @param cls the `struct GNUNET_NAT_AutoHandle` + * @param res the result + * @return #GNUNET_OK if @a res is well-formed (always for now) + */ +static int +check_auto_result (void *cls, + const struct GNUNET_NAT_AutoconfigResultMessage *res) +{ + return GNUNET_OK; +} + + +/** + * Handle result from autoconfiguration attempt. + * + * @param cls the `struct GNUNET_NAT_AutoHandle` + * @param res the result + */ +static void +handle_auto_result (void *cls, + const struct GNUNET_NAT_AutoconfigResultMessage *res) +{ + struct GNUNET_NAT_AutoHandle *ah = cls; + size_t left; + struct GNUNET_CONFIGURATION_Handle *cfg; + enum GNUNET_NAT_Type type + = (enum GNUNET_NAT_Type) ntohl (res->type); + enum GNUNET_NAT_StatusCode status + = (enum GNUNET_NAT_StatusCode) ntohl (res->status_code); + + left = ntohs (res->header.size) - sizeof (*res); + cfg = GNUNET_CONFIGURATION_create (); + if (GNUNET_OK != + GNUNET_CONFIGURATION_deserialize (cfg, + (const char *) &res[1], + left, + GNUNET_NO)) + { + GNUNET_break (0); + ah->arc (ah->arc_cls, + NULL, + GNUNET_NAT_ERROR_IPC_FAILURE, + type); + } + else + { + ah->arc (ah->arc_cls, + cfg, + status, + type); + } + GNUNET_CONFIGURATION_destroy (cfg); + GNUNET_NAT_autoconfig_cancel (ah); +} + + +/** + * Handle queue errors by reporting autoconfiguration failure. + * + * @param cls the `struct GNUNET_NAT_AutoHandle *` + * @param error details about the error + */ +static void +ah_error_handler (void *cls, + enum GNUNET_MQ_Error error) +{ + struct GNUNET_NAT_AutoHandle *ah = cls; + + ah->arc (ah->arc_cls, + NULL, + GNUNET_NAT_ERROR_IPC_FAILURE, + GNUNET_NAT_TYPE_UNKNOWN); + GNUNET_NAT_autoconfig_cancel (ah); +} + + +/** + * Start auto-configuration routine. The transport adapters should + * be stopped while this function is called. + * + * @param cfg initial configuration + * @param cb function to call with autoconfiguration result + * @param cb_cls closure for @a cb + * @return handle to cancel operation + */ +struct GNUNET_NAT_AutoHandle * +GNUNET_NAT_autoconfig_start (const struct GNUNET_CONFIGURATION_Handle *cfg, + GNUNET_NAT_AutoResultCallback cb, + void *cb_cls) +{ + struct GNUNET_NAT_AutoHandle *ah = GNUNET_new (struct GNUNET_NAT_AutoHandle); + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_var_size (auto_result, + GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT, + struct GNUNET_NAT_AutoconfigResultMessage, + ah), + GNUNET_MQ_handler_end () + }; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_NAT_AutoconfigRequestMessage *req; + char *buf; + size_t size; + + buf = GNUNET_CONFIGURATION_serialize (cfg, + &size); + if (size > GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (*req)) + { + GNUNET_break (0); + GNUNET_free (buf); + GNUNET_free (ah); + return NULL; + } + ah->arc = cb; + ah->arc_cls = cb_cls; + ah->mq = GNUNET_CLIENT_connecT (cfg, + "nat", + handlers, + &ah_error_handler, + ah); + if (NULL == ah->mq) + { + GNUNET_break (0); + GNUNET_free (buf); + GNUNET_free (ah); + return NULL; + } + env = GNUNET_MQ_msg_extra (req, + size, + GNUNET_MESSAGE_TYPE_NAT_AUTO_REQUEST_CFG); + GNUNET_memcpy (&req[1], + buf, + size); + GNUNET_free (buf); + GNUNET_MQ_send (ah->mq, + env); + return ah; +} + + +/** + * Abort autoconfiguration. + * + * @param ah handle for operation to abort + */ +void +GNUNET_NAT_autoconfig_cancel (struct GNUNET_NAT_AutoHandle *ah) +{ + GNUNET_MQ_destroy (ah->mq); + GNUNET_free (ah); +} + +/* end of nat_api_auto.c */ diff --git a/src/nat-auto/nat_auto_api_test.c b/src/nat-auto/nat_auto_api_test.c new file mode 100644 index 000000000..056d2a2bf --- /dev/null +++ b/src/nat-auto/nat_auto_api_test.c @@ -0,0 +1,644 @@ +/* + This file is part of GNUnet. + Copyright (C) 2011, 2016 GNUnet e.V. + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/** + * @file nat/nat_auto_api_test.c + * @brief functions to test if the NAT configuration is successful at achieving NAT traversal (with the help of a gnunet-nat-server) + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_nat_lib.h" +#include "nat-auto.h" + +#define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__) + +#define NAT_SERVER_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) + +/** + * Entry we keep for each incoming connection. + */ +struct NatActivity +{ + /** + * This is a doubly-linked list. + */ + struct NatActivity *next; + + /** + * This is a doubly-linked list. + */ + struct NatActivity *prev; + + /** + * Socket of the incoming connection. + */ + struct GNUNET_NETWORK_Handle *sock; + + /** + * Handle of the master context. + */ + struct GNUNET_NAT_Test *h; + + /** + * Task reading from the incoming connection. + */ + struct GNUNET_SCHEDULER_Task *rtask; +}; + + +/** + * Entry we keep for each connection to the gnunet-nat-service. + */ +struct ClientActivity +{ + /** + * This is a doubly-linked list. + */ + struct ClientActivity *next; + + /** + * This is a doubly-linked list. + */ + struct ClientActivity *prev; + + /** + * Socket of the incoming connection. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Handle to overall NAT test. + */ + struct GNUNET_NAT_Test *h; + +}; + + +/** + * Handle to a NAT test. + */ +struct GNUNET_NAT_Test +{ + + /** + * Configuration used + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Function to call with success report + */ + GNUNET_NAT_TestCallback report; + + /** + * Closure for @e report. + */ + void *report_cls; + + /** + * Handle to NAT traversal in use + */ + struct GNUNET_NAT_Handle *nat; + + /** + * Handle to listen socket, or NULL + */ + struct GNUNET_NETWORK_Handle *lsock; + + /** + * Head of list of nat activities. + */ + struct NatActivity *na_head; + + /** + * Tail of list of nat activities. + */ + struct NatActivity *na_tail; + + /** + * Head of list of client activities. + */ + struct ClientActivity *ca_head; + + /** + * Tail of list of client activities. + */ + struct ClientActivity *ca_tail; + + /** + * Identity of task for the listen socket (if any) + */ + struct GNUNET_SCHEDULER_Task *ltask; + + /** + * Task identifier for the timeout (if any) + */ + struct GNUNET_SCHEDULER_Task *ttask; + + /** + * #GNUNET_YES if we're testing TCP + */ + int is_tcp; + + /** + * Data that should be transmitted or source-port. + */ + uint16_t data; + + /** + * Advertised port to the other peer. + */ + uint16_t adv_port; + + /** + * Status code to be reported to the timeout/status call + */ + enum GNUNET_NAT_StatusCode status; +}; + + +/** + * Function called from #GNUNET_NAT_register whenever someone asks us + * to do connection reversal. + * + * @param cls closure, our `struct GNUNET_NAT_Handle` + * @param addr public IP address of the other peer + * @param addrlen actual lenght of the @a addr + */ +static void +reversal_cb (void *cls, + const struct sockaddr *addr, + socklen_t addrlen) +{ + struct GNUNET_NAT_Test *h = cls; + const struct sockaddr_in *sa; + + if (sizeof (struct sockaddr_in) != addrlen) + return; + sa = (const struct sockaddr_in *) addr; + if (h->data != sa->sin_port) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received connection reversal request for wrong port\n"); + return; /* wrong port */ + } + /* report success */ + h->report (h->report_cls, + GNUNET_NAT_ERROR_SUCCESS); +} + + +/** + * Activity on our incoming socket. Read data from the + * incoming connection. + * + * @param cls the `struct GNUNET_NAT_Test` + */ +static void +do_udp_read (void *cls) +{ + struct GNUNET_NAT_Test *tst = cls; + uint16_t data; + const struct GNUNET_SCHEDULER_TaskContext *tc; + + tc = GNUNET_SCHEDULER_get_task_context (); + tst->ltask = + GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + tst->lsock, + &do_udp_read, + tst); + if ((NULL != tc->write_ready) && + (GNUNET_NETWORK_fdset_isset (tc->read_ready, + tst->lsock)) && + (sizeof (data) == + GNUNET_NETWORK_socket_recv (tst->lsock, + &data, + sizeof (data)))) + { + if (data == tst->data) + tst->report (tst->report_cls, + GNUNET_NAT_ERROR_SUCCESS); + else + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received data mismatches expected value\n"); + } + else + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Failed to receive data from inbound connection\n"); +} + + +/** + * Activity on our incoming socket. Read data from the + * incoming connection. + * + * @param cls the `struct NatActivity` + */ +static void +do_read (void *cls) +{ + struct NatActivity *na = cls; + struct GNUNET_NAT_Test *tst; + uint16_t data; + const struct GNUNET_SCHEDULER_TaskContext *tc; + + tc = GNUNET_SCHEDULER_get_task_context (); + na->rtask = NULL; + tst = na->h; + GNUNET_CONTAINER_DLL_remove (tst->na_head, + tst->na_tail, + na); + if ((NULL != tc->write_ready) && + (GNUNET_NETWORK_fdset_isset (tc->read_ready, + na->sock)) && + (sizeof (data) == + GNUNET_NETWORK_socket_recv (na->sock, + &data, + sizeof (data)))) + { + if (data == tst->data) + tst->report (tst->report_cls, + GNUNET_NAT_ERROR_SUCCESS); + else + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received data does not match expected value\n"); + } + else + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Failed to receive data from inbound connection\n"); + GNUNET_NETWORK_socket_close (na->sock); + GNUNET_free (na); +} + + +/** + * Activity on our listen socket. Accept the + * incoming connection. + * + * @param cls the `struct GNUNET_NAT_Test` + */ +static void +do_accept (void *cls) +{ + struct GNUNET_NAT_Test *tst = cls; + struct GNUNET_NETWORK_Handle *s; + struct NatActivity *wl; + + tst->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + tst->lsock, + &do_accept, + tst); + s = GNUNET_NETWORK_socket_accept (tst->lsock, + NULL, + NULL); + if (NULL == s) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, + "accept"); + return; /* odd error */ + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got an inbound connection, waiting for data\n"); + wl = GNUNET_new (struct NatActivity); + wl->sock = s; + wl->h = tst; + wl->rtask = + GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + wl->sock, + &do_read, + wl); + GNUNET_CONTAINER_DLL_insert (tst->na_head, + tst->na_tail, + wl); +} + + +/** + * We got disconnected from the NAT server. Stop + * waiting for a reply. + * + * @param cls the `struct ClientActivity` + * @param error error code + */ +static void +mq_error_handler (void *cls, + enum GNUNET_MQ_Error error) +{ + struct ClientActivity *ca = cls; + struct GNUNET_NAT_Test *tst = ca->h; + + GNUNET_CONTAINER_DLL_remove (tst->ca_head, + tst->ca_tail, + ca); + GNUNET_MQ_destroy (ca->mq); + GNUNET_free (ca); +} + + +/** + * Address-callback, used to send message to gnunet-nat-server. + * + * @param cls closure + * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean + * the previous (now invalid) one + * @param addr either the previous or the new public IP address + * @param addrlen actual length of the @a addr + */ +static void +addr_cb (void *cls, + int add_remove, + const struct sockaddr *addr, + socklen_t addrlen) +{ + struct GNUNET_NAT_Test *h = cls; + struct ClientActivity *ca; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_NAT_TestMessage *msg; + const struct sockaddr_in *sa; + + if (GNUNET_YES != add_remove) + return; + if (addrlen != sizeof (struct sockaddr_in)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "NAT test ignores IPv6 address `%s' returned from NAT library\n", + GNUNET_a2s (addr, + addrlen)); + return; /* ignore IPv6 here */ + } + LOG (GNUNET_ERROR_TYPE_INFO, + "Asking gnunet-nat-server to connect to `%s'\n", + GNUNET_a2s (addr, + addrlen)); + + ca = GNUNET_new (struct ClientActivity); + ca->h = h; + ca->mq = GNUNET_CLIENT_connecT (h->cfg, + "gnunet-nat-server", + NULL, + &mq_error_handler, + ca); + if (NULL == ca->mq) + { + GNUNET_free (ca); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to connect to `gnunet-nat-server'\n")); + return; + } + GNUNET_CONTAINER_DLL_insert (h->ca_head, + h->ca_tail, + ca); + sa = (const struct sockaddr_in *) addr; + env = GNUNET_MQ_msg (msg, + GNUNET_MESSAGE_TYPE_NAT_TEST); + msg->dst_ipv4 = sa->sin_addr.s_addr; + msg->dport = sa->sin_port; + msg->data = h->data; + msg->is_tcp = htonl ((uint32_t) h->is_tcp); + GNUNET_MQ_send (ca->mq, + env); +} + + +/** + * Timeout task for a nat test. + * Calls the report-callback with a timeout return value + * + * Destroys the nat handle after the callback has been processed. + * + * @param cls handle to the timed out NAT test + */ +static void +do_timeout (void *cls) +{ + struct GNUNET_NAT_Test *nh = cls; + + nh->ttask = NULL; + nh->report (nh->report_cls, + (GNUNET_NAT_ERROR_SUCCESS == nh->status) + ? GNUNET_NAT_ERROR_TIMEOUT + : nh->status); +} + + +/** + * Start testing if NAT traversal works using the + * given configuration (IPv4-only). + * + * ALL failures are reported directly to the report callback + * + * @param cfg configuration for the NAT traversal + * @param is_tcp #GNUNET_YES to test TCP, #GNUNET_NO to test UDP + * @param bnd_port port to bind to, 0 for connection reversal + * @param adv_port externally advertised port to use + * @param timeout delay after which the test should be aborted + * @param report function to call with the result of the test + * @param report_cls closure for @a report + * @return handle to cancel NAT test or NULL. The error is always indicated via the report callback + */ +struct GNUNET_NAT_Test * +GNUNET_NAT_test_start (const struct GNUNET_CONFIGURATION_Handle *cfg, + int is_tcp, + uint16_t bnd_port, + uint16_t adv_port, + struct GNUNET_TIME_Relative timeout, + GNUNET_NAT_TestCallback report, + void *report_cls) +{ + struct GNUNET_NAT_Test *nh; + struct sockaddr_in sa; + const struct sockaddr *addrs[] = { + (const struct sockaddr *) &sa + }; + const socklen_t addrlens[] = { + sizeof (sa) + }; + + memset (&sa, 0, sizeof (sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons (bnd_port); +#if HAVE_SOCKADDR_IN_SIN_LEN + sa.sin_len = sizeof (sa); +#endif + + nh = GNUNET_new (struct GNUNET_NAT_Test); + nh->cfg = cfg; + nh->is_tcp = is_tcp; + nh->data = bnd_port; + nh->adv_port = adv_port; + nh->report = report; + nh->report_cls = report_cls; + nh->status = GNUNET_NAT_ERROR_SUCCESS; + if (0 == bnd_port) + { + nh->nat + = GNUNET_NAT_register (cfg, + is_tcp, + 0, + 0, + NULL, + NULL, + &addr_cb, + &reversal_cb, + nh, + NULL); + } + else + { + nh->lsock = + GNUNET_NETWORK_socket_create (AF_INET, + (is_tcp == + GNUNET_YES) ? SOCK_STREAM : SOCK_DGRAM, + 0); + if ((nh->lsock == NULL) || + (GNUNET_OK != + GNUNET_NETWORK_socket_bind (nh->lsock, + (const struct sockaddr *) &sa, + sizeof (sa)))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to create listen socket bound to `%s' for NAT test: %s\n"), + GNUNET_a2s ((const struct sockaddr *) &sa, + sizeof (sa)), + STRERROR (errno)); + if (NULL != nh->lsock) + { + GNUNET_NETWORK_socket_close (nh->lsock); + nh->lsock = NULL; + } + nh->status = GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR; + nh->ttask = GNUNET_SCHEDULER_add_now (&do_timeout, + nh); + return nh; + } + if (GNUNET_YES == is_tcp) + { + GNUNET_break (GNUNET_OK == + GNUNET_NETWORK_socket_listen (nh->lsock, + 5)); + nh->ltask = + GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + nh->lsock, + &do_accept, + nh); + } + else + { + nh->ltask = + GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + nh->lsock, + &do_udp_read, + nh); + } + LOG (GNUNET_ERROR_TYPE_INFO, + "NAT test listens on port %u (%s)\n", + bnd_port, + (GNUNET_YES == is_tcp) ? "tcp" : "udp"); + nh->nat = GNUNET_NAT_register (cfg, + is_tcp, + adv_port, + 1, + addrs, + addrlens, + &addr_cb, + NULL, + nh, + NULL); + if (NULL == nh->nat) + { + LOG (GNUNET_ERROR_TYPE_INFO, + _("NAT test failed to start NAT library\n")); + if (NULL != nh->ltask) + { + GNUNET_SCHEDULER_cancel (nh->ltask); + nh->ltask = NULL; + } + if (NULL != nh->lsock) + { + GNUNET_NETWORK_socket_close (nh->lsock); + nh->lsock = NULL; + } + nh->status = GNUNET_NAT_ERROR_NAT_REGISTER_FAILED; + nh->ttask = GNUNET_SCHEDULER_add_now (&do_timeout, + nh); + return nh; + } + } + nh->ttask = GNUNET_SCHEDULER_add_delayed (timeout, + &do_timeout, + nh); + return nh; +} + + +/** + * Stop an active NAT test. + * + * @param tst test to stop. + */ +void +GNUNET_NAT_test_stop (struct GNUNET_NAT_Test *tst) +{ + struct NatActivity *pos; + struct ClientActivity *cpos; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Stopping NAT test\n"); + while (NULL != (cpos = tst->ca_head)) + { + GNUNET_CONTAINER_DLL_remove (tst->ca_head, + tst->ca_tail, + cpos); + GNUNET_MQ_destroy (cpos->mq); + GNUNET_free (cpos); + } + while (NULL != (pos = tst->na_head)) + { + GNUNET_CONTAINER_DLL_remove (tst->na_head, + tst->na_tail, + pos); + GNUNET_SCHEDULER_cancel (pos->rtask); + GNUNET_NETWORK_socket_close (pos->sock); + GNUNET_free (pos); + } + if (NULL != tst->ttask) + { + GNUNET_SCHEDULER_cancel (tst->ttask); + tst->ttask = NULL; + } + if (NULL != tst->ltask) + { + GNUNET_SCHEDULER_cancel (tst->ltask); + tst->ltask = NULL; + } + if (NULL != tst->lsock) + { + GNUNET_NETWORK_socket_close (tst->lsock); + tst->lsock = NULL; + } + if (NULL != tst->nat) + { + GNUNET_NAT_unregister (tst->nat); + tst->nat = NULL; + } + GNUNET_free (tst); +} + +/* end of nat_test.c */ diff --git a/src/nat/Makefile.am b/src/nat/Makefile.am index b2b9c4f50..456ddfb62 100644 --- a/src/nat/Makefile.am +++ b/src/nat/Makefile.am @@ -34,7 +34,6 @@ install-exec-hook: endif bin_PROGRAMS = \ - gnunet-nat-server \ gnunet-nat libexec_PROGRAMS = \ @@ -42,12 +41,6 @@ libexec_PROGRAMS = \ gnunet-service-nat -gnunet_nat_server_SOURCES = \ - gnunet-nat-server.c nat.h -gnunet_nat_server_LDADD = \ - libgnunetnat.la \ - $(top_builddir)/src/util/libgnunetutil.la - gnunet_helper_nat_server_SOURCES = \ $(NATSERVER) @@ -85,9 +78,7 @@ libgnunetnat_la_LDFLAGS = \ libgnunetnatnew_la_SOURCES = \ nat_api.c \ - nat_api_auto.c \ nat_api_stun.c nat_stun.h \ - nat_api_test.c \ nat.h libgnunetnatnew_la_LIBADD = \ $(top_builddir)/src/util/libgnunetutil.la \ diff --git a/src/nat/gnunet-nat-server.c b/src/nat/gnunet-nat-server.c deleted file mode 100644 index 1692a8ef1..000000000 --- a/src/nat/gnunet-nat-server.c +++ /dev/null @@ -1,384 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2011 GNUnet e.V. - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, 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 - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file src/nat/gnunet-nat-server.c - * @brief Daemon to run on 'gnunet.org' to help test NAT traversal code - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_nat_lib.h" -#include "gnunet_protocols.h" -#include "nat.h" - - -/** - * Our server. - */ -static struct GNUNET_SERVER_Handle *server; - -/** - * Our configuration. - */ -static const struct GNUNET_CONFIGURATION_Handle *cfg; - - -/** - * Try contacting the peer using autonomous NAT traveral method. - * - * @param dst_ipv4 IPv4 address to send the fake ICMP message - * @param dport destination port to include in ICMP message - * @param is_tcp mark for TCP (#GNUNET_YES) or UDP (#GNUNET_NO) - */ -static void -try_anat (uint32_t dst_ipv4, - uint16_t dport, - int is_tcp) -{ - struct GNUNET_NAT_Handle *h; - struct sockaddr_in sa; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Asking for connection reversal with %x and code %u\n", - (unsigned int) dst_ipv4, - (unsigned int) dport); - h = GNUNET_NAT_register (cfg, - is_tcp, - dport, - 0, - NULL, NULL, NULL, NULL, NULL, NULL); - memset (&sa, 0, sizeof (sa)); - sa.sin_family = AF_INET; -#if HAVE_SOCKADDR_IN_SIN_LEN - sa.sin_len = sizeof (sa); -#endif - sa.sin_addr.s_addr = dst_ipv4; - GNUNET_NAT_run_client (h, &sa); - GNUNET_NAT_unregister (h); -} - - -/** - * Closure for #tcp_send. - */ -struct TcpContext -{ - /** - * TCP socket. - */ - struct GNUNET_NETWORK_Handle *s; - - /** - * Data to transmit. - */ - uint16_t data; -}; - - -/** - * Task called by the scheduler once we can do the TCP send - * (or once we failed to connect...). - * - * @param cls the `struct TcpContext` - */ -static void -tcp_send (void *cls) -{ - struct TcpContext *ctx = cls; - const struct GNUNET_SCHEDULER_TaskContext *tc; - - tc = GNUNET_SCHEDULER_get_task_context (); - if ((NULL != tc->write_ready) && - (GNUNET_NETWORK_fdset_isset (tc->write_ready, ctx->s))) - { - if (-1 == - GNUNET_NETWORK_socket_send (ctx->s, &ctx->data, sizeof (ctx->data))) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "send"); - } - GNUNET_NETWORK_socket_shutdown (ctx->s, SHUT_RDWR); - } - GNUNET_NETWORK_socket_close (ctx->s); - GNUNET_free (ctx); -} - - -/** - * Try to send @a data to the - * IP @a dst_ipv4' at port @a dport via TCP. - * - * @param dst_ipv4 target IP - * @param dport target port - * @param data data to send - */ -static void -try_send_tcp (uint32_t dst_ipv4, - uint16_t dport, - uint16_t data) -{ - struct GNUNET_NETWORK_Handle *s; - struct sockaddr_in sa; - struct TcpContext *ctx; - - s = GNUNET_NETWORK_socket_create (AF_INET, - SOCK_STREAM, - 0); - if (NULL == s) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "socket"); - return; - } - memset (&sa, 0, sizeof (sa)); - sa.sin_family = AF_INET; -#if HAVE_SOCKADDR_IN_SIN_LEN - sa.sin_len = sizeof (sa); -#endif - sa.sin_addr.s_addr = dst_ipv4; - sa.sin_port = htons (dport); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending TCP message to `%s'\n", - GNUNET_a2s ((struct sockaddr *) &sa, - sizeof (sa))); - if ( (GNUNET_OK != - GNUNET_NETWORK_socket_connect (s, - (const struct sockaddr *) &sa, - sizeof (sa))) && - (errno != EINPROGRESS) ) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "connect"); - GNUNET_NETWORK_socket_close (s); - return; - } - ctx = GNUNET_new (struct TcpContext); - ctx->s = s; - ctx->data = data; - GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_SECONDS, - s, - &tcp_send, - ctx); -} - - -/** - * Try to send @a data to the - * IP @a dst_ipv4 at port @a dport via UDP. - * - * @param dst_ipv4 target IP - * @param dport target port - * @param data data to send - */ -static void -try_send_udp (uint32_t dst_ipv4, - uint16_t dport, - uint16_t data) -{ - struct GNUNET_NETWORK_Handle *s; - struct sockaddr_in sa; - - s = GNUNET_NETWORK_socket_create (AF_INET, - SOCK_DGRAM, - 0); - if (NULL == s) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "socket"); - return; - } - memset (&sa, 0, sizeof (sa)); - sa.sin_family = AF_INET; -#if HAVE_SOCKADDR_IN_SIN_LEN - sa.sin_len = sizeof (sa); -#endif - sa.sin_addr.s_addr = dst_ipv4; - sa.sin_port = htons (dport); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending UDP packet to `%s'\n", - GNUNET_a2s ((struct sockaddr *) &sa, - sizeof (sa))); - if (-1 == - GNUNET_NETWORK_socket_sendto (s, - &data, - sizeof (data), - (const struct sockaddr *) &sa, - sizeof (sa))) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "sendto"); - GNUNET_NETWORK_socket_close (s); -} - - -/** - * We've received a request to probe a NAT - * traversal. Do it. - * - * @param cls unused - * @param client handle to client (we always close) - * @param msg message with details about what to test - */ -static void -test (void *cls, - struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *msg) -{ - const struct GNUNET_NAT_TestMessage *tm; - uint16_t dport; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received test request\n"); - tm = (const struct GNUNET_NAT_TestMessage *) msg; - dport = ntohs (tm->dport); - if (0 == dport) - try_anat (tm->dst_ipv4, - ntohs (tm->data), - (int) ntohl (tm->is_tcp)); - else if (GNUNET_YES == ntohl (tm->is_tcp)) - try_send_tcp (tm->dst_ipv4, - dport, - tm->data); - else - try_send_udp (tm->dst_ipv4, - dport, - tm->data); - GNUNET_SERVER_receive_done (client, - GNUNET_NO); -} - - -/** - * Task run during shutdown. - * - * @param cls unused - */ -static void -shutdown_task (void *cls) -{ - GNUNET_SERVER_destroy (server); - server = NULL; -} - - -/** - * Main function that will be run. - * - * @param cls closure - * @param args remaining command-line arguments - * @param cfgfile name of the configuration file used (for saving, can be NULL!) - * @param c configuration - */ -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *c) -{ - static const struct GNUNET_SERVER_MessageHandler handlers[] = { - {&test, NULL, GNUNET_MESSAGE_TYPE_NAT_TEST, - sizeof (struct GNUNET_NAT_TestMessage)}, - {NULL, NULL, 0, 0} - }; - unsigned int port; - struct sockaddr_in in4; - struct sockaddr_in6 in6; - - socklen_t slen[] = { - sizeof (in4), - sizeof (in6), - 0 - }; - struct sockaddr *sa[] = { - (struct sockaddr *) &in4, - (struct sockaddr *) &in6, - NULL - }; - - cfg = c; - if ( (NULL == args[0]) || - (1 != SSCANF (args[0], "%u", &port)) || - (0 == port) || - (65536 <= port) ) - { - FPRINTF (stderr, - _("Please pass valid port number as the first argument! (got `%s')\n"), - args[0]); - return; - } - memset (&in4, 0, sizeof (in4)); - memset (&in6, 0, sizeof (in6)); - in4.sin_family = AF_INET; - in4.sin_port = htons ((uint16_t) port); - in6.sin6_family = AF_INET6; - in6.sin6_port = htons ((uint16_t) port); -#if HAVE_SOCKADDR_IN_SIN_LEN - in4.sin_len = sizeof (in4); - in6.sin6_len = sizeof (in6); -#endif - server = GNUNET_SERVER_create (NULL, - NULL, - (struct sockaddr * const *) sa, - slen, - GNUNET_TIME_UNIT_SECONDS, - GNUNET_YES); - GNUNET_SERVER_add_handlers (server, - handlers); - GNUNET_SCHEDULER_add_shutdown (&shutdown_task, - NULL); -} - - -/** - * Main function of gnunet-nat-server. - * - * @param argc number of command-line arguments - * @param argv command line - * @return 0 on success, -1 on error - */ -int -main (int argc, char *const argv[]) -{ - static const struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - - if (GNUNET_OK != - GNUNET_STRINGS_get_utf8_args (argc, argv, - &argc, &argv)) - return 2; - - if (GNUNET_OK != - GNUNET_PROGRAM_run (argc, - argv, - "gnunet-nat-server [options] PORT", - _("GNUnet NAT traversal test helper daemon"), - options, - &run, - NULL)) - { - GNUNET_free ((void*) argv); - return 1; - } - GNUNET_free ((void*) argv); - return 0; -} - - -/* end of gnunet-nat-server.c */ diff --git a/src/nat/gnunet-nat.c b/src/nat/gnunet-nat.c index 4171babf9..02d68d787 100644 --- a/src/nat/gnunet-nat.c +++ b/src/nat/gnunet-nat.c @@ -33,11 +33,6 @@ */ static int global_ret; -/** - * Handle to ongoing autoconfiguration. - */ -static struct GNUNET_NAT_AutoHandle *ah; - /** * Name of section in configuration file to use for * additional options. @@ -59,39 +54,11 @@ static int listen_reversal; */ static int use_tcp; -/** - * If we do auto-configuration, should we write the result - * to a file? - */ -static int write_cfg; - -/** - * Configuration filename. - */ -static const char *cfg_file; - -/** - * Original configuration. - */ -static const struct GNUNET_CONFIGURATION_Handle *cfg; - /** * Protocol to use. */ static uint8_t proto; -/** - * Address we are bound to (in test), or should bind to - * (if #do_stun is set). - */ -static char *bind_addr; - -/** - * External IP address and port to use for the test. - * If not set, use #bind_addr. - */ -static char *extern_addr; - /** * Local address to use for connection reversal request. */ @@ -107,16 +74,6 @@ static char *remote_addr; */ static unsigned int do_stun; -/** - * Should we run autoconfiguration? - */ -static unsigned int do_auto; - -/** - * Handle to a NAT test operation. - */ -static struct GNUNET_NAT_Test *nt; - /** * Handle to NAT operation. */ @@ -140,10 +97,6 @@ static struct GNUNET_SCHEDULER_Task *rtask; static void test_finished () { - if (NULL != ah) - return; - if (NULL != nt) - return; if (NULL != nh) return; if (NULL != rtask) @@ -152,160 +105,6 @@ test_finished () } -/** - * Function to iterate over sugested changes options - * - * @param cls closure - * @param section name of the section - * @param option name of the option - * @param value value of the option - */ -static void -auto_conf_iter (void *cls, - const char *section, - const char *option, - const char *value) -{ - struct GNUNET_CONFIGURATION_Handle *new_cfg = cls; - - PRINTF ("%s: %s\n", - option, - value); - if (NULL != new_cfg) - GNUNET_CONFIGURATION_set_value_string (new_cfg, - section, - option, - value); -} - - -/** - * Function called with the result from the autoconfiguration. - * - * @param cls closure - * @param diff minimal suggested changes to the original configuration - * to make it work (as best as we can) - * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code - * @param type what the situation of the NAT - */ -static void -auto_config_cb (void *cls, - const struct GNUNET_CONFIGURATION_Handle *diff, - enum GNUNET_NAT_StatusCode result, - enum GNUNET_NAT_Type type) -{ - const char *nat_type; - char unknown_type[64]; - struct GNUNET_CONFIGURATION_Handle *new_cfg; - - ah = NULL; - switch (type) - { - case GNUNET_NAT_TYPE_NO_NAT: - nat_type = "NO NAT"; - break; - case GNUNET_NAT_TYPE_UNREACHABLE_NAT: - nat_type = "NAT but we can traverse"; - break; - case GNUNET_NAT_TYPE_STUN_PUNCHED_NAT: - nat_type = "NAT but STUN is able to identify the correct information"; - break; - case GNUNET_NAT_TYPE_UPNP_NAT: - nat_type = "NAT but UPNP opened the ports"; - break; - default: - SPRINTF (unknown_type, - "NAT unknown, type %u", - type); - nat_type = unknown_type; - break; - } - - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - "NAT status: %s/%s\n", - GNUNET_NAT_status2string (result), - nat_type); - - /* Shortcut: if there are no changes suggested, bail out early. */ - if (GNUNET_NO == - GNUNET_CONFIGURATION_is_dirty (diff)) - { - test_finished (); - return; - } - - /* Apply diff to original configuration and show changes - to the user */ - new_cfg = write_cfg ? GNUNET_CONFIGURATION_dup (cfg) : NULL; - - if (NULL != diff) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - _("Suggested configuration changes:\n")); - GNUNET_CONFIGURATION_iterate_section_values (diff, - "nat", - &auto_conf_iter, - new_cfg); - } - - /* If desired, write configuration to file; we write only the - changes to the defaults to keep things compact. */ - if ( (write_cfg) && - (NULL != diff) ) - { - struct GNUNET_CONFIGURATION_Handle *def_cfg; - - GNUNET_CONFIGURATION_set_value_string (new_cfg, - "ARM", - "CONFIG", - NULL); - def_cfg = GNUNET_CONFIGURATION_create (); - GNUNET_break (GNUNET_OK == - GNUNET_CONFIGURATION_load (def_cfg, - NULL)); - if (GNUNET_OK != - GNUNET_CONFIGURATION_write_diffs (def_cfg, - new_cfg, - cfg_file)) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - _("Failed to write configuration to `%s'\n"), - cfg_file); - global_ret = 1; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - _("Wrote updated configuration to `%s'\n"), - cfg_file); - } - GNUNET_CONFIGURATION_destroy (def_cfg); - } - - if (NULL != new_cfg) - GNUNET_CONFIGURATION_destroy (new_cfg); - test_finished (); -} - - -/** - * Function called to report success or failure for - * NAT configuration test. - * - * @param cls closure - * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code - */ -static void -test_report_cb (void *cls, - enum GNUNET_NAT_StatusCode result) -{ - nt = NULL; - PRINTF ("NAT test result: %s\n", - GNUNET_NAT_status2string (result)); - test_finished (); -} - - /** * Signature of the callback passed to #GNUNET_NAT_register() for * a function to call whenever our set of 'valid' addresses changes. @@ -362,16 +161,6 @@ reversal_cb (void *cls, static void do_shutdown (void *cls) { - if (NULL != ah) - { - GNUNET_NAT_autoconfig_cancel (ah); - ah = NULL; - } - if (NULL != nt) - { - GNUNET_NAT_test_stop (nt); - nt = NULL; - } if (NULL != nh) { GNUNET_NAT_unregister (nh); @@ -452,16 +241,11 @@ run (void *cls, const struct GNUNET_CONFIGURATION_Handle *c) { uint8_t af; - struct sockaddr_in bind_sa; - struct sockaddr_in extern_sa; struct sockaddr *local_sa; struct sockaddr *remote_sa; socklen_t local_len; size_t remote_len; - cfg_file = cfgfile; - cfg = c; - if (use_tcp && use_udp) { GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, @@ -478,50 +262,13 @@ run (void *cls, GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); - if (do_auto) - { - ah = GNUNET_NAT_autoconfig_start (c, - &auto_config_cb, - NULL); - } - if (0 == proto) { - if (do_auto) - return; /* all good, we just run auto config */ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Must specify either TCP or UDP\n"); global_ret = 1; return; } - if (NULL != bind_addr) - { - if (GNUNET_OK != - GNUNET_STRINGS_to_address_ipv4 (bind_addr, - strlen (bind_addr), - &bind_sa)) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - "Invalid socket address `%s'\n", - bind_addr); - global_ret = 1; - return; - } - } - if (NULL != extern_addr) - { - if (GNUNET_OK != - GNUNET_STRINGS_to_address_ipv4 (extern_addr, - strlen (extern_addr), - &extern_sa)) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - "Invalid socket address `%s'\n", - extern_addr); - global_ret = 1; - return; - } - } if (NULL != local_addr) { local_len = (socklen_t) GNUNET_STRINGS_parse_socket_addr (local_addr, @@ -551,20 +298,6 @@ run (void *cls, } } - if (NULL != bind_addr) - { - if (NULL == extern_addr) - extern_sa = bind_sa; - nt = GNUNET_NAT_test_start (c, - proto, - bind_sa.sin_addr, - ntohs (bind_sa.sin_port), - extern_sa.sin_addr, - ntohs (extern_sa.sin_port), - &test_report_cb, - NULL); - } - if (NULL != local_addr) { nh = GNUNET_NAT_register (c, @@ -683,15 +416,6 @@ main (int argc, char *const argv[]) { static const struct GNUNET_GETOPT_CommandLineOption options[] = { - {'a', "auto", NULL, - gettext_noop ("run autoconfiguration"), - GNUNET_NO, &GNUNET_GETOPT_set_one, &do_auto }, - {'b', "bind", "ADDRESS", - gettext_noop ("which IP and port are we bound to"), - GNUNET_YES, &GNUNET_GETOPT_set_string, &bind_addr }, - {'e', "external", "ADDRESS", - gettext_noop ("which external IP and port should be used to test"), - GNUNET_YES, &GNUNET_GETOPT_set_string, &extern_addr }, {'i', "in", "ADDRESS", gettext_noop ("which IP and port are we locally using to bind/listen to"), GNUNET_YES, &GNUNET_GETOPT_set_string, &local_addr }, @@ -710,9 +434,6 @@ main (int argc, {'u', "udp", NULL, gettext_noop ("use UDP"), GNUNET_NO, &GNUNET_GETOPT_set_one, &use_udp }, - {'w', "write", NULL, - gettext_noop ("write configuration file (for autoconfiguration)"), - GNUNET_NO, &GNUNET_GETOPT_set_one, &write_cfg }, {'W', "watch", NULL, gettext_noop ("watch for connection reversal requests"), GNUNET_NO, &GNUNET_GETOPT_set_one, &listen_reversal }, diff --git a/src/nat/gnunet-service-nat.c b/src/nat/gnunet-service-nat.c index 79f806f27..2cb2b30ff 100644 --- a/src/nat/gnunet-service-nat.c +++ b/src/nat/gnunet-service-nat.c @@ -283,80 +283,6 @@ struct StunExternalIP }; -/** - * Context for autoconfiguration operations. - */ -struct AutoconfigContext -{ - /** - * Kept in a DLL. - */ - struct AutoconfigContext *prev; - - /** - * Kept in a DLL. - */ - struct AutoconfigContext *next; - - /** - * Which client asked the question. - */ - struct ClientHandle *ch; - - /** - * Configuration we are creating. - */ - struct GNUNET_CONFIGURATION_Handle *c; - - /** - * Original configuration (for diffing). - */ - struct GNUNET_CONFIGURATION_Handle *orig; - - /** - * Timeout task to force termination. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; - - /** - * What type of system are we on? - */ - char *system_type; - - /** - * Handle to activity to probe for our external IP. - */ - struct GNUNET_NAT_ExternalHandle *probe_external; - - /** - * #GNUNET_YES if upnpc should be used, - * #GNUNET_NO if upnpc should not be used, - * #GNUNET_SYSERR if we should simply not change the option. - */ - int enable_upnpc; - - /** - * Status code to return to the client. - */ - enum GNUNET_NAT_StatusCode status_code; - - /** - * NAT type to return to the client. - */ - enum GNUNET_NAT_Type type; -}; - - -/** - * DLL of our autoconfiguration operations. - */ -static struct AutoconfigContext *ac_head; - -/** - * DLL of our autoconfiguration operations. - */ -static struct AutoconfigContext *ac_tail; - /** * Timeout to use when STUN data is considered stale. */ @@ -1919,274 +1845,6 @@ handle_request_connection_reversal (void *cls, } -/** - * Check validity of #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG 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_autoconfig_request (void *cls, - const struct GNUNET_NAT_AutoconfigRequestMessage *message) -{ - return GNUNET_OK; /* checked later */ -} - - -/** - * Stop all pending activities with respect to the @a ac - * - * @param ac autoconfiguration to terminate activities for - */ -static void -terminate_ac_activities (struct AutoconfigContext *ac) -{ - if (NULL != ac->probe_external) - { - GNUNET_NAT_mini_get_external_ipv4_cancel_ (ac->probe_external); - ac->probe_external = NULL; - } - if (NULL != ac->timeout_task) - { - GNUNET_SCHEDULER_cancel (ac->timeout_task); - ac->timeout_task = NULL; - } -} - - -/** - * Finish handling the autoconfiguration request and send - * the response to the client. - * - * @param cls the `struct AutoconfigContext` to conclude - */ -static void -conclude_autoconfig_request (void *cls) -{ - struct AutoconfigContext *ac = cls; - struct ClientHandle *ch = ac->ch; - struct GNUNET_NAT_AutoconfigResultMessage *arm; - struct GNUNET_MQ_Envelope *env; - size_t c_size; - char *buf; - struct GNUNET_CONFIGURATION_Handle *diff; - - ac->timeout_task = NULL; - terminate_ac_activities (ac); - - /* Send back response */ - diff = GNUNET_CONFIGURATION_get_diff (ac->orig, - ac->c); - buf = GNUNET_CONFIGURATION_serialize (diff, - &c_size); - GNUNET_CONFIGURATION_destroy (diff); - env = GNUNET_MQ_msg_extra (arm, - c_size, - GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT); - arm->status_code = htonl ((uint32_t) ac->status_code); - arm->type = htonl ((uint32_t) ac->type); - GNUNET_memcpy (&arm[1], - buf, - c_size); - GNUNET_free (buf); - GNUNET_MQ_send (ch->mq, - env); - - /* clean up */ - GNUNET_free (ac->system_type); - GNUNET_CONFIGURATION_destroy (ac->orig); - GNUNET_CONFIGURATION_destroy (ac->c); - GNUNET_CONTAINER_DLL_remove (ac_head, - ac_tail, - ac); - GNUNET_free (ac); - GNUNET_SERVICE_client_continue (ch->client); -} - - -/** - * Check if all autoconfiguration operations have concluded, - * and if they have, send the result back to the client. - * - * @param ac autoconfiguation context to check - */ -static void -check_autoconfig_finished (struct AutoconfigContext *ac) -{ - if (NULL != ac->probe_external) - return; - GNUNET_SCHEDULER_cancel (ac->timeout_task); - ac->timeout_task - = GNUNET_SCHEDULER_add_now (&conclude_autoconfig_request, - ac); -} - - -/** - * Update ENABLE_UPNPC configuration option. - * - * @param ac autoconfiguration to update - */ -static void -update_enable_upnpc_option (struct AutoconfigContext *ac) -{ - switch (ac->enable_upnpc) - { - case GNUNET_YES: - GNUNET_CONFIGURATION_set_value_string (ac->c, - "NAT", - "ENABLE_UPNP", - "YES"); - break; - case GNUNET_NO: - GNUNET_CONFIGURATION_set_value_string (ac->c, - "NAT", - "ENABLE_UPNP", - "NO"); - break; - case GNUNET_SYSERR: - /* We are unsure, do not change option */ - break; - } -} - - -/** - * Handle result from external IP address probe during - * autoconfiguration. - * - * @param cls our `struct AutoconfigContext` - * @param addr the address, NULL on errors - * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code - */ -static void -auto_external_result_cb (void *cls, - const struct in_addr *addr, - enum GNUNET_NAT_StatusCode result) -{ - struct AutoconfigContext *ac = cls; - - ac->probe_external = NULL; - switch (result) - { - case GNUNET_NAT_ERROR_SUCCESS: - ac->enable_upnpc = GNUNET_YES; - break; - case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID: - case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID: - case GNUNET_NAT_ERROR_IPC_FAILURE: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Disabling UPNPC: %d\n", - (int) result); - ac->enable_upnpc = GNUNET_NO; /* did not work */ - break; - default: - GNUNET_break (0); /* unexpected */ - ac->enable_upnpc = GNUNET_SYSERR; - break; - } - update_enable_upnpc_option (ac); - check_autoconfig_finished (ac); -} - - -/** - * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message from - * client. - * - * @param cls client who sent the message - * @param message the message received - */ -static void -handle_autoconfig_request (void *cls, - const struct GNUNET_NAT_AutoconfigRequestMessage *message) -{ - struct ClientHandle *ch = cls; - size_t left = ntohs (message->header.size) - sizeof (*message); - struct LocalAddressList *lal; - struct AutoconfigContext *ac; - - ac = GNUNET_new (struct AutoconfigContext); - ac->status_code = GNUNET_NAT_ERROR_SUCCESS; - ac->ch = ch; - ac->c = GNUNET_CONFIGURATION_create (); - if (GNUNET_OK != - GNUNET_CONFIGURATION_deserialize (ac->c, - (const char *) &message[1], - left, - GNUNET_NO)) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (ch->client); - GNUNET_CONFIGURATION_destroy (ac->c); - GNUNET_free (ac); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received REQUEST_AUTO_CONFIG message from client\n"); - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (ac->c, - "PEER", - "SYSTEM_TYPE", - &ac->system_type)) - ac->system_type = GNUNET_strdup ("UNKNOWN"); - - GNUNET_CONTAINER_DLL_insert (ac_head, - ac_tail, - ac); - ac->orig - = GNUNET_CONFIGURATION_dup (ac->c); - ac->timeout_task - = GNUNET_SCHEDULER_add_delayed (AUTOCONFIG_TIMEOUT, - &conclude_autoconfig_request, - ac); - ac->enable_upnpc = GNUNET_SYSERR; /* undecided */ - - /* Probe for upnpc */ - if (GNUNET_SYSERR == - GNUNET_OS_check_helper_binary ("upnpc", - GNUNET_NO, - NULL)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("UPnP client `upnpc` command not found, disabling UPnP\n")); - ac->enable_upnpc = GNUNET_NO; - } - else - { - for (lal = lal_head; NULL != lal; lal = lal->next) - if (GNUNET_NAT_AC_LAN == (lal->ac & GNUNET_NAT_AC_LAN)) - /* we are behind NAT, useful to try upnpc */ - ac->enable_upnpc = GNUNET_YES; - } - if (GNUNET_YES == ac->enable_upnpc) - { - /* If we are a mobile device, always leave it on as the network - may change to one that supports UPnP anytime. If we are - stationary, check if our network actually supports UPnP, and if - not, disable it. */ - if ( (0 == strcasecmp (ac->system_type, - "INFRASTRUCTURE")) || - (0 == strcasecmp (ac->system_type, - "DESKTOP")) ) - { - /* Check if upnpc gives us an external IP */ - ac->probe_external - = GNUNET_NAT_mini_get_external_ipv4_ (&auto_external_result_cb, - ac); - } - } - if (NULL == ac->probe_external) - update_enable_upnpc_option (ac); - - /* Finally, check if we are already done */ - check_autoconfig_finished (ac); -} - - /** * Task run during shutdown. * @@ -2196,16 +1854,7 @@ static void shutdown_task (void *cls) { struct StunExternalIP *se; - struct AutoconfigContext *ac; - while (NULL != (ac = ac_head)) - { - GNUNET_CONTAINER_DLL_remove (ac_head, - ac_tail, - ac); - terminate_ac_activities (ac); - GNUNET_free (ac); - } while (NULL != (se = se_head)) { GNUNET_CONTAINER_DLL_remove (se_head, @@ -2386,10 +2035,6 @@ GNUNET_SERVICE_MAIN GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL, struct GNUNET_NAT_RequestConnectionReversalMessage, NULL), - GNUNET_MQ_hd_var_size (autoconfig_request, - GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG, - struct GNUNET_NAT_AutoconfigRequestMessage, - NULL), GNUNET_MQ_handler_end ()); diff --git a/src/nat/nat.conf.in b/src/nat/nat.conf.in index 304db3c15..88975c424 100644 --- a/src/nat/nat.conf.in +++ b/src/nat/nat.conf.in @@ -67,8 +67,3 @@ STUN_SERVERS = stun.gnunet.org stun.services.mozilla.com:3478 stun.ekiga.net:347 # After how long do we consider STUN data stale? STUN_STALE = 60 min - -[gnunet-nat-server] -HOSTNAME = gnunet.org -PORT = 5724 -NOARMBIND = YES diff --git a/src/nat/nat.h b/src/nat/nat.h index 02bae71e9..d34900bd1 100644 --- a/src/nat/nat.h +++ b/src/nat/nat.h @@ -224,45 +224,6 @@ struct GNUNET_NAT_AddressChangeNotificationMessage }; -/** - * Client requesting automatic configuration. - */ -struct GNUNET_NAT_AutoconfigRequestMessage -{ - /** - * Header with type #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG - */ - struct GNUNET_MessageHeader header; - - /* Followed by configuration (diff, serialized, compressed) */ - -}; - - -/** - * Service responding with proposed configuration. - */ -struct GNUNET_NAT_AutoconfigResultMessage -{ - /** - * Header with type #GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT - */ - struct GNUNET_MessageHeader header; - - /** - * An `enum GNUNET_NAT_StatusCode` in NBO. - */ - int32_t status_code GNUNET_PACKED; - - /** - * An `enum GNUNET_NAT_Type` in NBO. - */ - int32_t type GNUNET_PACKED; - - /* Followed by configuration (diff, serialized, compressed) */ -}; - - GNUNET_NETWORK_STRUCT_END #endif diff --git a/src/nat/nat_api_auto.c b/src/nat/nat_api_auto.c deleted file mode 100644 index c5c5fa67a..000000000 --- a/src/nat/nat_api_auto.c +++ /dev/null @@ -1,274 +0,0 @@ - -/* - This file is part of GNUnet. - Copyright (C) 2007-2016 GNUnet e.V. - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, 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 - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @author Christian Grothoff - * @author Milan Bouchet-Valat - * - * @file nat/nat_api_auto.c - * Routines for NAT auto configuration. - */ -#include "platform.h" -#include "gnunet_nat_service.h" -#include "nat.h" -#include "nat_stun.h" - - - -/** - * Handle to auto-configuration in progress. - */ -struct GNUNET_NAT_AutoHandle -{ - - /** - * Configuration we use. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Message queue for communicating with the NAT service. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Function called with the result from the autoconfiguration. - */ - GNUNET_NAT_AutoResultCallback arc; - - /** - * Closure for @e arc. - */ - void *arc_cls; - -}; - - -/** - * Converts `enum GNUNET_NAT_StatusCode` to string - * - * @param err error code to resolve to a string - * @return point to a static string containing the error code - */ -const char * -GNUNET_NAT_status2string (enum GNUNET_NAT_StatusCode err) -{ - switch (err) - { - case GNUNET_NAT_ERROR_SUCCESS: - return _ ("Operation Successful"); - case GNUNET_NAT_ERROR_IPC_FAILURE: - return _ ("IPC failure"); - case GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR: - return _ ("Failure in network subsystem, check permissions."); - case GNUNET_NAT_ERROR_TIMEOUT: - return _ ("Encountered timeout while performing operation"); - case GNUNET_NAT_ERROR_NOT_ONLINE: - return _ ("detected that we are offline"); - case GNUNET_NAT_ERROR_UPNPC_NOT_FOUND: - return _ ("`upnpc` command not found"); - case GNUNET_NAT_ERROR_UPNPC_FAILED: - return _ ("Failed to run `upnpc` command"); - case GNUNET_NAT_ERROR_UPNPC_TIMEOUT: - return _ ("`upnpc' command took too long, process killed"); - case GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED: - return _ ("`upnpc' command failed to establish port mapping"); - case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND: - return _ ("`external-ip' command not found"); - case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED: - return _ ("Failed to run `external-ip` command"); - case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID: - return _ ("`external-ip' command output invalid"); - case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID: - return _ ("no valid address was returned by `external-ip'"); - case GNUNET_NAT_ERROR_NO_VALID_IF_IP_COMBO: - return _ ("Could not determine interface with internal/local network address"); - case GNUNET_NAT_ERROR_HELPER_NAT_SERVER_NOT_FOUND: - return _ ("No functioning gnunet-helper-nat-server installation found"); - case GNUNET_NAT_ERROR_NAT_TEST_START_FAILED: - return _ ("NAT test could not be initialized"); - case GNUNET_NAT_ERROR_NAT_TEST_TIMEOUT: - return _ ("NAT test timeout reached"); - case GNUNET_NAT_ERROR_NAT_REGISTER_FAILED: - return _ ("could not register NAT"); - case GNUNET_NAT_ERROR_HELPER_NAT_CLIENT_NOT_FOUND: - return _ ("No working gnunet-helper-nat-client installation found"); - default: - return "unknown status code"; - } -} - - -/** - * Check result from autoconfiguration attempt. - * - * @param cls the `struct GNUNET_NAT_AutoHandle` - * @param res the result - * @return #GNUNET_OK if @a res is well-formed (always for now) - */ -static int -check_auto_result (void *cls, - const struct GNUNET_NAT_AutoconfigResultMessage *res) -{ - return GNUNET_OK; -} - - -/** - * Handle result from autoconfiguration attempt. - * - * @param cls the `struct GNUNET_NAT_AutoHandle` - * @param res the result - */ -static void -handle_auto_result (void *cls, - const struct GNUNET_NAT_AutoconfigResultMessage *res) -{ - struct GNUNET_NAT_AutoHandle *ah = cls; - size_t left; - struct GNUNET_CONFIGURATION_Handle *cfg; - enum GNUNET_NAT_Type type - = (enum GNUNET_NAT_Type) ntohl (res->type); - enum GNUNET_NAT_StatusCode status - = (enum GNUNET_NAT_StatusCode) ntohl (res->status_code); - - left = ntohs (res->header.size) - sizeof (*res); - cfg = GNUNET_CONFIGURATION_create (); - if (GNUNET_OK != - GNUNET_CONFIGURATION_deserialize (cfg, - (const char *) &res[1], - left, - GNUNET_NO)) - { - GNUNET_break (0); - ah->arc (ah->arc_cls, - NULL, - GNUNET_NAT_ERROR_IPC_FAILURE, - type); - } - else - { - ah->arc (ah->arc_cls, - cfg, - status, - type); - } - GNUNET_CONFIGURATION_destroy (cfg); - GNUNET_NAT_autoconfig_cancel (ah); -} - - -/** - * Handle queue errors by reporting autoconfiguration failure. - * - * @param cls the `struct GNUNET_NAT_AutoHandle *` - * @param error details about the error - */ -static void -ah_error_handler (void *cls, - enum GNUNET_MQ_Error error) -{ - struct GNUNET_NAT_AutoHandle *ah = cls; - - ah->arc (ah->arc_cls, - NULL, - GNUNET_NAT_ERROR_IPC_FAILURE, - GNUNET_NAT_TYPE_UNKNOWN); - GNUNET_NAT_autoconfig_cancel (ah); -} - - -/** - * Start auto-configuration routine. The transport adapters should - * be stopped while this function is called. - * - * @param cfg initial configuration - * @param cb function to call with autoconfiguration result - * @param cb_cls closure for @a cb - * @return handle to cancel operation - */ -struct GNUNET_NAT_AutoHandle * -GNUNET_NAT_autoconfig_start (const struct GNUNET_CONFIGURATION_Handle *cfg, - GNUNET_NAT_AutoResultCallback cb, - void *cb_cls) -{ - struct GNUNET_NAT_AutoHandle *ah = GNUNET_new (struct GNUNET_NAT_AutoHandle); - struct GNUNET_MQ_MessageHandler handlers[] = { - GNUNET_MQ_hd_var_size (auto_result, - GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT, - struct GNUNET_NAT_AutoconfigResultMessage, - ah), - GNUNET_MQ_handler_end () - }; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_NAT_AutoconfigRequestMessage *req; - char *buf; - size_t size; - - buf = GNUNET_CONFIGURATION_serialize (cfg, - &size); - if (size > GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (*req)) - { - GNUNET_break (0); - GNUNET_free (buf); - GNUNET_free (ah); - return NULL; - } - ah->arc = cb; - ah->arc_cls = cb_cls; - ah->mq = GNUNET_CLIENT_connecT (cfg, - "nat", - handlers, - &ah_error_handler, - ah); - if (NULL == ah->mq) - { - GNUNET_break (0); - GNUNET_free (buf); - GNUNET_free (ah); - return NULL; - } - env = GNUNET_MQ_msg_extra (req, - size, - GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG); - GNUNET_memcpy (&req[1], - buf, - size); - GNUNET_free (buf); - GNUNET_MQ_send (ah->mq, - env); - return ah; -} - - -/** - * Abort autoconfiguration. - * - * @param ah handle for operation to abort - */ -void -GNUNET_NAT_autoconfig_cancel (struct GNUNET_NAT_AutoHandle *ah) -{ - GNUNET_MQ_destroy (ah->mq); - GNUNET_free (ah); -} - -/* end of nat_api_auto.c */ diff --git a/src/nat/nat_api_test.c b/src/nat/nat_api_test.c deleted file mode 100644 index d47c14094..000000000 --- a/src/nat/nat_api_test.c +++ /dev/null @@ -1,644 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2011, 2016 GNUnet e.V. - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, 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 - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file nat/nat_api_test.c - * @brief functions to test if the NAT configuration is successful at achieving NAT traversal (with the help of a gnunet-nat-server) - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_nat_lib.h" -#include "nat.h" - -#define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__) - -#define NAT_SERVER_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) - -/** - * Entry we keep for each incoming connection. - */ -struct NatActivity -{ - /** - * This is a doubly-linked list. - */ - struct NatActivity *next; - - /** - * This is a doubly-linked list. - */ - struct NatActivity *prev; - - /** - * Socket of the incoming connection. - */ - struct GNUNET_NETWORK_Handle *sock; - - /** - * Handle of the master context. - */ - struct GNUNET_NAT_Test *h; - - /** - * Task reading from the incoming connection. - */ - struct GNUNET_SCHEDULER_Task *rtask; -}; - - -/** - * Entry we keep for each connection to the gnunet-nat-service. - */ -struct ClientActivity -{ - /** - * This is a doubly-linked list. - */ - struct ClientActivity *next; - - /** - * This is a doubly-linked list. - */ - struct ClientActivity *prev; - - /** - * Socket of the incoming connection. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Handle to overall NAT test. - */ - struct GNUNET_NAT_Test *h; - -}; - - -/** - * Handle to a NAT test. - */ -struct GNUNET_NAT_Test -{ - - /** - * Configuration used - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Function to call with success report - */ - GNUNET_NAT_TestCallback report; - - /** - * Closure for @e report. - */ - void *report_cls; - - /** - * Handle to NAT traversal in use - */ - struct GNUNET_NAT_Handle *nat; - - /** - * Handle to listen socket, or NULL - */ - struct GNUNET_NETWORK_Handle *lsock; - - /** - * Head of list of nat activities. - */ - struct NatActivity *na_head; - - /** - * Tail of list of nat activities. - */ - struct NatActivity *na_tail; - - /** - * Head of list of client activities. - */ - struct ClientActivity *ca_head; - - /** - * Tail of list of client activities. - */ - struct ClientActivity *ca_tail; - - /** - * Identity of task for the listen socket (if any) - */ - struct GNUNET_SCHEDULER_Task *ltask; - - /** - * Task identifier for the timeout (if any) - */ - struct GNUNET_SCHEDULER_Task *ttask; - - /** - * #GNUNET_YES if we're testing TCP - */ - int is_tcp; - - /** - * Data that should be transmitted or source-port. - */ - uint16_t data; - - /** - * Advertised port to the other peer. - */ - uint16_t adv_port; - - /** - * Status code to be reported to the timeout/status call - */ - enum GNUNET_NAT_StatusCode status; -}; - - -/** - * Function called from #GNUNET_NAT_register whenever someone asks us - * to do connection reversal. - * - * @param cls closure, our `struct GNUNET_NAT_Handle` - * @param addr public IP address of the other peer - * @param addrlen actual lenght of the @a addr - */ -static void -reversal_cb (void *cls, - const struct sockaddr *addr, - socklen_t addrlen) -{ - struct GNUNET_NAT_Test *h = cls; - const struct sockaddr_in *sa; - - if (sizeof (struct sockaddr_in) != addrlen) - return; - sa = (const struct sockaddr_in *) addr; - if (h->data != sa->sin_port) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received connection reversal request for wrong port\n"); - return; /* wrong port */ - } - /* report success */ - h->report (h->report_cls, - GNUNET_NAT_ERROR_SUCCESS); -} - - -/** - * Activity on our incoming socket. Read data from the - * incoming connection. - * - * @param cls the `struct GNUNET_NAT_Test` - */ -static void -do_udp_read (void *cls) -{ - struct GNUNET_NAT_Test *tst = cls; - uint16_t data; - const struct GNUNET_SCHEDULER_TaskContext *tc; - - tc = GNUNET_SCHEDULER_get_task_context (); - tst->ltask = - GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - tst->lsock, - &do_udp_read, - tst); - if ((NULL != tc->write_ready) && - (GNUNET_NETWORK_fdset_isset (tc->read_ready, - tst->lsock)) && - (sizeof (data) == - GNUNET_NETWORK_socket_recv (tst->lsock, - &data, - sizeof (data)))) - { - if (data == tst->data) - tst->report (tst->report_cls, - GNUNET_NAT_ERROR_SUCCESS); - else - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received data mismatches expected value\n"); - } - else - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Failed to receive data from inbound connection\n"); -} - - -/** - * Activity on our incoming socket. Read data from the - * incoming connection. - * - * @param cls the `struct NatActivity` - */ -static void -do_read (void *cls) -{ - struct NatActivity *na = cls; - struct GNUNET_NAT_Test *tst; - uint16_t data; - const struct GNUNET_SCHEDULER_TaskContext *tc; - - tc = GNUNET_SCHEDULER_get_task_context (); - na->rtask = NULL; - tst = na->h; - GNUNET_CONTAINER_DLL_remove (tst->na_head, - tst->na_tail, - na); - if ((NULL != tc->write_ready) && - (GNUNET_NETWORK_fdset_isset (tc->read_ready, - na->sock)) && - (sizeof (data) == - GNUNET_NETWORK_socket_recv (na->sock, - &data, - sizeof (data)))) - { - if (data == tst->data) - tst->report (tst->report_cls, - GNUNET_NAT_ERROR_SUCCESS); - else - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received data does not match expected value\n"); - } - else - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Failed to receive data from inbound connection\n"); - GNUNET_NETWORK_socket_close (na->sock); - GNUNET_free (na); -} - - -/** - * Activity on our listen socket. Accept the - * incoming connection. - * - * @param cls the `struct GNUNET_NAT_Test` - */ -static void -do_accept (void *cls) -{ - struct GNUNET_NAT_Test *tst = cls; - struct GNUNET_NETWORK_Handle *s; - struct NatActivity *wl; - - tst->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - tst->lsock, - &do_accept, - tst); - s = GNUNET_NETWORK_socket_accept (tst->lsock, - NULL, - NULL); - if (NULL == s) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, - "accept"); - return; /* odd error */ - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got an inbound connection, waiting for data\n"); - wl = GNUNET_new (struct NatActivity); - wl->sock = s; - wl->h = tst; - wl->rtask = - GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - wl->sock, - &do_read, - wl); - GNUNET_CONTAINER_DLL_insert (tst->na_head, - tst->na_tail, - wl); -} - - -/** - * We got disconnected from the NAT server. Stop - * waiting for a reply. - * - * @param cls the `struct ClientActivity` - * @param error error code - */ -static void -mq_error_handler (void *cls, - enum GNUNET_MQ_Error error) -{ - struct ClientActivity *ca = cls; - struct GNUNET_NAT_Test *tst = ca->h; - - GNUNET_CONTAINER_DLL_remove (tst->ca_head, - tst->ca_tail, - ca); - GNUNET_MQ_destroy (ca->mq); - GNUNET_free (ca); -} - - -/** - * Address-callback, used to send message to gnunet-nat-server. - * - * @param cls closure - * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean - * the previous (now invalid) one - * @param addr either the previous or the new public IP address - * @param addrlen actual length of the @a addr - */ -static void -addr_cb (void *cls, - int add_remove, - const struct sockaddr *addr, - socklen_t addrlen) -{ - struct GNUNET_NAT_Test *h = cls; - struct ClientActivity *ca; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_NAT_TestMessage *msg; - const struct sockaddr_in *sa; - - if (GNUNET_YES != add_remove) - return; - if (addrlen != sizeof (struct sockaddr_in)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "NAT test ignores IPv6 address `%s' returned from NAT library\n", - GNUNET_a2s (addr, - addrlen)); - return; /* ignore IPv6 here */ - } - LOG (GNUNET_ERROR_TYPE_INFO, - "Asking gnunet-nat-server to connect to `%s'\n", - GNUNET_a2s (addr, - addrlen)); - - ca = GNUNET_new (struct ClientActivity); - ca->h = h; - ca->mq = GNUNET_CLIENT_connecT (h->cfg, - "gnunet-nat-server", - NULL, - &mq_error_handler, - ca); - if (NULL == ca->mq) - { - GNUNET_free (ca); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Failed to connect to `gnunet-nat-server'\n")); - return; - } - GNUNET_CONTAINER_DLL_insert (h->ca_head, - h->ca_tail, - ca); - sa = (const struct sockaddr_in *) addr; - env = GNUNET_MQ_msg (msg, - GNUNET_MESSAGE_TYPE_NAT_TEST); - msg->dst_ipv4 = sa->sin_addr.s_addr; - msg->dport = sa->sin_port; - msg->data = h->data; - msg->is_tcp = htonl ((uint32_t) h->is_tcp); - GNUNET_MQ_send (ca->mq, - env); -} - - -/** - * Timeout task for a nat test. - * Calls the report-callback with a timeout return value - * - * Destroys the nat handle after the callback has been processed. - * - * @param cls handle to the timed out NAT test - */ -static void -do_timeout (void *cls) -{ - struct GNUNET_NAT_Test *nh = cls; - - nh->ttask = NULL; - nh->report (nh->report_cls, - (GNUNET_NAT_ERROR_SUCCESS == nh->status) - ? GNUNET_NAT_ERROR_TIMEOUT - : nh->status); -} - - -/** - * Start testing if NAT traversal works using the - * given configuration (IPv4-only). - * - * ALL failures are reported directly to the report callback - * - * @param cfg configuration for the NAT traversal - * @param is_tcp #GNUNET_YES to test TCP, #GNUNET_NO to test UDP - * @param bnd_port port to bind to, 0 for connection reversal - * @param adv_port externally advertised port to use - * @param timeout delay after which the test should be aborted - * @param report function to call with the result of the test - * @param report_cls closure for @a report - * @return handle to cancel NAT test or NULL. The error is always indicated via the report callback - */ -struct GNUNET_NAT_Test * -GNUNET_NAT_test_start (const struct GNUNET_CONFIGURATION_Handle *cfg, - int is_tcp, - uint16_t bnd_port, - uint16_t adv_port, - struct GNUNET_TIME_Relative timeout, - GNUNET_NAT_TestCallback report, - void *report_cls) -{ - struct GNUNET_NAT_Test *nh; - struct sockaddr_in sa; - const struct sockaddr *addrs[] = { - (const struct sockaddr *) &sa - }; - const socklen_t addrlens[] = { - sizeof (sa) - }; - - memset (&sa, 0, sizeof (sa)); - sa.sin_family = AF_INET; - sa.sin_port = htons (bnd_port); -#if HAVE_SOCKADDR_IN_SIN_LEN - sa.sin_len = sizeof (sa); -#endif - - nh = GNUNET_new (struct GNUNET_NAT_Test); - nh->cfg = cfg; - nh->is_tcp = is_tcp; - nh->data = bnd_port; - nh->adv_port = adv_port; - nh->report = report; - nh->report_cls = report_cls; - nh->status = GNUNET_NAT_ERROR_SUCCESS; - if (0 == bnd_port) - { - nh->nat - = GNUNET_NAT_register (cfg, - is_tcp, - 0, - 0, - NULL, - NULL, - &addr_cb, - &reversal_cb, - nh, - NULL); - } - else - { - nh->lsock = - GNUNET_NETWORK_socket_create (AF_INET, - (is_tcp == - GNUNET_YES) ? SOCK_STREAM : SOCK_DGRAM, - 0); - if ((nh->lsock == NULL) || - (GNUNET_OK != - GNUNET_NETWORK_socket_bind (nh->lsock, - (const struct sockaddr *) &sa, - sizeof (sa)))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Failed to create listen socket bound to `%s' for NAT test: %s\n"), - GNUNET_a2s ((const struct sockaddr *) &sa, - sizeof (sa)), - STRERROR (errno)); - if (NULL != nh->lsock) - { - GNUNET_NETWORK_socket_close (nh->lsock); - nh->lsock = NULL; - } - nh->status = GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR; - nh->ttask = GNUNET_SCHEDULER_add_now (&do_timeout, - nh); - return nh; - } - if (GNUNET_YES == is_tcp) - { - GNUNET_break (GNUNET_OK == - GNUNET_NETWORK_socket_listen (nh->lsock, - 5)); - nh->ltask = - GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - nh->lsock, - &do_accept, - nh); - } - else - { - nh->ltask = - GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - nh->lsock, - &do_udp_read, - nh); - } - LOG (GNUNET_ERROR_TYPE_INFO, - "NAT test listens on port %u (%s)\n", - bnd_port, - (GNUNET_YES == is_tcp) ? "tcp" : "udp"); - nh->nat = GNUNET_NAT_register (cfg, - is_tcp, - adv_port, - 1, - addrs, - addrlens, - &addr_cb, - NULL, - nh, - NULL); - if (NULL == nh->nat) - { - LOG (GNUNET_ERROR_TYPE_INFO, - _("NAT test failed to start NAT library\n")); - if (NULL != nh->ltask) - { - GNUNET_SCHEDULER_cancel (nh->ltask); - nh->ltask = NULL; - } - if (NULL != nh->lsock) - { - GNUNET_NETWORK_socket_close (nh->lsock); - nh->lsock = NULL; - } - nh->status = GNUNET_NAT_ERROR_NAT_REGISTER_FAILED; - nh->ttask = GNUNET_SCHEDULER_add_now (&do_timeout, - nh); - return nh; - } - } - nh->ttask = GNUNET_SCHEDULER_add_delayed (timeout, - &do_timeout, - nh); - return nh; -} - - -/** - * Stop an active NAT test. - * - * @param tst test to stop. - */ -void -GNUNET_NAT_test_stop (struct GNUNET_NAT_Test *tst) -{ - struct NatActivity *pos; - struct ClientActivity *cpos; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Stopping NAT test\n"); - while (NULL != (cpos = tst->ca_head)) - { - GNUNET_CONTAINER_DLL_remove (tst->ca_head, - tst->ca_tail, - cpos); - GNUNET_MQ_destroy (cpos->mq); - GNUNET_free (cpos); - } - while (NULL != (pos = tst->na_head)) - { - GNUNET_CONTAINER_DLL_remove (tst->na_head, - tst->na_tail, - pos); - GNUNET_SCHEDULER_cancel (pos->rtask); - GNUNET_NETWORK_socket_close (pos->sock); - GNUNET_free (pos); - } - if (NULL != tst->ttask) - { - GNUNET_SCHEDULER_cancel (tst->ttask); - tst->ttask = NULL; - } - if (NULL != tst->ltask) - { - GNUNET_SCHEDULER_cancel (tst->ltask); - tst->ltask = NULL; - } - if (NULL != tst->lsock) - { - GNUNET_NETWORK_socket_close (tst->lsock); - tst->lsock = NULL; - } - if (NULL != tst->nat) - { - GNUNET_NAT_unregister (tst->nat); - tst->nat = NULL; - } - GNUNET_free (tst); -} - -/* end of nat_test.c */