X-Git-Url: https://www.tinc-vpn.org/git/browse?a=blobdiff_plain;f=src%2Fupnp.c;h=5e41d1b653996d16dbf0e81016341fa17a2e95ab;hb=1c475ecb575367a6b3f9328b0f643ad636155341;hp=a03143a12deac3d425d48f9bf460249680010e1c;hpb=513bffe1fee07bcbcb50691e221874adc1507857;p=tinc diff --git a/src/upnp.c b/src/upnp.c index a03143a1..5e41d1b6 100644 --- a/src/upnp.c +++ b/src/upnp.c @@ -37,29 +37,66 @@ static bool upnp_udp; static int upnp_discover_wait = 5; static int upnp_refresh_period = 60; +// Unfortunately, libminiupnpc devs don't seem to care about API compatibility, +// and there are slight changes to function signatures between library versions. +// Well, at least they publish a "MINIUPNPC_API_VERSION" constant, so we got that going for us, which is nice. +// Differences between API versions are documented in "apiversions.txt" in the libminiupnpc distribution. + +#ifndef MINIUPNPC_API_VERSION +#define MINIUPNPC_API_VERSION 0 +#endif + +static struct UPNPDev *upnp_discover(int delay, int *error) { +#if MINIUPNPC_API_VERSION <= 13 + +#if MINIUPNPC_API_VERSION < 8 +#warning "The version of libminiupnpc you're building against seems to be too old. Expect trouble." +#endif + + return upnpDiscover(delay, NULL, NULL, false, false, error); + +#elif MINIUPNPC_API_VERSION <= 14 + + return upnpDiscover(delay, NULL, NULL, false, false, 2, error); + +#else + +#if MINIUPNPC_API_VERSION > 17 +#warning "The version of libminiupnpc you're building against seems to be too recent. Expect trouble." +#endif + + return upnpDiscover(delay, NULL, NULL, UPNP_LOCAL_PORT_ANY, false, 2, error); + +#endif +} + static void upnp_add_mapping(struct UPNPUrls *urls, struct IGDdatas *data, const char *myaddr, int socket, const char *proto) { // Extract the port from the listening socket. // Note that we can't simply use listen_socket[].sa because this won't have the port // if we're running with Port=0 (dynamically assigned port). sockaddr_t sa; - socklen_t salen = sizeof sa; - if (getsockname(socket, &sa.sa, &salen)) { + socklen_t salen = sizeof(sa); + + if(getsockname(socket, &sa.sa, &salen)) { logger(DEBUG_PROTOCOL, LOG_ERR, "[upnp] Unable to get socket address: [%d] %s", sockerrno, sockstrerror(sockerrno)); return; } + char *port; sockaddr2str(&sa, NULL, &port); - if (!port) { + + if(!port) { logger(DEBUG_PROTOCOL, LOG_ERR, "[upnp] Unable to get socket port"); return; } // Use a lease twice as long as the refresh period so that the mapping won't expire before we refresh. char lease_duration[16]; - snprintf(lease_duration, sizeof lease_duration, "%d", upnp_refresh_period * 2); + snprintf(lease_duration, sizeof(lease_duration), "%d", upnp_refresh_period * 2); int error = UPNP_AddPortMapping(urls->controlURL, data->first.servicetype, port, port, myaddr, identname, proto, NULL, lease_duration); - if (error == 0) { + + if(error == 0) { logger(DEBUG_PROTOCOL, LOG_INFO, "[upnp] Successfully set port mapping (%s:%s %s for %s seconds)", myaddr, port, proto, lease_duration); } else { logger(DEBUG_PROTOCOL, LOG_ERR, "[upnp] Failed to set port mapping (%s:%s %s for %s seconds): [%d] %s", myaddr, port, proto, lease_duration, error, strupnperror(error)); @@ -72,27 +109,38 @@ static void upnp_refresh() { logger(DEBUG_PROTOCOL, LOG_INFO, "[upnp] Discovering IGD devices"); int error; - struct UPNPDev *devices = upnpDiscover(upnp_discover_wait * 1000, NULL, NULL, false, false, &error); - if (!devices) { + struct UPNPDev *devices = upnp_discover(upnp_discover_wait * 1000, &error); + + if(!devices) { logger(DEBUG_PROTOCOL, LOG_WARNING, "[upnp] Unable to find IGD devices: [%d] %s", error, strupnperror(error)); freeUPNPDevlist(devices); return; } struct UPNPUrls urls; + struct IGDdatas data; + char myaddr[64]; - int result = UPNP_GetValidIGD(devices, &urls, &data, myaddr, sizeof myaddr); - if (result <= 0) { + + int result = UPNP_GetValidIGD(devices, &urls, &data, myaddr, sizeof(myaddr)); + + if(result <= 0) { logger(DEBUG_PROTOCOL, LOG_WARNING, "[upnp] No IGD found"); freeUPNPDevlist(devices); return; } + logger(DEBUG_PROTOCOL, LOG_INFO, "[upnp] IGD found: [%d] %s (local address: %s, service type: %s)", result, urls.controlURL, myaddr, data.first.servicetype); - for (int i = 0; i < listen_sockets; i++) { - if (upnp_tcp) upnp_add_mapping(&urls, &data, myaddr, listen_socket[i].tcp.fd, "TCP"); - if (upnp_udp) upnp_add_mapping(&urls, &data, myaddr, listen_socket[i].udp.fd, "UDP"); + for(int i = 0; i < listen_sockets; i++) { + if(upnp_tcp) { + upnp_add_mapping(&urls, &data, myaddr, listen_socket[i].tcp.fd, "TCP"); + } + + if(upnp_udp) { + upnp_add_mapping(&urls, &data, myaddr, listen_socket[i].udp.fd, "UDP"); + } } FreeUPNPUrls(&urls); @@ -100,14 +148,19 @@ static void upnp_refresh() { } static void *upnp_thread(void *data) { - while (true) { + (void)data; + + while(true) { time_t start = time(NULL); upnp_refresh(); // Make sure we'll stick to the refresh period no matter how long upnp_refresh() takes. time_t refresh_time = start + upnp_refresh_period; time_t now = time(NULL); - if (now < refresh_time) sleep(refresh_time - now); + + if(now < refresh_time) { + sleep(refresh_time - now); + } } // TODO: we don't have a clean thread shutdown procedure, so we can't remove the mapping. @@ -125,7 +178,8 @@ void upnp_init(bool tcp, bool udp) { pthread_t thread; int error = pthread_create(&thread, NULL, upnp_thread, NULL); - if (error) { + + if(error) { logger(DEBUG_ALWAYS, LOG_ERR, "Unable to start UPnP-IGD client thread: [%d] %s", error, strerror(error)); } }