diff --git a/miniupnpc-async/miniupnpc-async.c b/miniupnpc-async/miniupnpc-async.c index ad16ade..0ef3559 100644 --- a/miniupnpc-async/miniupnpc-async.c +++ b/miniupnpc-async/miniupnpc-async.c @@ -1,6 +1,6 @@ /* $Id: miniupnpc-async.c,v 1.19 2014/11/07 12:05:40 nanard Exp $ */ /* miniupnpc-async - * Copyright (c) 2008-2014, Thomas BERNARD + * Copyright (c) 2008-2017, Thomas BERNARD * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * * Permission to use, copy, modify, and/or distribute this software for any @@ -72,8 +72,8 @@ struct upnp_args { /* private functions */ -static int upnpc_connect(upnpc_t * p, const char * url); -static int upnpc_send_request(upnpc_t * p); +static int upnpc_connect(upnpc_device_t * p, const char * url); +static int upnpc_send_request(upnpc_device_t * p); /* parse_msearch_reply() @@ -83,8 +83,8 @@ static int upnpc_send_request(upnpc_t * p); * The strings are NOT null terminated */ static void parse_msearch_reply(const char * reply, int size, - const char * * location, int * locationsize, - const char * * st, int * stsize) + const char * * location, unsigned int * locationsize, + const char * * st, unsigned int * stsize) { int a, b, i; i = 0; /* current character index */ @@ -157,16 +157,16 @@ static int upnpc_send_ssdp_msearch(upnpc_t * p, const char * device, unsigned in return 0; } -static int upnpc_set_root_desc_location(upnpc_t * p, const char * location, int locationsize) +static int upnpc_set_root_desc_location(upnpc_device_t * d, const char * location, int locationsize) { char * tmp; - tmp = realloc(p->root_desc_location, locationsize + 1); + tmp = realloc(d->root_desc_location, locationsize + 1); if(tmp == 0) { return -1; } memcpy(tmp, location, locationsize); tmp[locationsize] = '\0'; - p->root_desc_location = tmp; + d->root_desc_location = tmp; return 0; } @@ -181,20 +181,38 @@ static int upnpc_receive_and_parse_ssdp(upnpc_t * p) debug_printf("empty packet received\n"); } else { const char * location = NULL; - int locationsize; + unsigned int locationsize; const char * st = NULL; - int stsize; + unsigned int stsize; debug_printf("%.*s", n, bufr); parse_msearch_reply(bufr, n, &location, &locationsize, &st, &stsize); debug_printf("location = '%.*s'\n", locationsize, location); debug_printf("st = '%.*s'\n", stsize, st); if(location != NULL) { - if(upnpc_set_root_desc_location(p, location, locationsize) < 0) { + upnpc_device_t * dev = p->device_list; + while(dev != NULL) { + if(dev->root_desc_location != NULL + && strlen(dev->root_desc_location) == locationsize + && memcmp(dev->root_desc_location, location, locationsize) == 0) { + debug_printf("device already in list (location='%s')\n", dev->root_desc_location); + return -1; + } + dev = dev->next; + } + dev = calloc(1, sizeof(upnpc_device_t)); + if(dev == NULL) { p->state = EError; return -1; } - p->state = EGetDescConnect; - upnpc_connect(p, p->root_desc_location); + if(upnpc_set_root_desc_location(dev, location, locationsize) < 0) { + free(dev); + p->state = EError; + return -1; + } + dev->next = p->device_list; + p->device_list = dev; + dev->state = EGetDescConnect; + upnpc_connect(dev, dev->root_desc_location); } else { /* or do nothing ? */ p->state = EError; @@ -298,7 +316,7 @@ parseURL(const char * url, return 1; } -static int upnpc_connect(upnpc_t * p, const char * url) +static int upnpc_connect(upnpc_device_t * p, const char * url) { int r; char hostname[MAXHOSTNAMELEN+1]; @@ -308,10 +326,10 @@ static int upnpc_connect(upnpc_t * p, const char * url) struct sockaddr_in addr; socklen_t addrlen; - if(p->root_desc_location == 0) { + /*if(p->root_desc_location == 0) { p->state = EError; return -1; - } + }*/ if(!parseURL(url/*p->root_desc_location*/, hostname, &port, &path, &scope_id)) { p->state = EError; @@ -353,7 +371,7 @@ static int upnpc_connect(upnpc_t * p, const char * url) return 0; } -static int upnpc_complete_connect(upnpc_t * p) +static int upnpc_complete_connect(upnpc_device_t * p) { socklen_t len; int err; @@ -376,7 +394,7 @@ static int upnpc_complete_connect(upnpc_t * p) return 0; } -static int upnpc_send_request(upnpc_t * p) +static int upnpc_send_request(upnpc_device_t * p) { ssize_t n; static const char reqfmt[] = "GET %s HTTP/1.1\r\n" @@ -440,7 +458,7 @@ static int upnpc_send_request(upnpc_t * p) return 0; } -static int upnpc_parse_headers(upnpc_t * p) +static int upnpc_parse_headers(upnpc_device_t * p) { /* search for CR LF CR LF (end of headers) * recognize also LF LF */ @@ -547,7 +565,7 @@ static char * build_url_string(const char * urlbase, const char * root_desc_url, return s; } -static int upnpc_get_response(upnpc_t * p) +static int upnpc_get_response(upnpc_device_t * p) { ssize_t n; ssize_t count; @@ -643,7 +661,7 @@ static int upnpc_get_response(upnpc_t * p) #define SERVICEPREFIX "u" #define SERVICEPREFIX2 'u' -static int upnpc_build_soap_request(upnpc_t * p, const char * url, +static int upnpc_build_soap_request(upnpc_device_t * p, const char * url, const char * service, const char * action, const struct upnp_args * args, int arg_count) @@ -789,30 +807,35 @@ int upnpc_init(upnpc_t * p, const char * multicastif) int upnpc_finalize(upnpc_t * p) { if(!p) return UPNPC_ERR_INVALID_ARGS; - free(p->root_desc_location); - p->root_desc_location = NULL; - free(p->http_request); - p->http_request = NULL; - free(p->http_response); - p->http_response = NULL; - free(p->control_cif_url); - p->control_cif_url = NULL; - free(p->control_conn_url); - p->control_conn_url = NULL; if(p->ssdp_socket >= 0) { close(p->ssdp_socket); p->ssdp_socket = -1; } - if(p->http_socket >= 0) { - close(p->http_socket); - p->http_socket = -1; + while(p->device_list) { + upnpc_device_t * next = p->device_list->next; + free(p->device_list->root_desc_location); + p->device_list->root_desc_location = NULL; + free(p->device_list->http_request); + p->device_list->http_request = NULL; + free(p->device_list->http_response); + p->device_list->http_response = NULL; + free(p->device_list->control_cif_url); + p->device_list->control_cif_url = NULL; + free(p->device_list->control_conn_url); + p->device_list->control_conn_url = NULL; + if(p->device_list->http_socket >= 0) { + close(p->device_list->http_socket); + p->device_list->http_socket = -1; + } + ClearNameValueList(&p->device_list->soap_response_data); + free(p->device_list); + p->device_list = next; } - ClearNameValueList(&p->soap_response_data); p->state = EFinalized; return UPNPC_OK; } -int upnpc_get_external_ip_address(upnpc_t * p) +int upnpc_get_external_ip_address(upnpc_device_t * p) { upnpc_build_soap_request(p, p->control_conn_url, "urn:schemas-upnp-org:service:WANIPConnection:1", @@ -822,7 +845,7 @@ int upnpc_get_external_ip_address(upnpc_t * p) return 0; } -int upnpc_get_link_layer_max_rate(upnpc_t * p) +int upnpc_get_link_layer_max_rate(upnpc_device_t * p) { upnpc_build_soap_request(p, p->control_cif_url, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1", @@ -832,7 +855,7 @@ int upnpc_get_link_layer_max_rate(upnpc_t * p) return 0; } -int upnpc_add_port_mapping(upnpc_t * p, +int upnpc_add_port_mapping(upnpc_device_t * p, const char * remote_host, unsigned short ext_port, unsigned short int_port, const char * int_client, const char * proto, const char * description, @@ -876,8 +899,32 @@ int upnpc_add_port_mapping(upnpc_t * p, #ifdef UPNPC_USE_SELECT int upnpc_select_fds(upnpc_t * p, int * nfds, fd_set * readfds, fd_set * writefds) { + upnpc_device_t * d; int n = 0; if(!p) return UPNPC_ERR_INVALID_ARGS; + for(d = p->device_list; d != NULL; d = d->next) { + switch(d->state) { + case EGetDescConnect: + case EGetDescRequest: + case ESoapConnect: + case ESoapRequest: + FD_SET(d->http_socket, writefds); + if(*nfds < d->http_socket) + *nfds = d->http_socket; + n++; + break; + case EGetDescResponse: + case ESoapResponse: + FD_SET(d->http_socket, readfds); + if(*nfds < d->http_socket) + *nfds = d->http_socket; + n++; + break; + default: + break; + } + } + switch(p->state) { case ESendSSDP: FD_SET(p->ssdp_socket, writefds); @@ -886,32 +933,35 @@ int upnpc_select_fds(upnpc_t * p, int * nfds, fd_set * readfds, fd_set * writefd n++; break; case EReceiveSSDP: + default: + /* still receive SSDP responses when processing Description, etc. */ FD_SET(p->ssdp_socket, readfds); if(*nfds < p->ssdp_socket) *nfds = p->ssdp_socket; n++; break; - case EGetDescConnect: - case EGetDescRequest: - case ESoapConnect: - case ESoapRequest: - FD_SET(p->http_socket, writefds); - if(*nfds < p->http_socket) - *nfds = p->http_socket; - n++; - break; - case EGetDescResponse: - case ESoapResponse: - FD_SET(p->http_socket, readfds); - if(*nfds < p->http_socket) - *nfds = p->http_socket; - n++; - break; - default: - return 0; } return n; } + +void upnpc_check_select_fds(upnpc_t * p, const fd_set * readfds, const fd_set * writefds) +{ + upnpc_device_t * d; + + p->socket_flags = 0; + if(FD_ISSET(p->ssdp_socket, readfds)) + p->socket_flags = UPNPC_SSDP_READABLE; + if(FD_ISSET(p->ssdp_socket, writefds)) + p->socket_flags = UPNPC_SSDP_WRITEABLE; + + for(d = p->device_list; d != NULL; d = d->next) { + d->socket_flags = 0; + if(FD_ISSET(d->http_socket, readfds)) + d->socket_flags = UPNPC_HTTP_READABLE; + if(FD_ISSET(d->http_socket, writefds)) + d->socket_flags = UPNPC_HTTP_WRITEABLE; + } +} #endif static const char * devices_to_search[] = { @@ -924,6 +974,7 @@ static const char * devices_to_search[] = { int upnpc_process(upnpc_t * p) { + upnpc_device_t * d; /* 1) Envoyer les paquets de discovery SSDP 2) Recevoir et traiter les reponses @@ -931,7 +982,36 @@ int upnpc_process(upnpc_t * p) 4) tester les etats */ if(!p) return UPNPC_ERR_INVALID_ARGS; - debug_printf("state=%d\n", (int)p->state); + debug_printf("state=%d socket_flags=0x%04x\n", (int)p->state, p->socket_flags); + + for(d = p->device_list; d != NULL; d = d->next) { + switch(d->state) { + case EGetDescConnect: + case ESoapConnect: + upnpc_complete_connect(d); + break; + case EGetDescRequest: + case ESoapRequest: + upnpc_send_request(d); + break; + case EGetDescResponse: + case ESoapResponse: + upnpc_get_response(d); + break; + default: + break; + } + } + /* all devices ready => ready */ + if(p->device_list != NULL) { + d = p->device_list; + while(d && d->state == EReady) d = d->next; + p->state = (d == NULL) ? EReady : EProcessing; + } + + if(p->socket_flags & UPNPC_SSDP_READABLE) { + upnpc_receive_and_parse_ssdp(p); + } switch(p->state) { case EInit: upnpc_send_ssdp_msearch(p, devices_to_search[0], 2); @@ -940,22 +1020,13 @@ int upnpc_process(upnpc_t * p) upnpc_send_ssdp_msearch(p, devices_to_search[0], 2); break; case EReceiveSSDP: - upnpc_receive_and_parse_ssdp(p); + /*upnpc_receive_and_parse_ssdp(p);*/ break; /*case EGetDesc: upnpc_connect(p); break;*/ - case EGetDescConnect: - case ESoapConnect: - upnpc_complete_connect(p); - break; - case EGetDescRequest: - case ESoapRequest: - upnpc_send_request(p); - break; - case EGetDescResponse: - case ESoapResponse: - upnpc_get_response(p); + case EReady: + case EProcessing: break; default: return UPNPC_ERR_UNKNOWN_STATE; diff --git a/miniupnpc-async/miniupnpc-async.h b/miniupnpc-async/miniupnpc-async.h index ccb2391..d4eff71 100644 --- a/miniupnpc-async/miniupnpc-async.h +++ b/miniupnpc-async/miniupnpc-async.h @@ -1,6 +1,6 @@ /* $Id: miniupnpc-async.h,v 1.13 2014/11/07 11:25:52 nanard Exp $ */ /* miniupnpc-async - * Copyright (c) 2008-2014, Thomas BERNARD + * Copyright (c) 2008-2017, Thomas BERNARD * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * * Permission to use, copy, modify, and/or distribute this software for any @@ -30,12 +30,15 @@ extern "C" { #define UPNPC_ERR_BIND_FAILED (-3) #define UPNPC_ERR_UNKNOWN_STATE (-4) -typedef struct { +#define UPNPC_SSDP_READABLE 0x0001 +#define UPNPC_SSDP_WRITEABLE 0x0100 +#define UPNPC_HTTP_READABLE 0x0002 +#define UPNPC_HTTP_WRITEABLE 0x0200 + +typedef struct upnpc_device { + struct upnpc_device * next; enum { EInit = 1, - ESendSSDP, - EReceiveSSDP, - /*EGetDesc,*/ EGetDescConnect, EGetDescRequest, EGetDescResponse, @@ -46,9 +49,11 @@ typedef struct { EFinalized = 99, EError = 1000 } state; - int ssdp_socket; char * root_desc_location; + char * control_cif_url; + char * control_conn_url; int http_socket; + int socket_flags; /* see UPNPC_*_READABLE, etc. */ char * http_request; int http_request_len; int http_request_sent; @@ -58,20 +63,34 @@ typedef struct { int http_response_content_length; int http_response_chunked; int http_response_code; - char * control_cif_url; - char * control_conn_url; struct NameValueParserData soap_response_data; +} upnpc_device_t; + +typedef struct { + enum { + EInit = 1, + ESendSSDP, + EReceiveSSDP, + EGetDesc, + EReady, + EProcessing, + EFinalized = 99, + EError = 1000 + } state; + int socket_flags; /* see UPNPC_*_READABLE, etc. */ + int ssdp_socket; + upnpc_device_t * device_list; } upnpc_t; int upnpc_init(upnpc_t * p, const char * multicastif); int upnpc_finalize(upnpc_t * p); -int upnpc_get_external_ip_address(upnpc_t * p); +int upnpc_get_external_ip_address(upnpc_device_t * p); -int upnpc_get_link_layer_max_rate(upnpc_t * p); +int upnpc_get_link_layer_max_rate(upnpc_device_t * p); -int upnpc_add_port_mapping(upnpc_t * p, +int upnpc_add_port_mapping(upnpc_device_t * p, const char * remote_host, unsigned short ext_port, unsigned short int_port, const char * int_client, const char * proto, const char * description, @@ -79,6 +98,7 @@ int upnpc_add_port_mapping(upnpc_t * p, #ifdef UPNPC_USE_SELECT int upnpc_select_fds(upnpc_t * p, int * nfds, fd_set * readfds, fd_set * writefds); +void upnpc_check_select_fds(upnpc_t * p, const fd_set * readfds, const fd_set * writefds); #endif /* UPNPC_USE_SELECT */ int upnpc_process(upnpc_t * p); diff --git a/miniupnpc-async/testasync.c b/miniupnpc-async/testasync.c index ad43c5e..9a970e3 100644 --- a/miniupnpc-async/testasync.c +++ b/miniupnpc-async/testasync.c @@ -1,6 +1,6 @@ /* $Id: testasync.c,v 1.14 2014/11/07 12:07:38 nanard Exp $ */ /* miniupnpc-async - * Copyright (c) 2008-2014, Thomas BERNARD + * Copyright (c) 2008-2017, Thomas BERNARD * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * * Permission to use, copy, modify, and/or distribute this software for any @@ -43,7 +43,7 @@ int main(int argc, char * * argv) } r = upnpc_process(&upnp); printf("upnpc_process returned %d\n", r); - while((upnp.state != EReady) && (upnp.state != EError)) { + while(upnp.state != EError) { int nfds; fd_set readfds; fd_set writefds; @@ -68,6 +68,7 @@ int main(int argc, char * * argv) perror("select"); return 1; } + upnpc_check_select_fds(&upnp, &readfds, &writefds); r = upnpc_process(&upnp); #if DEBUG printf("upnpc_process returned %d\n", r); @@ -76,18 +77,18 @@ int main(int argc, char * * argv) break; if(upnp.state == EReady) { char * p; - printf("Process UPnP IGD Method results : HTTP %d\n", upnp.http_response_code); - if(upnp.http_response_code == 200) { + printf("Process UPnP IGD Method results : HTTP %d\n", upnp.device_list->http_response_code); /* XXX */ + if(upnp.device_list->http_response_code == 200) { switch(last_method) { case EGetExternalIP: - p = GetValueFromNameValueList(&upnp.soap_response_data, "NewExternalIPAddress"); + p = GetValueFromNameValueList(&upnp.device_list->soap_response_data, "NewExternalIPAddress"); printf("ExternalIPAddress = %s\n", p); /* p = GetValueFromNameValueList(&pdata, "errorCode");*/ break; case EGetRates: - p = GetValueFromNameValueList(&upnp.soap_response_data, "NewLayer1DownstreamMaxBitRate"); + p = GetValueFromNameValueList(&upnp.device_list->soap_response_data, "NewLayer1DownstreamMaxBitRate"); printf("DownStream MaxBitRate = %s\t", p); - p = GetValueFromNameValueList(&upnp.soap_response_data, "NewLayer1UpstreamMaxBitRate"); + p = GetValueFromNameValueList(&upnp.device_list->soap_response_data, "NewLayer1UpstreamMaxBitRate"); printf("UpStream MaxBitRate = %s\n", p); break; case EAddPortMapping: @@ -98,10 +99,10 @@ int main(int argc, char * * argv) } } else { printf("SOAP error :\n"); - printf(" faultcode='%s'\n", GetValueFromNameValueList(&upnp.soap_response_data, "faultcode")); - printf(" faultstring='%s'\n", GetValueFromNameValueList(&upnp.soap_response_data, "faultstring")); - printf(" errorCode=%s\n", GetValueFromNameValueList(&upnp.soap_response_data, "errorCode")); - printf(" errorDescription='%s'\n", GetValueFromNameValueList(&upnp.soap_response_data, "errorDescription")); + printf(" faultcode='%s'\n", GetValueFromNameValueList(&upnp.device_list->soap_response_data, "faultcode")); + printf(" faultstring='%s'\n", GetValueFromNameValueList(&upnp.device_list->soap_response_data, "faultstring")); + printf(" errorCode=%s\n", GetValueFromNameValueList(&upnp.device_list->soap_response_data, "errorCode")); + printf(" errorDescription='%s'\n", GetValueFromNameValueList(&upnp.device_list->soap_response_data, "errorDescription")); } if(next_method_to_call == ENothing) break; @@ -110,17 +111,17 @@ int main(int argc, char * * argv) switch(next_method_to_call) { case EGetExternalIP: printf("GetExternalIPAddress\n"); - upnpc_get_external_ip_address(&upnp); + upnpc_get_external_ip_address(upnp.device_list); next_method_to_call = EGetRates; break; case EGetRates: printf("GetCommonLinkProperties\n"); - upnpc_get_link_layer_max_rate(&upnp); + upnpc_get_link_layer_max_rate(upnp.device_list); next_method_to_call = EAddPortMapping; break; case EAddPortMapping: printf("AddPortMapping\n"); - upnpc_add_port_mapping(&upnp, + upnpc_add_port_mapping(upnp.device_list, /* XXX */ NULL /* remote_host */, 40002 /* ext_port */, 42042 /* int_port */, "192.168.1.202" /* int_client */, "TCP" /* proto */, "this is a test" /* description */,