diff --git a/CMakeLists.txt b/CMakeLists.txt index 20f7989..7d7ccc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,9 +125,6 @@ if(N2N_OPTION_USE_PORTMAPPING) add_subdirectory(${THIRD_PARTY_DIR}/libnatpmp libnatpmp) link_directories(${PROJECT_BINARY_DIR}/libnatpmp) - # Turns on the generic code for supporting either of the above - ADD_DEFINITIONS("-DHAVE_PORT_FORWARDING") - # TODO: # - this is the odd one out, is it needed? include_directories(${PROJECT_BINARY_DIR}/lib_miniupnpc) @@ -264,6 +261,8 @@ add_executable(n2n-keygen tools/n2n-keygen.c) target_link_libraries(n2n-keygen n2n) add_executable(n2n-route tools/n2n-route.c) target_link_libraries(n2n-route n2n) +add_executable(n2n-portfwd tools/n2n-portfwd.c) +target_link_libraries(n2n-portfwd n2n) add_executable(tests-auth tools/tests-auth.c) target_link_libraries(tests-auth n2n) diff --git a/configure.ac b/configure.ac index 5241e8f..105cd32 100644 --- a/configure.ac +++ b/configure.ac @@ -62,7 +62,6 @@ AS_IF([test "x$enable_miniupnp" != xno], [AC_CHECK_LIB([miniupnpc], [upnpDiscover], [ AC_DEFINE([HAVE_MINIUPNP], [1], [Have miniupnp library]) - AC_DEFINE([HAVE_PORT_FORWARDING],[1],[upnp libs are used]) N2N_LIBS="-lminiupnpc ${N2N_LIBS}" ], [AC_MSG_ERROR([miniupnp library not found])] @@ -76,7 +75,6 @@ AS_IF([test "x$enable_natpmp" != xno], [AC_CHECK_LIB([natpmp], [initnatpmp], [ AC_DEFINE([HAVE_NATPMP], [1], [Have natpmp library]) - AC_DEFINE([HAVE_PORT_FORWARDING],[1],[upnp libs are used]) N2N_LIBS="-lnatpmp ${N2N_LIBS}" ], [AC_MSG_ERROR([natpmp library not found])] diff --git a/edge.8 b/edge.8 index 54b1b73..720581f 100644 --- a/edge.8 +++ b/edge.8 @@ -109,11 +109,6 @@ defaults to load-based selection strategy if not provided. \fB\-\-select-mac\fR select supernode by MAC address if several to choose from (federation), lowest MAC address first. -.TP -\fB\-\-no-port-forwarding\fR -disables the default behavior of trying to have the edge's port forwarded -through a router eventually supporting it (only if compiled with miniupnp -and/or natpmp library support). .SH TAP DEVICE AND OVERLAY NETWORK CONFIGURATION .TP \fB\-a \fR[\fImode\fR]<\fIip\fR>[\fI/n\fR] diff --git a/include/n2n.h b/include/n2n.h index 28c8aaa..f5892dd 100644 --- a/include/n2n.h +++ b/include/n2n.h @@ -158,9 +158,7 @@ #include "network_traffic_filter.h" #include "auth.h" -#if defined(HAVE_MINIUPNP) || defined(HAVE_NATPMP) #include "n2n_port_mapping.h" -#endif // HAVE_MINIUPNP || HAVE_NATPMP #include "json.h" diff --git a/include/n2n_port_mapping.h b/include/n2n_port_mapping.h index 46b2511..e0c9006 100644 --- a/include/n2n_port_mapping.h +++ b/include/n2n_port_mapping.h @@ -20,13 +20,12 @@ #ifndef _N2N_PORT_MAPPING_H_ #define _N2N_PORT_MAPPING_H_ -#ifdef HAVE_PORT_FORWARDING #include #ifdef HAVE_MINIUPNP #ifdef CMAKE_BUILD -// CMAKE uses static linked lib as submodule which requires different includes than +// CMAKE uses statically linked lib as submodule which requires different includes than // the dynamically linked, intalled library in case of plain make #include #include @@ -44,8 +43,8 @@ #endif // HAVE_NATPMP -void n2n_chg_port_mapping (struct n2n_edge *eee, const uint16_t port); +void n2n_set_port_mapping (const uint16_t port); +void n2n_del_port_mapping (const uint16_t port); -#endif // HAVE_PORT_FORWARDING #endif // _N2N_PORT_MAPPING_H_ diff --git a/include/n2n_typedefs.h b/include/n2n_typedefs.h index 30dc32f..4a796bd 100644 --- a/include/n2n_typedefs.h +++ b/include/n2n_typedefs.h @@ -634,21 +634,6 @@ typedef struct n2n_resolve_parameter { /* *************************************************** */ -// structure to hold port mapping thread's parameters -typedef struct n2n_port_map_parameter { -#ifdef HAVE_PTHREAD - pthread_t id; /* thread id */ - pthread_mutex_t access; /* mutex for shared access */ -#endif - uint16_t mgmt_port; - uint16_t mapped_port; - uint16_t new_port; /* REVISIT: remove with management port subscriptions */ -} n2n_port_map_parameter_t; - - -/* *************************************************** */ - - typedef struct n2n_edge_conf { struct peer_info *supernodes; /**< List of supernodes */ n2n_community_t community_name; /**< The community. 16 full octets. */ @@ -686,7 +671,6 @@ typedef struct n2n_edge_conf { uint8_t sn_selection_strategy; /**< encodes currently chosen supernode selection strategy. */ uint8_t number_max_sn_pings; /**< Number of maximum concurrently allowed supernode pings. */ uint64_t mgmt_password_hash; /**< contains hash of managament port password. */ - uint8_t port_forwarding; /**< indicates if port forwarding UPNP/PMP is enabled */ } n2n_edge_conf_t; @@ -747,8 +731,6 @@ struct n2n_edge { n2n_resolve_parameter_t *resolve_parameter; /**< Pointer to name resolver's parameter block */ uint8_t resolution_request; /**< Flag an immediate DNS resolution request */ - n2n_port_map_parameter_t *port_map_parameter; /**< Pointer to port mapping thread's parameter block */ - n2n_tuntap_priv_config_t tuntap_priv_conf; /**< Tuntap config */ network_traffic_filter_t *network_traffic_filter; diff --git a/src/edge.c b/src/edge.c index 672ee89..c6e2801 100644 --- a/src/edge.c +++ b/src/edge.c @@ -234,9 +234,6 @@ static void help (int level) { "\n [-E] accept multicast MAC addresses" "\n [--select-rtt] select supernode by round trip time" "\n [--select-mac] select supernode by MAC address" -#if defined(HAVE_MINIUPNP) || defined(HAVE_NATPMP) - "\n [--no-port-forwarding] disable UPnP/PMP port forwarding" -#endif // HAVE_MINIUPNP || HAVE_NATPMP #ifndef WIN32 "\n [-f] do not fork but run in foreground" #endif @@ -298,11 +295,6 @@ static void help (int level) { printf("--select-rtt | supernode selection based on round trip time\n" "--select-mac | supernode selection based on MAC address (default:\n" " | by load)\n"); -#if defined(HAVE_MINIUPNP) || defined(HAVE_NATPMP) - printf("--no-port-... | disable UPnP/PMP port forwarding\n" - "...forwarding | \n"); -#endif // HAVE_MINIUPNP || HAVE_NATPMP - printf ("\n"); printf (" TAP DEVICE AND OVERLAY NETWORK CONFIGURATION\n"); printf (" --------------------------------------------\n\n"); @@ -728,12 +720,6 @@ static int setOption (int optkey, char *optargument, n2n_tuntap_priv_config_t *e break; } - case '}': /* disable port forwarding */ { - conf->port_forwarding = 0; - - break; - } - case 'h': /* quick reference */ { return 2; } @@ -790,7 +776,6 @@ static const struct option long_options[] = { "select-rtt", no_argument, NULL, '[' }, /* '[' rtt selection strategy */ { "select-mac", no_argument, NULL, ']' }, /* ']' mac selection strategy */ { "management-password", required_argument, NULL, '{' }, /* '{' management port password */ - { "no-port-forwarding", no_argument, NULL, '}' }, /* '}' disable port forwarding */ { NULL, 0, NULL, 0 } }; diff --git a/src/edge_management.c b/src/edge_management.c index d1115f6..2956af4 100644 --- a/src/edge_management.c +++ b/src/edge_management.c @@ -165,6 +165,7 @@ static void mgmt_edge_info (mgmt_req_t *req, strbuf_t *buf) { macstr_t mac_buf; struct in_addr ip_addr, ip_addr_mask; ipstr_t ip_address, ip_address_mask; + n2n_sock_str_t sockbuf; ip_addr.s_addr = req->eee->device.ip_addr; inaddrtoa(ip_address, ip_addr); @@ -178,11 +179,13 @@ static void mgmt_edge_info (mgmt_req_t *req, strbuf_t *buf) { "\"version\":\"%s\"," "\"macaddr\":\"%s\"," "\"ip4addr\":\"%s\"," - "\"ip4netmask\":\"%s\"}\n", + "\"ip4netmask\":\"%s\"," + "\"sockaddr\":\"%s\"}\n", req->tag, PACKAGE_VERSION, is_null_mac(req->eee->device.mac_addr) ? "" : macaddr_str(mac_buf, req->eee->device.mac_addr), - ip_address, ip_address_mask); + ip_address, ip_address_mask, + sock_to_cstr(sockbuf, &req->eee->conf.preferred_sock)); send_reply(req, buf, msg_len); } diff --git a/src/edge_utils.c b/src/edge_utils.c index 437f590..7d77d6d 100644 --- a/src/edge_utils.c +++ b/src/edge_utils.c @@ -27,11 +27,6 @@ int resolve_create_thread (n2n_resolve_parameter_t **param, struct peer_info *sn int resolve_check (n2n_resolve_parameter_t *param, uint8_t resolution_request, time_t now); int resolve_cancel_thread (n2n_resolve_parameter_t *param); -#ifdef HAVE_PORT_FORWARDING -int port_map_create_thread (n2n_port_map_parameter_t **param, uint16_t mgmt_port); -int port_map_cancel_thread (n2n_port_map_parameter_t *param); -#endif - static const char * supernode_ip (const n2n_edge_t * eee); static void send_register (n2n_edge_t *eee, const n2n_sock_t *remote_peer, const n2n_mac_t peer_mac, n2n_cookie_t cookie); @@ -329,12 +324,9 @@ int supernode_connect (n2n_edge_t *eee) { eee->cb.sock_opened(eee); } -#ifdef HAVE_PORT_FORWARDING - if(eee->conf.port_forwarding) - // REVISIT: replace with mgmt port notification to listener for mgmt port - // subscription support - n2n_chg_port_mapping(eee, eee->conf.preferred_sock.port); -#endif // HAVE_PORT_FORWARDING + // REVISIT: add mgmt port notification to listener for better mgmt port + // subscription support + return 0; } @@ -490,12 +482,7 @@ n2n_edge_t* edge_init (const n2n_edge_conf_t *conf, int *rv) { if(resolve_create_thread(&(eee->resolve_parameter), eee->conf.supernodes) == 0) { traceEvent(TRACE_NORMAL, "successfully created resolver thread"); } -#ifdef HAVE_PORT_FORWARDING - if(eee->conf.port_forwarding) - if(port_map_create_thread(&eee->port_map_parameter, eee->conf.mgmt_port) == 0) { - traceEvent(TRACE_NORMAL, "successfully created port mapping thread"); - } -#endif // HAVE_MINIUPNP || HAVE_NATPMP + eee->network_traffic_filter = create_network_traffic_filter(); network_traffic_filter_add_rule(eee->network_traffic_filter, eee->conf.network_traffic_filter_rules); @@ -2993,10 +2980,7 @@ int run_edge_loop (n2n_edge_t *eee) { void edge_term (n2n_edge_t * eee) { resolve_cancel_thread(eee->resolve_parameter); -#ifdef HAVE_PORT_FORWARDING - if(eee->conf.port_forwarding) - port_map_cancel_thread(eee->port_map_parameter); -#endif // HAVE_MINIUPNP || HAVE_NATPMP + if(eee->sock >= 0) closesocket(eee->sock); @@ -3117,10 +3101,6 @@ void edge_init_conf_defaults (n2n_edge_conf_t *conf) { free(tmp_string); } -#if defined(HAVE_MINIUPNP) || defined(HAVE_NATPMP) - conf->port_forwarding = 1; -#endif // HAVE_MINIUPNP || HAVE_NATPMP - conf->sn_selection_strategy = SN_SELECTION_STRATEGY_LOAD; conf->metric = 0; } diff --git a/src/n2n_port_mapping.c b/src/n2n_port_mapping.c index 29fbc6f..35d1670 100644 --- a/src/n2n_port_mapping.c +++ b/src/n2n_port_mapping.c @@ -56,7 +56,6 @@ #include "n2n.h" -#ifdef HAVE_PORT_FORWARDING #ifdef HAVE_MINIUPNP @@ -463,9 +462,12 @@ static int n2n_natpmp_del_port_mapping (const uint16_t port) { #endif // HAVE_NATPMP +// static // ---------------------------------------------------------------------------------------------------- +// public -static void n2n_set_port_mapping (const uint16_t port) { + +void n2n_set_port_mapping (const uint16_t port) { #ifdef HAVE_NATPMP // since the NAT-PMP protocol is more concise than UPnP, NAT-PMP is preferred. @@ -479,7 +481,7 @@ static void n2n_set_port_mapping (const uint16_t port) { } -static void n2n_del_port_mapping (const uint16_t port) { +void n2n_del_port_mapping (const uint16_t port) { #ifdef HAVE_NATPMP if(n2n_natpmp_del_port_mapping(port)) @@ -490,151 +492,3 @@ static void n2n_del_port_mapping (const uint16_t port) { #endif // HAVE_MINIUPNP } } - - -// static -// ---------------------------------------------------------------------------------------------------- -// public - - -#ifdef HAVE_PTHREAD /* future management port subscriptions will deprecate the following temporary code */ -void n2n_chg_port_mapping (struct n2n_edge *eee, uint16_t port) { - // write a port change request to param struct, it will be handled in the thread - pthread_mutex_lock(&eee->port_map_parameter->access); - eee->port_map_parameter->new_port = port; - pthread_mutex_unlock(&eee->port_map_parameter->access); - -} -#else -#error "enabling port mapping requires enabling pthread" -#endif - - -N2N_THREAD_RETURN_DATATYPE port_map_thread(N2N_THREAD_PARAMETER_DATATYPE p) { - -#ifdef HAVE_PTHREAD /* future management port subscriptions will deprecate the following temporary code */ - n2n_port_map_parameter_t *param = (n2n_port_map_parameter_t*)p; - - while(1) { - sleep(2); - pthread_mutex_lock(¶m->access); - if(param->mapped_port != param->new_port) { - if(param->mapped_port) - n2n_del_port_mapping(param->mapped_port); - if(param->new_port) - n2n_set_port_mapping(param->new_port); - param->mapped_port = param->new_port; - } - pthread_mutex_unlock(¶m->access); - } -#endif - -#if 0 /* to be used with future management port subscriptions */ - n2n_port_map_parameter_t *param = (n2n_port_map_parameter_t*)p; - SOCKET socket_fd; - fd_set socket_mask; - struct timeval wait_time; - int ret = 0; - char udp_buf[N2N_PKT_BUF_SIZE]; - ssize_t msg_len; - uint32_t addr_tmp = htonl(INADDR_LOOPBACK); - n2n_sock_t sock_tmp; - struct sockaddr_in sock; - socklen_t sock_len; - - // open a new socket ... - socket_fd = open_socket(0 /* no specific port */, INADDR_LOOPBACK, 0 /* UDP */); - if(socket_fd < 0) { - traceEvent(TRACE_ERROR, "port_map_thread failed to open a socket to management port"); - return 0; - } - // ... and connect to local mgmt port - sock_tmp.family = AF_INET; - memcpy(&sock_tmp.addr.v4, &addr_tmp, IPV4_SIZE); - sock_tmp.port = param->mgmt_port; - sock_len = sizeof(sock); - fill_sockaddr((struct sockaddr*)&sock, sock_len, &sock_tmp); - connect(socket_fd, (struct sockaddr*)&sock, sock_len); - - // prepare a subscription request in 'udp_buf' of length 'msg_len' - // !!! dummy - udp_buf[0] = '\n'; - msg_len = 1; - send(socket_fd, udp_buf, msg_len, 0 /*flags*/); - // note: 'msg_len' and 'sock' get re-used hereafter - - while(1) { - FD_ZERO(&socket_mask); - FD_SET(socket_fd, &socket_mask); - - wait_time.tv_sec = SOCKET_TIMEOUT_INTERVAL_SECS; - wait_time.tv_usec = 0; - - ret = select(socket_fd + 1, &socket_mask, NULL, NULL, &wait_time); - - if(ret > 0) { - if(FD_ISSET(socket_fd, &socket_mask)) { - // get the data - sock_len = sizeof(sock); - msg_len = recv(socket_fd, udp_buf, N2N_PKT_BUF_SIZE, 0 /*flags*/); - - // check message format, first message could be the still buffered answer to the subscription request - // !!! - if(1 /* !!! correct message format */) { - // delete an eventually previous port mapping - if(param->mapped_port) - n2n_del_port_mapping(param->mapped_port); - // extract port from message and set accordingly if valid - param->mapped_port = 0; // !!! - if(param->mapped_port) - n2n_set_port_mapping(param->mapped_port); - } - } - } - } -#endif - - return 0; /* should never happen */ -} - - -int port_map_create_thread (n2n_port_map_parameter_t **param, uint16_t mgmt_port) { - -#ifdef HAVE_PTHREAD - int ret; - - // create parameter structure - *param = (n2n_port_map_parameter_t*)calloc(1, sizeof(n2n_port_map_parameter_t)); - if(*param) { - // initialize - (*param)->mgmt_port = mgmt_port; - } else { - traceEvent(TRACE_WARNING, "port_map_create_thread was unable to create parameter structure"); - return -1; - } - - // create thread - ret = pthread_create(&((*param)->id), NULL, port_map_thread, (void *)*param); - if(ret) { - traceEvent(TRACE_WARNING, "port_map_create_thread failed to create port mapping thread with error number %d", ret); - return -1; - } - - return 0; -#else - return -1; -#endif -} - - -void port_map_cancel_thread (n2n_port_map_parameter_t *param) { - -#ifdef HAVE_PTHREAD - pthread_cancel(param->id); - if(param->mapped_port) - n2n_del_port_mapping(param->mapped_port); - free(param); -#endif -} - -#endif // HAVE_PORT_FORWARDING diff --git a/tools/Makefile.in b/tools/Makefile.in index 815ed5b..3ab0e95 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -14,7 +14,7 @@ LDFLAGS+=-L.. N2N_LIB=../libn2n.a -TOOLS=n2n-benchmark n2n-keygen n2n-route +TOOLS=n2n-benchmark n2n-keygen n2n-route n2n-portfwd TOOLS+=@ADDITIONAL_TOOLS@ TESTS=tests-compress tests-elliptic tests-hashing tests-transform @@ -27,6 +27,7 @@ all: $(TOOLS) $(TESTS) n2n-benchmark.o: $(N2N_LIB) $(HEADERS) ../Makefile Makefile n2n-keygen.o: $(N2N_LIB) $(HEADERS) ../Makefile Makefile n2n-route.o: $(N2N_LIB) $(HEADERS) ../Makefile Makefile +n2n-portfwd.o: $(N2N_LIB) $(HEADERS) ../Makefile Makefile n2n-decode: n2n-decode.c $(N2N_LIB) $(HEADERS) ../Makefile Makefile $(CC) $(CFLAGS) $< $(LDFLAGS) $(LDLIBS) -lpcap -o $@ diff --git a/tools/n2n-portfwd.c b/tools/n2n-portfwd.c new file mode 100644 index 0000000..fbb0731 --- /dev/null +++ b/tools/n2n-portfwd.c @@ -0,0 +1,376 @@ +/** + * (C) 2007-22 - ntop.org and contributors + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not see see + * + */ + + +#include "n2n.h" + + +#define WITH_PORT 1 +#define CORRECT_TAG 2 + +#define SOCKET_TIMEOUT 2 +#define INFO_INTERVAL 5 + +#ifdef WIN32 +#define STDIN_FILENO _fileno(stdin) +#endif + + +typedef struct n2n_portfwd_conf { + uint16_t port; /* management port */ +} n2n_portfwd_conf_t; + + +static int keep_running = 1; /* for main loop, handled by signals */ + + +// ------------------------------------------------------------------------------------------------------- +// PLATFORM-DEPENDANT CODE (FOR NON-MANDATORY FEATURE) + + +void set_term_handler(const void *handler) { + +#ifdef __linux__ + signal(SIGPIPE, SIG_IGN); + signal(SIGTERM, handler); + signal(SIGINT, handler); +#endif +#ifdef WIN32 + SetConsoleCtrlHandler(handler, TRUE); +#endif +} + + +#ifdef WIN32 +BOOL WINAPI term_handler (DWORD sig) { +#else +static void term_handler (int sig) { +#endif + + static int called = 0; + + if(called) { + traceEvent(TRACE_NORMAL, "ok, leaving now"); + _exit(0); + } else { + traceEvent(TRACE_NORMAL, "shutting down..."); + called = 1; + } + + keep_running = 0; +#ifdef WIN32 + return TRUE; +#endif +} + + +// ------------------------------------------------------------------------------------------------------- + + +SOCKET connect_to_management_port (n2n_portfwd_conf_t *ppp) { + + SOCKET ret; + struct sockaddr_in sock_addr; + + ret = socket (PF_INET, SOCK_DGRAM, 0); + if((int)ret < 0) + return -1; + + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sock_addr.sin_port = htons(ppp->port); + if(0 != connect(ret, (struct sockaddr *)&sock_addr, sizeof(sock_addr))) + return -1; + + return ret; +} + + +// ------------------------------------------------------------------------------------------------------- + + +int get_port_from_json (uint16_t *port, json_object_t *json, char *key, int tag, int flags) { + + int i; + char *colon = NULL; + + if(NULL == json) + return 0; + + for(i = 0; i < json->count; i++) { + if(json->pairs[i].type == JSON_STRING) { + if(!strcmp(json->pairs[i].key, key)) { + // cut off port from IP address + if((colon = strchr(json->pairs[i].value->string_value, ':'))) { + if(*colon != '\0') { + *port = atoi(colon + 1); + flags |= WITH_PORT; + } + } + } + if(!strcmp(json->pairs[i].key, "_tag" )) { + if(atoi(json->pairs[i].value->string_value) == tag) { + flags |= CORRECT_TAG; + } + } + } else if(json->pairs[i].type == JSON_OBJECT) { + flags |= get_port_from_json(port, json, key, tag, flags); + } + } + + return flags; +} + + +// ------------------------------------------------------------------------------------------------------- + + +// taken from https://web.archive.org/web/20170407122137/http://cc.byexamples.com/2007/04/08/non-blocking-user-input-in-loop-without-ncurses/ +int kbhit () { + + struct timeval tv; + fd_set fds; + + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0 + select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); + + return FD_ISSET(STDIN_FILENO, &fds); +} + + +// ------------------------------------------------------------------------------------------------------- + + +static void help (int level) { + + if(level == 0) return; /* no help required */ + + printf(" n2n-portfwd [-t ] [-V] [-v]" + "\n" + "\n This tool tries to find a router in local network and asks it to" + "\n forward the edge's port(UDP and TCP) by sending corresponding" + "\n UPnP and PMP requests." + "\n\n Adapt port (default: %d) to match your edge's management port" + "\n configuration." + "\n\n Verbosity can be increased or decreased with -V or -v , repeat as" + "\n as needed." + "\n\n", + N2N_EDGE_MGMT_PORT); + + exit(0); +} + + +static int set_option (n2n_portfwd_conf_t *ppp, int optkey, char *optargument) { + + switch(optkey) { + case 't': /* management port */ { + uint16_t port = atoi(optargument); + if(port) { + ppp->port = port; + } else { + traceEvent(TRACE_WARNING, "invalid management port provided with '-t'"); + } + break; + } + + case 'V': /* more verbose */ { + setTraceLevel(getTraceLevel() + 1); + break; + } + + case 'v': /* less verbose */ { + setTraceLevel(getTraceLevel() - 1); + break; + } + + default: /* unknown option */ { + return 1; /* for help */ + } + } + + return 0; +} + + +// ------------------------------------------------------------------------------------------------------- + + +int main (int argc, char* argv[]) { + + n2n_portfwd_conf_t ppp; + uint8_t c; + SOCKET sock; + size_t msg_len; + char udp_buf[N2N_PKT_BUF_SIZE]; + fd_set socket_mask; + struct timeval wait_time; + time_t now = 0; + time_t last_info_req = 0; + json_object_t *json; + int ret; + int tag_info; + uint16_t port = 0, current_port = 0; + + // version + print_n2n_version(); + + // ensure prerequisites +#ifndef HAVE_MINIUPNP + traceEvent(TRACE_WARNING, "no miniupnp support compiled"); +#else + traceEvent(TRACE_NORMAL, "compiled with miniupnp support"); +#endif +#ifndef HAVE_NATPMP + traceEvent(TRACE_WARNING, "no natpmp support compiled"); +#else + traceEvent(TRACE_NORMAL, "compiled with natpmp support"); +#endif +#if !defined(HAVE_MINIUPNP) && !defined(HAVE_NATPMP) + traceEvent(TRACE_WARNING, "no port forwarding method found"); + return -1; +#endif + + // handle signals to properly end the tool + set_term_handler(term_handler); + + // init data structure + ppp.port = N2N_EDGE_MGMT_PORT; + setTraceLevel(2); /* NORMAL, should already be default */ + n2n_srand(n2n_seed()); + + // get command line options and eventually overwrite initialized conf + while((c = getopt_long(argc, argv, "t:vV", NULL, NULL)) != '?') { + if(c == 255) break; + help(set_option(&ppp, c, optarg)); + } + + // verify conf and react with output to conf-related changes + // (nothing to do) + + // additional checks + // (nothing to do) + + // connect to mamagement port + traceEvent(TRACE_NORMAL, "connecting to edge management port %d", ppp.port); + sock = connect_to_management_port(&ppp); + if(sock == -1) { + traceEvent(TRACE_ERROR, "unable to open socket for management port connection"); + goto end_route_tool; + } + + // output status + traceEvent(TRACE_NORMAL, "press ENTER to end the program"); + +reset_main_loop: + + wait_time.tv_sec = SOCKET_TIMEOUT; + wait_time.tv_usec = 0; + port = 0; + current_port = 0; + tag_info = 0; + + // main loop + // read answer packet by packet which are only accepted if a corresponding request was sent before + // of which we know about by having set the related tag, tag_info + // a valid sock address indicates that we have seen a valid answer to the info request + while(keep_running && !kbhit()) { + // current time + now = time(NULL); + + // check if we need to send info request again + if(now > last_info_req + INFO_INTERVAL) { + // send info read request + while(!(tag_info = ((uint32_t)n2n_rand()) >> 23)); + msg_len = 0; + msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len), + "r %u info\n", tag_info); + ret = send(sock, udp_buf, msg_len, 0); + last_info_req = now; + } + + // wait for any answer to info or read request + FD_ZERO(&socket_mask); + FD_SET(sock, &socket_mask); + ret = select(sock + 1, &socket_mask, NULL, NULL, &wait_time); + + // refresh current time after having waited + now = time(NULL); + + if(ret > 0) { + if(FD_ISSET(sock, &socket_mask)) { + msg_len = recv(sock, udp_buf, sizeof(udp_buf), 0); + if((msg_len > 0) && (msg_len < sizeof(udp_buf))) { + // make sure it is a string and replace all newlines with spaces + udp_buf[msg_len] = '\0'; + for (char *p = udp_buf; (p = strchr(p, '\n')) != NULL; p++) *p = ' '; + traceEvent(TRACE_DEBUG, "received '%s' from management port", udp_buf); + + // handle the answer, json needs to be freed later + json = json_parse(udp_buf); + + // look for local edge information + if(tag_info) { + // local IP address port + port = 0; + ret = get_port_from_json(&port, json, "sockaddr", tag_info, 0); + if(ret == (WITH_PORT | CORRECT_TAG)) { + traceEvent(TRACE_DEBUG, "received information about edge port %d", port); + // evaluate current situation and take appropriate action + if(port != current_port) { + traceEvent(TRACE_NORMAL, "found edge port %d", port); + if(current_port) + n2n_del_port_mapping(current_port); + if(port) + n2n_set_port_mapping(port); + current_port = port; + } + } + } + + // no need for current json object anymore + json_free(json); + } + } else { + // can this happen? reset the loop + traceEvent(TRACE_ERROR, "loop reset"); + goto reset_main_loop; + } + } else if(ret == 0) { + // select() timeout + // action required? + } else { + // select() error + // action required? + } + } + +end_route_tool: + + // delete port forwarding if any + if(current_port) + n2n_del_port_mapping(current_port); + + // close connection + closesocket(sock); + + return 0; +}