1 /* $Id: miniupnpc.c,v 1.111 2012/10/09 17:53:14 nanard Exp $ */
2 /* Project : miniupnp
3 * Web : http://miniupnp.free.fr/
4 * Author : Thomas BERNARD
5 * copyright (c) 2005-2012 Thomas Bernard
6 * This software is subjet to the conditions detailed in the
7 * provided LICENSE file. */
8 #define __EXTENSIONS__ 1
9 #if !defined(MACOSX) && !defined(__sun)
10 #if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__) && !defined(__NetBSD__)
11 #ifndef __cplusplus
12 #define _XOPEN_SOURCE 600
13 #endif
14 #endif
15 #ifndef __BSD_VISIBLE
16 #define __BSD_VISIBLE 1
17 #endif
18 #endif
19
20 #if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(MACOSX) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__HAIKU__)
21 #define HAS_IP_MREQN
22 #endif
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #ifdef _WIN32
28 /* Win32 Specific includes and defines */
29 #include <winsock2.h>
30 #include <ws2tcpip.h>
31 #include <io.h>
32 #include <iphlpapi.h>
33 #define snprintf _snprintf
34 #define strdup _strdup
35 #ifndef strncasecmp
36 #if defined(_MSC_VER) && (_MSC_VER >= 1400)
37 #define strncasecmp _memicmp
38 #else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
39 #define strncasecmp memicmp
40 #endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
41 #endif /* #ifndef strncasecmp */
42 #define MAXHOSTNAMELEN 64
43 #else /* #ifdef _WIN32 */
44 /* Standard POSIX includes */
45 #include <unistd.h>
46 #if defined(__amigaos__) && !defined(__amigaos4__)
47 /* Amiga OS 3 specific stuff */
48 #define socklen_t int
49 #else
50 #include <sys/select.h>
51 #endif
52 #include <sys/socket.h>
53 #include <sys/types.h>
54 #include <sys/param.h>
55 #include <netinet/in.h>
56 #include <arpa/inet.h>
57 #include <netdb.h>
58 #include <net/if.h>
59 #if !defined(__amigaos__) && !defined(__amigaos4__)
60 #include <poll.h>
61 #endif
62 #include <strings.h>
63 #include <errno.h>
64 #define closesocket close
65 #endif /* #else _WIN32 */
66 #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
67 #include <sys/time.h>
68 #endif
69 #if defined(__amigaos__) || defined(__amigaos4__)
70 /* Amiga OS specific stuff */
71 #define TIMEVAL struct timeval
72 #endif
73
74 #include "miniupnpc.h"
75 #include "minissdpc.h"
76 #include "miniwget.h"
77 #include "minisoap.h"
78 #include "minixml.h"
79 #include "upnpcommands.h"
80 #include "connecthostport.h"
81 #include "receivedata.h"
82
83 #ifdef _WIN32
84 #define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
85 #else
86 #define PRINT_SOCKET_ERROR(x) perror(x)
87 #endif
88
89 #define SOAPPREFIX "s"
90 #define SERVICEPREFIX "u"
91 #define SERVICEPREFIX2 'u'
92
93 /* root description parsing */
parserootdesc(const char * buffer,int bufsize,struct IGDdatas * data)94 LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
95 {
96 struct xmlparser parser;
97 /* xmlparser object */
98 parser.xmlstart = buffer;
99 parser.xmlsize = bufsize;
100 parser.data = data;
101 parser.starteltfunc = IGDstartelt;
102 parser.endeltfunc = IGDendelt;
103 parser.datafunc = IGDdata;
104 parser.attfunc = 0;
105 parsexml(&parser);
106 #ifdef DEBUG
107 printIGD(data);
108 #endif
109 }
110
111 /* simpleUPnPcommand2 :
112 * not so simple !
113 * return values :
114 * pointer - OK
115 * NULL - error */
simpleUPnPcommand2(int s,const char * url,const char * service,const char * action,struct UPNParg * args,int * bufsize,const char * httpversion)116 char * simpleUPnPcommand2(int s, const char * url, const char * service,
117 const char * action, struct UPNParg * args,
118 int * bufsize, const char * httpversion)
119 {
120 char hostname[MAXHOSTNAMELEN+1];
121 unsigned short port = 0;
122 char * path;
123 char soapact[128];
124 char soapbody[2048];
125 char * buf;
126 int n;
127
128 *bufsize = 0;
129 snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
130 if(args==NULL)
131 {
132 /*soapbodylen = */snprintf(soapbody, sizeof(soapbody),
133 "<?xml version=\"1.0\"?>\r\n"
134 "<" SOAPPREFIX ":Envelope "
135 "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
136 SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
137 "<" SOAPPREFIX ":Body>"
138 "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
139 "</" SERVICEPREFIX ":%s>"
140 "</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
141 "\r\n", action, service, action);
142 }
143 else
144 {
145 char * p;
146 const char * pe, * pv;
147 int soapbodylen;
148 soapbodylen = snprintf(soapbody, sizeof(soapbody),
149 "<?xml version=\"1.0\"?>\r\n"
150 "<" SOAPPREFIX ":Envelope "
151 "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
152 SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
153 "<" SOAPPREFIX ":Body>"
154 "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
155 action, service);
156 p = soapbody + soapbodylen;
157 while(args->elt)
158 {
159 /* check that we are never overflowing the string... */
160 if(soapbody + sizeof(soapbody) <= p + 100)
161 {
162 /* we keep a margin of at least 100 bytes */
163 return NULL;
164 }
165 *(p++) = '<';
166 pe = args->elt;
167 while(*pe)
168 *(p++) = *(pe++);
169 *(p++) = '>';
170 if((pv = args->val))
171 {
172 while(*pv)
173 *(p++) = *(pv++);
174 }
175 *(p++) = '<';
176 *(p++) = '/';
177 pe = args->elt;
178 while(*pe)
179 *(p++) = *(pe++);
180 *(p++) = '>';
181 args++;
182 }
183 *(p++) = '<';
184 *(p++) = '/';
185 *(p++) = SERVICEPREFIX2;
186 *(p++) = ':';
187 pe = action;
188 while(*pe)
189 *(p++) = *(pe++);
190 strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
191 soapbody + sizeof(soapbody) - p);
192 }
193 if(!parseURL(url, hostname, &port, &path, NULL)) return NULL;
194 if(s < 0) {
195 s = connecthostport(hostname, port, 0);
196 if(s < 0) {
197 /* failed to connect */
198 return NULL;
199 }
200 }
201
202 n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion);
203 if(n<=0) {
204 #ifdef DEBUG
205 printf("Error sending SOAP request\n");
206 #endif
207 closesocket(s);
208 return NULL;
209 }
210
211 buf = getHTTPResponse(s, bufsize);
212 #ifdef DEBUG
213 if(*bufsize > 0 && buf)
214 {
215 printf("SOAP Response :\n%.*s\n", *bufsize, buf);
216 }
217 #endif
218 closesocket(s);
219 return buf;
220 }
221
222 /* simpleUPnPcommand :
223 * not so simple !
224 * return values :
225 * pointer - OK
226 * NULL - error */
simpleUPnPcommand(int s,const char * url,const char * service,const char * action,struct UPNParg * args,int * bufsize)227 char * simpleUPnPcommand(int s, const char * url, const char * service,
228 const char * action, struct UPNParg * args,
229 int * bufsize)
230 {
231 char * buf;
232
233 #if 1
234 buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
235 #else
236 buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.0");
237 if (!buf || *bufsize == 0)
238 {
239 #if DEBUG
240 printf("Error or no result from SOAP request; retrying with HTTP/1.1\n");
241 #endif
242 buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
243 }
244 #endif
245 return buf;
246 }
247
248 /* parseMSEARCHReply()
249 * the last 4 arguments are filled during the parsing :
250 * - location/locationsize : "location:" field of the SSDP reply packet
251 * - st/stsize : "st:" field of the SSDP reply packet.
252 * The strings are NOT null terminated */
253 static void
parseMSEARCHReply(const char * reply,int size,const char ** location,int * locationsize,const char ** st,int * stsize)254 parseMSEARCHReply(const char * reply, int size,
255 const char * * location, int * locationsize,
256 const char * * st, int * stsize)
257 {
258 int a, b, i;
259 i = 0;
260 a = i; /* start of the line */
261 b = 0; /* end of the "header" (position of the colon) */
262 while(i<size)
263 {
264 switch(reply[i])
265 {
266 case ':':
267 if(b==0)
268 {
269 b = i; /* end of the "header" */
270 /*for(j=a; j<b; j++)
271 {
272 putchar(reply[j]);
273 }
274 */
275 }
276 break;
277 case '\x0a':
278 case '\x0d':
279 if(b!=0)
280 {
281 /*for(j=b+1; j<i; j++)
282 {
283 putchar(reply[j]);
284 }
285 putchar('\n');*/
286 /* skip the colon and white spaces */
287 do { b++; } while(reply[b]==' ');
288 if(0==strncasecmp(reply+a, "location", 8))
289 {
290 *location = reply+b;
291 *locationsize = i-b;
292 }
293 else if(0==strncasecmp(reply+a, "st", 2))
294 {
295 *st = reply+b;
296 *stsize = i-b;
297 }
298 b = 0;
299 }
300 a = i+1;
301 break;
302 default:
303 break;
304 }
305 i++;
306 }
307 }
308
309 /* port upnp discover : SSDP protocol */
310 #define PORT 1900
311 #define XSTR(s) STR(s)
312 #define STR(s) #s
313 #define UPNP_MCAST_ADDR "239.255.255.250"
314 /* for IPv6 */
315 #define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */
316 #define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */
317
318 /* upnpDiscover() :
319 * return a chained list of all devices found or NULL if
320 * no devices was found.
321 * It is up to the caller to free the chained list
322 * delay is in millisecond (poll) */
323 LIBSPEC struct UPNPDev *
upnpDiscover(int delay,const char * multicastif,const char * minissdpdsock,int sameport,int ipv6,int * error)324 upnpDiscover(int delay, const char * multicastif,
325 const char * minissdpdsock, int sameport,
326 int ipv6,
327 int * error)
328 {
329 struct UPNPDev * tmp;
330 struct UPNPDev * devlist = 0;
331 unsigned int scope_id = 0;
332 int opt = 1;
333 static const char MSearchMsgFmt[] =
334 "M-SEARCH * HTTP/1.1\r\n"
335 "HOST: %s:" XSTR(PORT) "\r\n"
336 "ST: %s\r\n"
337 "MAN: \"ssdp:discover\"\r\n"
338 "MX: %u\r\n"
339 "\r\n";
340 static const char * const deviceList[] = {
341 #if 0
342 "urn:schemas-upnp-org:device:InternetGatewayDevice:2",
343 "urn:schemas-upnp-org:service:WANIPConnection:2",
344 #endif
345 "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
346 "urn:schemas-upnp-org:service:WANIPConnection:1",
347 "urn:schemas-upnp-org:service:WANPPPConnection:1",
348 "upnp:rootdevice",
349 0
350 };
351 int deviceIndex = 0;
352 char bufr[1536]; /* reception and emission buffer */
353 int sudp;
354 int n;
355 struct sockaddr_storage sockudp_r;
356 unsigned int mx;
357 #ifdef NO_GETADDRINFO
358 struct sockaddr_storage sockudp_w;
359 #else
360 int rv;
361 struct addrinfo hints, *servinfo, *p;
362 #endif
363 #ifdef _WIN32
364 MIB_IPFORWARDROW ip_forward;
365 #endif
366 int linklocal = 1;
367
368 if(error)
369 *error = UPNPDISCOVER_UNKNOWN_ERROR;
370 #if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
371 /* first try to get infos from minissdpd ! */
372 if(!minissdpdsock)
373 minissdpdsock = "/var/run/minissdpd.sock";
374 while(!devlist && deviceList[deviceIndex]) {
375 devlist = getDevicesFromMiniSSDPD(deviceList[deviceIndex],
376 minissdpdsock);
377 /* We return what we have found if it was not only a rootdevice */
378 if(devlist && !strstr(deviceList[deviceIndex], "rootdevice")) {
379 if(error)
380 *error = UPNPDISCOVER_SUCCESS;
381 return devlist;
382 }
383 deviceIndex++;
384 }
385 deviceIndex = 0;
386 #endif
387 /* fallback to direct discovery */
388 #ifdef _WIN32
389 sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP);
390 #else
391 sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0);
392 #endif
393 if(sudp < 0)
394 {
395 if(error)
396 *error = UPNPDISCOVER_SOCKET_ERROR;
397 PRINT_SOCKET_ERROR("socket");
398 return NULL;
399 }
400 /* reception */
401 memset(&sockudp_r, 0, sizeof(struct sockaddr_storage));
402 if(ipv6) {
403 struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;
404 p->sin6_family = AF_INET6;
405 if(sameport)
406 p->sin6_port = htons(PORT);
407 p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */
408 } else {
409 struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
410 p->sin_family = AF_INET;
411 if(sameport)
412 p->sin_port = htons(PORT);
413 p->sin_addr.s_addr = INADDR_ANY;
414 }
415 #ifdef _WIN32
416 /* This code could help us to use the right Network interface for
417 * SSDP multicast traffic */
418 /* Get IP associated with the index given in the ip_forward struct
419 * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
420 if(!ipv6
421 && (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR)) {
422 DWORD dwRetVal = 0;
423 PMIB_IPADDRTABLE pIPAddrTable;
424 DWORD dwSize = 0;
425 #ifdef DEBUG
426 IN_ADDR IPAddr;
427 #endif
428 int i;
429 #ifdef DEBUG
430 printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop);
431 #endif
432 pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE));
433 if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
434 free(pIPAddrTable);
435 pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize);
436 }
437 if(pIPAddrTable) {
438 dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
439 #ifdef DEBUG
440 printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
441 #endif
442 for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
443 #ifdef DEBUG
444 printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
445 IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
446 printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
447 IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask;
448 printf("\tSubnet Mask[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
449 IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr;
450 printf("\tBroadCast[%d]: \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr);
451 printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize);
452 printf("\tType and State[%d]:", i);
453 printf("\n");
454 #endif
455 if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) {
456 /* Set the address of this interface to be used */
457 struct in_addr mc_if;
458 memset(&mc_if, 0, sizeof(mc_if));
459 mc_if.s_addr = pIPAddrTable->table[i].dwAddr;
460 if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
461 PRINT_SOCKET_ERROR("setsockopt");
462 }
463 ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr;
464 #ifndef DEBUG
465 break;
466 #endif
467 }
468 }
469 free(pIPAddrTable);
470 pIPAddrTable = NULL;
471 }
472 }
473 #endif
474
475 #ifdef _WIN32
476 if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
477 #else
478 if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
479 #endif
480 {
481 if(error)
482 *error = UPNPDISCOVER_SOCKET_ERROR;
483 PRINT_SOCKET_ERROR("setsockopt");
484 return NULL;
485 }
486
487 if(multicastif)
488 {
489 if(ipv6) {
490 #if !defined(_WIN32)
491 /* according to MSDN, if_nametoindex() is supported since
492 * MS Windows Vista and MS Windows Server 2008.
493 * http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */
494 unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
495 if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(&ifindex)) < 0)
496 {
497 PRINT_SOCKET_ERROR("setsockopt");
498 }
499 #else
500 #ifdef DEBUG
501 printf("Setting of multicast interface not supported in IPv6 under Windows.\n");
502 #endif
503 #endif
504 } else {
505 struct in_addr mc_if;
506 mc_if.s_addr = inet_addr(multicastif); /* ex: 192.168.x.x */
507 if(mc_if.s_addr != INADDR_NONE)
508 {
509 ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
510 if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
511 {
512 PRINT_SOCKET_ERROR("setsockopt");
513 }
514 } else {
515 #ifdef HAS_IP_MREQN
516 /* was not an ip address, try with an interface name */
517 struct ip_mreqn reqn; /* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */
518 memset(&reqn, 0, sizeof(struct ip_mreqn));
519 reqn.imr_ifindex = if_nametoindex(multicastif);
520 if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)
521 {
522 PRINT_SOCKET_ERROR("setsockopt");
523 }
524 #else
525 #ifdef DEBUG
526 printf("Setting of multicast interface not supported with interface name.\n");
527 #endif
528 #endif
529 }
530 }
531 }
532
533 /* Avant d'envoyer le paquet on bind pour recevoir la reponse */
534 if (bind(sudp, (const struct sockaddr *)&sockudp_r,
535 ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0)
536 {
537 if(error)
538 *error = UPNPDISCOVER_SOCKET_ERROR;
539 PRINT_SOCKET_ERROR("bind");
540 closesocket(sudp);
541 return NULL;
542 }
543
544 if(error)
545 *error = UPNPDISCOVER_SUCCESS;
546 /* Calculating maximum response time in seconds */
547 mx = ((unsigned int)delay) / 1000u;
548 /* receiving SSDP response packet */
549 for(n = 0; deviceList[deviceIndex]; deviceIndex++)
550 {
551 if(n == 0)
552 {
553 /* sending the SSDP M-SEARCH packet */
554 n = snprintf(bufr, sizeof(bufr),
555 MSearchMsgFmt,
556 ipv6 ?
557 (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")
558 : UPNP_MCAST_ADDR,
559 deviceList[deviceIndex], mx);
560 #ifdef DEBUG
561 printf("Sending %s", bufr);
562 #endif
563 #ifdef NO_GETADDRINFO
564 /* the following code is not using getaddrinfo */
565 /* emission */
566 memset(&sockudp_w, 0, sizeof(struct sockaddr_storage));
567 if(ipv6) {
568 struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;
569 p->sin6_family = AF_INET6;
570 p->sin6_port = htons(PORT);
571 inet_pton(AF_INET6,
572 linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,
573 &(p->sin6_addr));
574 } else {
575 struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;
576 p->sin_family = AF_INET;
577 p->sin_port = htons(PORT);
578 p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
579 }
580 n = sendto(sudp, bufr, n, 0,
581 &sockudp_w,
582 ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
583 if (n < 0) {
584 if(error)
585 *error = UPNPDISCOVER_SOCKET_ERROR;
586 PRINT_SOCKET_ERROR("sendto");
587 break;
588 }
589 #else /* #ifdef NO_GETADDRINFO */
590 memset(&hints, 0, sizeof(hints));
591 hints.ai_family = AF_UNSPEC; /* AF_INET6 or AF_INET */
592 hints.ai_socktype = SOCK_DGRAM;
593 /*hints.ai_flags = */
594 if ((rv = getaddrinfo(ipv6
595 ? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR)
596 : UPNP_MCAST_ADDR,
597 XSTR(PORT), &hints, &servinfo)) != 0) {
598 if(error)
599 *error = UPNPDISCOVER_SOCKET_ERROR;
600 #ifdef _WIN32
601 fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
602 #else
603 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
604 #endif
605 break;
606 }
607 for(p = servinfo; p; p = p->ai_next) {
608 n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen);
609 if (n < 0) {
610 #ifdef DEBUG
611 char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
612 if (getnameinfo(p->ai_addr, p->ai_addrlen, hbuf, sizeof(hbuf), sbuf,
613 sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
614 fprintf(stderr, "host:%s port:%s\n", hbuf, sbuf);
615 }
616 #endif
617 PRINT_SOCKET_ERROR("sendto");
618 continue;
619 }
620 }
621 freeaddrinfo(servinfo);
622 if(n < 0) {
623 if(error)
624 *error = UPNPDISCOVER_SOCKET_ERROR;
625 break;
626 }
627 #endif /* #ifdef NO_GETADDRINFO */
628 }
629 /* Waiting for SSDP REPLY packet to M-SEARCH */
630 n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);
631 if (n < 0) {
632 /* error */
633 if(error)
634 *error = UPNPDISCOVER_SOCKET_ERROR;
635 break;
636 } else if (n == 0) {
637 /* no data or Time Out */
638 if (devlist) {
639 /* no more device type to look for... */
640 if(error)
641 *error = UPNPDISCOVER_SUCCESS;
642 break;
643 }
644 if(ipv6) {
645 if(linklocal) {
646 linklocal = 0;
647 --deviceIndex;
648 } else {
649 linklocal = 1;
650 }
651 }
652 } else {
653 const char * descURL=NULL;
654 int urlsize=0;
655 const char * st=NULL;
656 int stsize=0;
657 /*printf("%d byte(s) :\n%s\n", n, bufr);*/ /* affichage du message */
658 parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize);
659 if(st&&descURL)
660 {
661 #ifdef DEBUG
662 printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
663 stsize, st, urlsize, descURL);
664 #endif
665 for(tmp=devlist; tmp; tmp = tmp->pNext) {
666 if(memcmp(tmp->descURL, descURL, urlsize) == 0 &&
667 tmp->descURL[urlsize] == '\0' &&
668 memcmp(tmp->st, st, stsize) == 0 &&
669 tmp->st[stsize] == '\0')
670 break;
671 }
672 /* at the exit of the loop above, tmp is null if
673 * no duplicate device was found */
674 if(tmp)
675 continue;
676 tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
677 if(!tmp) {
678 /* memory allocation error */
679 if(error)
680 *error = UPNPDISCOVER_MEMORY_ERROR;
681 break;
682 }
683 tmp->pNext = devlist;
684 tmp->descURL = tmp->buffer;
685 tmp->st = tmp->buffer + 1 + urlsize;
686 memcpy(tmp->buffer, descURL, urlsize);
687 tmp->buffer[urlsize] = '\0';
688 memcpy(tmp->buffer + urlsize + 1, st, stsize);
689 tmp->buffer[urlsize+1+stsize] = '\0';
690 tmp->scope_id = scope_id;
691 devlist = tmp;
692 }
693 }
694 }
695 closesocket(sudp);
696 return devlist;
697 }
698
699 /* freeUPNPDevlist() should be used to
700 * free the chained list returned by upnpDiscover() */
freeUPNPDevlist(struct UPNPDev * devlist)701 LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist)
702 {
703 struct UPNPDev * next;
704 while(devlist)
705 {
706 next = devlist->pNext;
707 free(devlist);
708 devlist = next;
709 }
710 }
711
712 static void
url_cpy_or_cat(char * dst,const char * src,int n)713 url_cpy_or_cat(char * dst, const char * src, int n)
714 {
715 if( (src[0] == 'h')
716 &&(src[1] == 't')
717 &&(src[2] == 't')
718 &&(src[3] == 'p')
719 &&(src[4] == ':')
720 &&(src[5] == '/')
721 &&(src[6] == '/'))
722 {
723 strncpy(dst, src, n);
724 }
725 else
726 {
727 int l = strlen(dst);
728 if(src[0] != '/')
729 dst[l++] = '/';
730 if(l<=n)
731 strncpy(dst + l, src, n - l);
732 }
733 }
734
735 /* Prepare the Urls for usage...
736 */
737 LIBSPEC void
GetUPNPUrls(struct UPNPUrls * urls,struct IGDdatas * data,const char * descURL,unsigned int scope_id)738 GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
739 const char * descURL, unsigned int scope_id)
740 {
741 char * p;
742 int n1, n2, n3, n4;
743 #ifdef IF_NAMESIZE
744 char ifname[IF_NAMESIZE];
745 #else
746 char scope_str[8];
747 #endif
748
749 n1 = strlen(data->urlbase);
750 if(n1==0)
751 n1 = strlen(descURL);
752 if(scope_id != 0) {
753 #ifdef IF_NAMESIZE
754 if(if_indextoname(scope_id, ifname)) {
755 n1 += 3 + strlen(ifname); /* 3 == strlen(%25) */
756 }
757 #else
758 /* under windows, scope is numerical */
759 snprintf(scope_str, sizeof(scope_str), "%u", scope_id);
760 #endif
761 }
762 n1 += 2; /* 1 byte more for Null terminator, 1 byte for '/' if needed */
763 n2 = n1; n3 = n1; n4 = n1;
764 n1 += strlen(data->first.scpdurl);
765 n2 += strlen(data->first.controlurl);
766 n3 += strlen(data->CIF.controlurl);
767 n4 += strlen(data->IPv6FC.controlurl);
768
769 /* allocate memory to store URLs */
770 urls->ipcondescURL = (char *)malloc(n1);
771 urls->controlURL = (char *)malloc(n2);
772 urls->controlURL_CIF = (char *)malloc(n3);
773 urls->controlURL_6FC = (char *)malloc(n4);
774
775 /* strdup descURL */
776 urls->rootdescURL = strdup(descURL);
777
778 /* get description of WANIPConnection */
779 if(data->urlbase[0] != '\0')
780 strncpy(urls->ipcondescURL, data->urlbase, n1);
781 else
782 strncpy(urls->ipcondescURL, descURL, n1);
783 p = strchr(urls->ipcondescURL+7, '/');
784 if(p) p[0] = '\0';
785 if(scope_id != 0) {
786 if(0 == memcmp(urls->ipcondescURL, "http://[fe80:", 13)) {
787 /* this is a linklocal IPv6 address */
788 p = strchr(urls->ipcondescURL, ']');
789 if(p) {
790 /* insert %25<scope> into URL */
791 #ifdef IF_NAMESIZE
792 memmove(p + 3 + strlen(ifname), p, strlen(p) + 1);
793 memcpy(p, "%25", 3);
794 memcpy(p + 3, ifname, strlen(ifname));
795 #else
796 memmove(p + 3 + strlen(scope_str), p, strlen(p) + 1);
797 memcpy(p, "%25", 3);
798 memcpy(p + 3, scope_str, strlen(scope_str));
799 #endif
800 }
801 }
802 }
803 strncpy(urls->controlURL, urls->ipcondescURL, n2);
804 strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3);
805 strncpy(urls->controlURL_6FC, urls->ipcondescURL, n4);
806
807 url_cpy_or_cat(urls->ipcondescURL, data->first.scpdurl, n1);
808
809 url_cpy_or_cat(urls->controlURL, data->first.controlurl, n2);
810
811 url_cpy_or_cat(urls->controlURL_CIF, data->CIF.controlurl, n3);
812
813 url_cpy_or_cat(urls->controlURL_6FC, data->IPv6FC.controlurl, n4);
814
815 #ifdef DEBUG
816 printf("urls->ipcondescURL='%s' %u n1=%d\n", urls->ipcondescURL,
817 (unsigned)strlen(urls->ipcondescURL), n1);
818 printf("urls->controlURL='%s' %u n2=%d\n", urls->controlURL,
819 (unsigned)strlen(urls->controlURL), n2);
820 printf("urls->controlURL_CIF='%s' %u n3=%d\n", urls->controlURL_CIF,
821 (unsigned)strlen(urls->controlURL_CIF), n3);
822 printf("urls->controlURL_6FC='%s' %u n4=%d\n", urls->controlURL_6FC,
823 (unsigned)strlen(urls->controlURL_6FC), n4);
824 #endif
825 }
826
827 LIBSPEC void
FreeUPNPUrls(struct UPNPUrls * urls)828 FreeUPNPUrls(struct UPNPUrls * urls)
829 {
830 if(!urls)
831 return;
832 free(urls->controlURL);
833 urls->controlURL = 0;
834 free(urls->ipcondescURL);
835 urls->ipcondescURL = 0;
836 free(urls->controlURL_CIF);
837 urls->controlURL_CIF = 0;
838 free(urls->controlURL_6FC);
839 urls->controlURL_6FC = 0;
840 free(urls->rootdescURL);
841 urls->rootdescURL = 0;
842 }
843
844 int
UPNPIGD_IsConnected(struct UPNPUrls * urls,struct IGDdatas * data)845 UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
846 {
847 char status[64];
848 unsigned int uptime;
849 status[0] = '\0';
850 UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
851 status, &uptime, NULL);
852 if(0 == strcmp("Connected", status))
853 {
854 return 1;
855 }
856 else
857 return 0;
858 }
859
860
861 /* UPNP_GetValidIGD() :
862 * return values :
863 * -1 = Internal error
864 * 0 = NO IGD found
865 * 1 = A valid connected IGD has been found
866 * 2 = A valid IGD has been found but it reported as
867 * not connected
868 * 3 = an UPnP device has been found but was not recognized as an IGD
869 *
870 * In any non zero return case, the urls and data structures
871 * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
872 * free allocated memory.
873 */
874 LIBSPEC int
UPNP_GetValidIGD(struct UPNPDev * devlist,struct UPNPUrls * urls,struct IGDdatas * data,char * lanaddr,int lanaddrlen)875 UPNP_GetValidIGD(struct UPNPDev * devlist,
876 struct UPNPUrls * urls,
877 struct IGDdatas * data,
878 char * lanaddr, int lanaddrlen)
879 {
880 struct xml_desc {
881 char * xml;
882 int size;
883 } * desc = NULL;
884 struct UPNPDev * dev;
885 int ndev = 0;
886 int i;
887 int state = -1; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
888 if(!devlist)
889 {
890 #ifdef DEBUG
891 printf("Empty devlist\n");
892 #endif
893 return 0;
894 }
895 for(dev = devlist; dev; dev = dev->pNext)
896 ndev++;
897 if(ndev > 0)
898 {
899 desc = calloc(ndev, sizeof(struct xml_desc));
900 if(!desc)
901 return -1; /* memory allocation error */
902 }
903 for(state = 1; state <= 3; state++)
904 {
905 for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
906 {
907 /* we should choose an internet gateway device.
908 * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
909 if(state == 1)
910 {
911 desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size),
912 lanaddr, lanaddrlen,
913 dev->scope_id);
914 #ifdef DEBUG
915 if(!desc[i].xml)
916 {
917 printf("error getting XML description %s\n", dev->descURL);
918 }
919 #endif
920 }
921 if(desc[i].xml)
922 {
923 memset(data, 0, sizeof(struct IGDdatas));
924 memset(urls, 0, sizeof(struct UPNPUrls));
925 parserootdesc(desc[i].xml, desc[i].size, data);
926 if(0==strcmp(data->CIF.servicetype,
927 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")
928 || state >= 3 )
929 {
930 GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
931
932 #ifdef DEBUG
933 printf("UPNPIGD_IsConnected(%s) = %d\n",
934 urls->controlURL,
935 UPNPIGD_IsConnected(urls, data));
936 #endif
937 if((state >= 2) || UPNPIGD_IsConnected(urls, data))
938 goto free_and_return;
939 FreeUPNPUrls(urls);
940 if(data->second.servicetype[0] != '\0') {
941 #ifdef DEBUG
942 printf("We tried %s, now we try %s !\n",
943 data->first.servicetype, data->second.servicetype);
944 #endif
945 /* swaping WANPPPConnection and WANIPConnection ! */
946 memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
947 memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
948 memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
949 GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
950 #ifdef DEBUG
951 printf("UPNPIGD_IsConnected(%s) = %d\n",
952 urls->controlURL,
953 UPNPIGD_IsConnected(urls, data));
954 #endif
955 if((state >= 2) || UPNPIGD_IsConnected(urls, data))
956 goto free_and_return;
957 FreeUPNPUrls(urls);
958 }
959 }
960 memset(data, 0, sizeof(struct IGDdatas));
961 }
962 }
963 }
964 state = 0;
965 free_and_return:
966 if(desc) {
967 for(i = 0; i < ndev; i++) {
968 if(desc[i].xml) {
969 free(desc[i].xml);
970 }
971 }
972 free(desc);
973 }
974 return state;
975 }
976
977 /* UPNP_GetIGDFromUrl()
978 * Used when skipping the discovery process.
979 * return value :
980 * 0 - Not ok
981 * 1 - OK */
982 int
UPNP_GetIGDFromUrl(const char * rootdescurl,struct UPNPUrls * urls,struct IGDdatas * data,char * lanaddr,int lanaddrlen)983 UPNP_GetIGDFromUrl(const char * rootdescurl,
984 struct UPNPUrls * urls,
985 struct IGDdatas * data,
986 char * lanaddr, int lanaddrlen)
987 {
988 char * descXML;
989 int descXMLsize = 0;
990 descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
991 lanaddr, lanaddrlen, 0);
992 if(descXML) {
993 memset(data, 0, sizeof(struct IGDdatas));
994 memset(urls, 0, sizeof(struct UPNPUrls));
995 parserootdesc(descXML, descXMLsize, data);
996 free(descXML);
997 descXML = NULL;
998 GetUPNPUrls(urls, data, rootdescurl, 0);
999 return 1;
1000 } else {
1001 return 0;
1002 }
1003 }
1004
1005