diff --git a/README b/README index eee148b..a064e7f 100644 --- a/README +++ b/README @@ -10,6 +10,9 @@ miniupnpd/ : MiniUPnP daemon - an implementation of a UPnP IGD + NAT-PMP / PCP gateway minissdpd/ : SSDP managing daemon. Designed to work with miniupnpc, miniupnpd, minidlna, etc. +miniupnpc-async/ : Proof of concept for a UPnP IGD control point using + asynchronous (non blocking) sockets. +miniupnpc-libevent/ : UPnP IGD control point using libevent2 http://libevent.org/ Thanks to : * Ryan Wagoner diff --git a/miniupnpc-libevent/miniupnpc-libevent.c b/miniupnpc-libevent/miniupnpc-libevent.c index 6331748..5b67ead 100644 --- a/miniupnpc-libevent/miniupnpc-libevent.c +++ b/miniupnpc-libevent/miniupnpc-libevent.c @@ -1,4 +1,4 @@ -/* $Id: miniupnpc-libevent.c,v 1.8 2014/11/13 09:15:23 nanard Exp $ */ +/* $Id: miniupnpc-libevent.c,v 1.11 2014/11/17 09:17:38 nanard Exp $ */ /* miniupnpc-libevent * Copyright (c) 2008-2014, Thomas BERNARD * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ @@ -342,9 +342,13 @@ static void upnpc_desc_received(struct evhttp_request * req, void * pvoid) printIGD(&igd); #endif /* DEBUG */ p->control_conn_url = build_url_string(igd.urlbase, p->root_desc_location, igd.first.controlurl); + p->conn_service_type = strdup(igd.first.servicetype); p->control_cif_url = build_url_string(igd.urlbase, p->root_desc_location, igd.CIF.controlurl); - debug_printf("control_conn_url='%s'\n", p->control_conn_url); - debug_printf("control_cif_url='%s'\n", p->control_cif_url); + p->cif_service_type = strdup(igd.CIF.servicetype); + debug_printf("control_conn_url='%s'\n (service_type='%s')\n", + p->control_conn_url, p->conn_service_type); + debug_printf("control_cif_url='%s'\n (service_type='%s')\n", + p->control_cif_url, p->cif_service_type); p->ready_cb(evhttp_request_get_response_code(req), p->cb_data); } @@ -592,8 +596,12 @@ int upnpc_finalize(upnpc_t * p) p->root_desc_location = NULL; free(p->control_cif_url); p->control_cif_url = NULL; + free(p->cif_service_type); + p->cif_service_type = NULL; free(p->control_conn_url); p->control_conn_url = NULL; + free(p->conn_service_type); + p->conn_service_type = NULL; if(p->ssdp_socket >= 0) { close(p->ssdp_socket); p->ssdp_socket = -1; @@ -621,17 +629,39 @@ int upnpc_finalize(upnpc_t * p) int upnpc_get_external_ip_address(upnpc_t * p) { return upnpc_send_soap_request(p, p->control_conn_url, - "urn:schemas-upnp-org:service:WANIPConnection:1", + p->conn_service_type/*"urn:schemas-upnp-org:service:WANIPConnection:1"*/, "GetExternalIPAddress", NULL, 0); } int upnpc_get_link_layer_max_rate(upnpc_t * p) { return upnpc_send_soap_request(p, p->control_cif_url, - "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1", + p->cif_service_type/*"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"*/, "GetCommonLinkProperties", NULL, 0); } +int upnpc_delete_port_mapping(upnpc_t * p, + const char * remote_host, unsigned short ext_port, + const char * proto) +{ + struct upnp_args args[3]; + char ext_port_str[8]; + + if(proto == NULL || ext_port == 0) + return UPNPC_ERR_INVALID_ARGS; + snprintf(ext_port_str, sizeof(ext_port_str), "%hu", ext_port); + args[0].elt = "NewRemoteHost"; + args[0].val = remote_host?remote_host:""; + args[1].elt = "NewExternalPort"; + args[1].val = ext_port_str; + args[2].elt = "NewProtocol"; + args[2].val = proto; + return upnpc_send_soap_request(p, p->control_conn_url, + p->conn_service_type,/*"urn:schemas-upnp-org:service:WANIPConnection:1",*/ + "DeletePortMapping", + args, 3); +} + int upnpc_add_port_mapping(upnpc_t * p, const char * remote_host, unsigned short ext_port, unsigned short int_port, const char * int_client, @@ -665,7 +695,7 @@ int upnpc_add_port_mapping(upnpc_t * p, args[7].elt = "NewLeaseDuration"; args[7].val = lease_duration_str; return upnpc_send_soap_request(p, p->control_conn_url, - "urn:schemas-upnp-org:service:WANIPConnection:1", + p->conn_service_type/*"urn:schemas-upnp-org:service:WANIPConnection:1"*/, "AddPortMapping", args, 8); } diff --git a/miniupnpc-libevent/miniupnpc-libevent.h b/miniupnpc-libevent/miniupnpc-libevent.h index e0b5207..306012f 100644 --- a/miniupnpc-libevent/miniupnpc-libevent.h +++ b/miniupnpc-libevent/miniupnpc-libevent.h @@ -1,4 +1,4 @@ -/* $Id: miniupnpc-libevent.h,v 1.3 2014/11/12 14:10:52 nanard Exp $ */ +/* $Id: miniupnpc-libevent.h,v 1.6 2014/11/17 09:17:38 nanard Exp $ */ /* miniupnpc-libevent * Copyright (c) 2008-2014, Thomas BERNARD * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ @@ -42,7 +42,9 @@ typedef struct { char * root_desc_location; struct evhttp_connection * desc_conn; char * control_cif_url; + char * cif_service_type; char * control_conn_url; + char * conn_service_type; struct evhttp_connection * soap_conn; struct NameValueParserData soap_response_data; upnpc_callback_fn ready_cb; @@ -65,6 +67,10 @@ int upnpc_add_port_mapping(upnpc_t * p, const char * proto, const char * description, unsigned int lease_duration); +int upnpc_delete_port_mapping(upnpc_t * p, + const char * remote_host, unsigned short ext_port, + const char * proto); + #ifdef UPNPC_USE_SELECT int upnpc_select_fds(upnpc_t * p, int * nfds, fd_set * readfds, fd_set * writefds); #endif /* UPNPC_USE_SELECT */ diff --git a/miniupnpc-libevent/upnpc-libevent.c b/miniupnpc-libevent/upnpc-libevent.c index d9ad60a..104ad5f 100644 --- a/miniupnpc-libevent/upnpc-libevent.c +++ b/miniupnpc-libevent/upnpc-libevent.c @@ -1,4 +1,4 @@ -/* $Id: upnpc-libevent.c,v 1.5 2014/11/13 09:46:12 nanard Exp $ */ +/* $Id: upnpc-libevent.c,v 1.7 2014/11/14 11:37:45 nanard Exp $ */ /* miniupnpc-libevent * Copyright (c) 2008-2014, Thomas BERNARD * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ @@ -18,16 +18,22 @@ #include #include #include +#include +#include +#include +#include #include "miniupnpc-libevent.h" static struct event_base *base = NULL; +static char local_address[32]; static void sighandler(int signal) { (void)signal; /*printf("signal %d\n", signal);*/ - event_base_loopbreak(base); + if(base != NULL) + event_base_loopbreak(base); } /* ready callback */ @@ -39,7 +45,13 @@ static void ready(int code, void * data) upnpc_get_external_ip_address(p); } -static enum { EGetExtIp = 0, EGetMaxRate, EAddPortMapping, EFinished } state = EGetExtIp; +static enum { + EGetExtIp = 0, + EGetMaxRate, + EAddPortMapping, + EDeletePortMapping, + EFinished + } state = EGetExtIp; /* soap callback */ static void soap(int code, void * data) @@ -55,11 +67,16 @@ static void soap(int code, void * data) break; case EGetMaxRate: printf("DownStream MaxBitRate = %s\t", GetValueFromNameValueList(&p->soap_response_data, "NewLayer1DownstreamMaxBitRate")); - upnpc_add_port_mapping(p, NULL, 60001, 60002, "192.168.0.42", "TCP", "test port mapping", 0); + upnpc_add_port_mapping(p, NULL, 60001, 60002, local_address, "TCP", "test port mapping", 0); printf("UpStream MaxBitRate = %s\n", GetValueFromNameValueList(&p->soap_response_data, "NewLayer1UpstreamMaxBitRate")); state = EAddPortMapping; break; case EAddPortMapping: + printf("OK!\n"); + upnpc_delete_port_mapping(p, NULL, 60001, "TCP"); + state = EDeletePortMapping; + break; + case EDeletePortMapping: printf("OK!\n"); state = EFinished; default: @@ -75,6 +92,51 @@ static void soap(int code, void * data) } } +/* use a UDP "connection" to 8.8.8.8 + * to retrieve local address */ +int find_local_address(void) +{ + int s; + struct sockaddr_in local, remote; + socklen_t len; + + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if(s < 0) { + perror("socket"); + return -1; + } + + memset(&local, 0, sizeof(local)); + memset(&remote, 0, sizeof(remote)); + /* bind to local port 4567 */ + local.sin_family = AF_INET; + local.sin_port = htons(4567); + local.sin_addr.s_addr = htonl(INADDR_ANY); + if(bind(s, (struct sockaddr *)&local, sizeof(local)) < 0) { + perror("bind"); + return -1; + } + /* "connect" google's DNS server at 8.8.8.8 port 4567 */ + remote.sin_family = AF_INET; + remote.sin_port = htons(4567); + remote.sin_addr.s_addr = inet_addr("8.8.8.8"); + if(connect(s, (struct sockaddr *)&remote, sizeof(remote)) < 0) { + perror("connect"); + return -1; + } + len = sizeof(local); + if(getsockname(s, (struct sockaddr *)&local, &len) < 0) { + perror("getsockname"); + return -1; + } + if(inet_ntop(AF_INET, &(local.sin_addr), local_address, sizeof(local_address)) == NULL) { + perror("inet_ntop"); + return -1; + } + printf("local address : %s\n", local_address); + close(s); + return 0; +} /* program entry point */ @@ -93,6 +155,11 @@ int main(int argc, char * * argv) if(sigaction(SIGINT, &sa, NULL) < 0) { perror("sigaction"); } + + if(find_local_address() < 0) { + fprintf(stderr, "failed to get local address\n"); + return 1; + } #ifdef DEBUG event_enable_debug_mode(); #if LIBEVENT_VERSION_NUMBER >= 0x02010100 diff --git a/miniupnpc/CMakeLists.txt b/miniupnpc/CMakeLists.txt index 2f97601..6df60fb 100644 --- a/miniupnpc/CMakeLists.txt +++ b/miniupnpc/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 2.6) project (miniupnpc C) set (MINIUPNPC_VERSION 1.9) -set (MINIUPNPC_API_VERSION 11) +set (MINIUPNPC_API_VERSION 12) if (NOT CMAKE_BUILD_TYPE) if (WIN32) diff --git a/miniupnpc/Changelog.txt b/miniupnpc/Changelog.txt index 45026ab..e8e50fc 100644 --- a/miniupnpc/Changelog.txt +++ b/miniupnpc/Changelog.txt @@ -1,6 +1,15 @@ $Id: Changelog.txt,v 1.199 2014/11/05 06:06:37 nanard Exp $ miniUPnP client Changelog. +2014/11/17: + search all : + upnpDiscoverDevices() / upnpDiscoverAll() functions + listdevices executable + increment API_VERSION to 12 + +2014/11/13: + increment API_VERSION to 11 + 2014/11/05: simplified function GetUPNPUrls() diff --git a/miniupnpc/Makefile b/miniupnpc/Makefile index d21440f..c0a288e 100644 --- a/miniupnpc/Makefile +++ b/miniupnpc/Makefile @@ -55,7 +55,7 @@ ifeq (SunOS, $(OS)) endif # APIVERSION is used to build SONAME -APIVERSION = 11 +APIVERSION = 12 SRCS = igd_desc_parse.c miniupnpc.c minixml.c minisoap.c miniwget.c \ upnpc.c upnpcommands.c upnpreplyparse.c testminixml.c \ diff --git a/miniupnpc/apiversions.txt b/miniupnpc/apiversions.txt index 9761b41..2e041e8 100644 --- a/miniupnpc/apiversions.txt +++ b/miniupnpc/apiversions.txt @@ -2,10 +2,21 @@ $Id: apiversions.txt,v 1.3 2014/01/31 13:14:32 nanard Exp $ Differences in API between miniUPnPc versions +API version 12 +miniupnpc.h : + add upnpDiscoverAll() / upnpDiscoverDevice() / upnpDiscoverDevices() + functions + updated macros : + #define MINIUPNPC_API_VERSION 12 + API version 11 -add upnpDiscoverAll() / upnpDiscoverDevice() / upnpDiscoverDevices() -functions +upnpreplyparse.h / portlistingparse.h : + removed usage of sys/queue.h / bsdqueue.h + +miniupnpc.h: + updated macros : + #define MINIUPNPC_API_VERSION 11 ====================== miniUPnPc version 1.9 ====================== API version 10 diff --git a/miniupnpc/miniupnpc.h b/miniupnpc/miniupnpc.h index 90d505c..0cf160c 100644 --- a/miniupnpc/miniupnpc.h +++ b/miniupnpc/miniupnpc.h @@ -19,7 +19,7 @@ /* versions : */ #define MINIUPNPC_VERSION "1.9" -#define MINIUPNPC_API_VERSION 11 +#define MINIUPNPC_API_VERSION 12 #ifdef __cplusplus extern "C" { diff --git a/miniupnpc/receivedata.c b/miniupnpc/receivedata.c index f9b9901..fb05e09 100644 --- a/miniupnpc/receivedata.c +++ b/miniupnpc/receivedata.c @@ -1,16 +1,17 @@ -/* $Id: receivedata.c,v 1.4 2012/06/23 22:34:47 nanard Exp $ */ +/* $Id: receivedata.c,v 1.6 2014/11/13 13:51:52 nanard Exp $ */ /* Project : miniupnp * Website : http://miniupnp.free.fr/ * Author : Thomas Bernard - * Copyright (c) 2011-2012 Thomas Bernard + * Copyright (c) 2011-2014 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. */ #include +#include #ifdef _WIN32 #include #include -#else +#else /* _WIN32 */ #include #if defined(__amigaos__) && !defined(__amigaos4__) #define socklen_t int @@ -21,10 +22,10 @@ #include #if !defined(__amigaos__) && !defined(__amigaos4__) #include -#endif +#endif /* !defined(__amigaos__) && !defined(__amigaos4__) */ #include #define MINIUPNPC_IGNORE_EINTR -#endif +#endif /* _WIN32 */ #ifdef _WIN32 #define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError()); @@ -42,20 +43,20 @@ receivedata(int socket, #if MINIUPNPC_GET_SRC_ADDR struct sockaddr_storage src_addr; socklen_t src_addr_len = sizeof(src_addr); -#endif +#endif /* MINIUPNPC_GET_SRC_ADDR */ int n; #if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) /* using poll */ struct pollfd fds[1]; /* for the poll */ #ifdef MINIUPNPC_IGNORE_EINTR do { -#endif +#endif /* MINIUPNPC_IGNORE_EINTR */ fds[0].fd = socket; fds[0].events = POLLIN; n = poll(fds, 1, timeout); #ifdef MINIUPNPC_IGNORE_EINTR } while(n < 0 && errno == EINTR); -#endif +#endif /* MINIUPNPC_IGNORE_EINTR */ if(n < 0) { PRINT_SOCKET_ERROR("poll"); return -1; @@ -63,7 +64,7 @@ receivedata(int socket, /* timeout */ return 0; } -#else /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */ +#else /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */ /* using select under _WIN32 and amigaos */ fd_set socketSet; TIMEVAL timeval; @@ -78,13 +79,14 @@ receivedata(int socket, } else if(n == 0) { return 0; } -#endif +#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */ #if MINIUPNPC_GET_SRC_ADDR + memset(&src_addr, 0, sizeof(src_addr)); n = recvfrom(socket, data, length, 0, (struct sockaddr *)&src_addr, &src_addr_len); -#else +#else /* MINIUPNPC_GET_SRC_ADDR */ n = recv(socket, data, length, 0); -#endif +#endif /* MINIUPNPC_GET_SRC_ADDR */ if(n<0) { PRINT_SOCKET_ERROR("recv"); } @@ -93,12 +95,11 @@ receivedata(int socket, const struct sockaddr_in6 * src_addr6 = (struct sockaddr_in6 *)&src_addr; #ifdef DEBUG printf("scope_id=%u\n", src_addr6->sin6_scope_id); -#endif +#endif /* DEBUG */ if(scope_id) *scope_id = src_addr6->sin6_scope_id; } -#endif +#endif /* MINIUPNPC_GET_SRC_ADDR */ return n; } -