1 /* $Id: miniupnpc.c,v 1.117 2014/01/31 14:19:13 nanard Exp $ */
3 * Web : http://miniupnp.free.fr/
4 * Author : Thomas BERNARD
5 * copyright (c) 2005-2014 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__)
12 #define _XOPEN_SOURCE 600
16 #define __BSD_VISIBLE 1
20 #if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(MACOSX) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun)
28 /* Win32 Specific includes and defines */
33 #define snprintf _snprintf
34 #define strdup _strdup
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 */
46 #if defined(__amigaos__) && !defined(__amigaos4__)
47 /* Amiga OS 3 specific stuff */
50 #include <sys/select.h>
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>
59 #if !defined(__amigaos__) && !defined(__amigaos4__)
64 #define closesocket close
65 #endif /* #else _WIN32 */
66 #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
69 #if defined(__amigaos__) || defined(__amigaos4__)
70 /* Amiga OS specific stuff */
71 #define TIMEVAL struct timeval
75 #if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
76 /* Several versions of glibc don't define this structure, define it here and compile with CFLAGS NEED_STRUCT_IP_MREQN */
79 struct in_addr imr_multiaddr
; /* IP multicast address of group */
80 struct in_addr imr_address
; /* local IP address of interface */
81 int imr_ifindex
; /* Interface index */
85 #include "miniupnpc.h"
86 #include "minissdpc.h"
90 #include "upnpcommands.h"
91 #include "connecthostport.h"
92 #include "receivedata.h"
95 #define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
97 #define PRINT_SOCKET_ERROR(x) perror(x)
100 #ifndef MAXHOSTNAMELEN
101 #define MAXHOSTNAMELEN 64
104 #define SOAPPREFIX "s"
105 #define SERVICEPREFIX "u"
106 #define SERVICEPREFIX2 'u'
108 /* root description parsing */
109 LIBSPEC
void parserootdesc(const char * buffer
, int bufsize
, struct IGDdatas
* data
)
111 struct xmlparser parser
;
112 /* xmlparser object */
113 parser
.xmlstart
= buffer
;
114 parser
.xmlsize
= bufsize
;
116 parser
.starteltfunc
= IGDstartelt
;
117 parser
.endeltfunc
= IGDendelt
;
118 parser
.datafunc
= IGDdata
;
126 /* simpleUPnPcommand2 :
131 static char * simpleUPnPcommand2(int s
, const char * url
, const char * service
,
132 const char * action
, struct UPNParg
* args
,
133 int * bufsize
, const char * httpversion
)
135 char hostname
[MAXHOSTNAMELEN
+1];
136 unsigned short port
= 0;
144 snprintf(soapact
, sizeof(soapact
), "%s#%s", service
, action
);
147 /*soapbodylen = */snprintf(soapbody
, sizeof(soapbody
),
148 "<?xml version=\"1.0\"?>\r\n"
149 "<" SOAPPREFIX
":Envelope "
150 "xmlns:" SOAPPREFIX
"=\"http://schemas.xmlsoap.org/soap/envelope/\" "
151 SOAPPREFIX
":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
152 "<" SOAPPREFIX
":Body>"
153 "<" SERVICEPREFIX
":%s xmlns:" SERVICEPREFIX
"=\"%s\">"
154 "</" SERVICEPREFIX
":%s>"
155 "</" SOAPPREFIX
":Body></" SOAPPREFIX
":Envelope>"
156 "\r\n", action
, service
, action
);
161 const char * pe
, * pv
;
163 soapbodylen
= snprintf(soapbody
, sizeof(soapbody
),
164 "<?xml version=\"1.0\"?>\r\n"
165 "<" SOAPPREFIX
":Envelope "
166 "xmlns:" SOAPPREFIX
"=\"http://schemas.xmlsoap.org/soap/envelope/\" "
167 SOAPPREFIX
":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
168 "<" SOAPPREFIX
":Body>"
169 "<" SERVICEPREFIX
":%s xmlns:" SERVICEPREFIX
"=\"%s\">",
171 p
= soapbody
+ soapbodylen
;
174 /* check that we are never overflowing the string... */
175 if(soapbody
+ sizeof(soapbody
) <= p
+ 100)
177 /* we keep a margin of at least 100 bytes */
200 *(p
++) = SERVICEPREFIX2
;
205 strncpy(p
, "></" SOAPPREFIX
":Body></" SOAPPREFIX
":Envelope>\r\n",
206 soapbody
+ sizeof(soapbody
) - p
);
208 if(!parseURL(url
, hostname
, &port
, &path
, NULL
)) return NULL
;
210 s
= connecthostport(hostname
, port
, 0);
212 /* failed to connect */
217 n
= soapPostSubmit(s
, path
, hostname
, port
, soapact
, soapbody
, httpversion
);
220 printf("Error sending SOAP request\n");
226 buf
= getHTTPResponse(s
, bufsize
);
228 if(*bufsize
> 0 && buf
)
230 printf("SOAP Response :\n%.*s\n", *bufsize
, buf
);
237 /* simpleUPnPcommand :
242 char * simpleUPnPcommand(int s
, const char * url
, const char * service
,
243 const char * action
, struct UPNParg
* args
,
249 buf
= simpleUPnPcommand2(s
, url
, service
, action
, args
, bufsize
, "1.1");
251 buf
= simpleUPnPcommand2(s
, url
, service
, action
, args
, bufsize
, "1.0");
252 if (!buf
|| *bufsize
== 0)
255 printf("Error or no result from SOAP request; retrying with HTTP/1.1\n");
257 buf
= simpleUPnPcommand2(s
, url
, service
, action
, args
, bufsize
, "1.1");
263 /* parseMSEARCHReply()
264 * the last 4 arguments are filled during the parsing :
265 * - location/locationsize : "location:" field of the SSDP reply packet
266 * - st/stsize : "st:" field of the SSDP reply packet.
267 * The strings are NOT null terminated */
269 parseMSEARCHReply(const char * reply
, int size
,
270 const char * * location
, int * locationsize
,
271 const char * * st
, int * stsize
)
275 a
= i
; /* start of the line */
276 b
= 0; /* end of the "header" (position of the colon) */
284 b
= i
; /* end of the "header" */
296 /*for(j=b+1; j<i; j++)
301 /* skip the colon and white spaces */
302 do { b
++; } while(reply
[b
]==' ');
303 if(0==strncasecmp(reply
+a
, "location", 8))
308 else if(0==strncasecmp(reply
+a
, "st", 2))
324 /* port upnp discover : SSDP protocol */
326 #define XSTR(s) STR(s)
328 #define UPNP_MCAST_ADDR "239.255.255.250"
330 #define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */
331 #define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */
334 * return a chained list of all devices found or NULL if
335 * no devices was found.
336 * It is up to the caller to free the chained list
337 * delay is in millisecond (poll) */
338 LIBSPEC
struct UPNPDev
*
339 upnpDiscover(int delay
, const char * multicastif
,
340 const char * minissdpdsock
, int sameport
,
344 struct UPNPDev
* tmp
;
345 struct UPNPDev
* devlist
= 0;
346 unsigned int scope_id
= 0;
348 static const char MSearchMsgFmt
[] =
349 "M-SEARCH * HTTP/1.1\r\n"
350 "HOST: %s:" XSTR(PORT
) "\r\n"
352 "MAN: \"ssdp:discover\"\r\n"
355 static const char * const deviceList
[] = {
357 "urn:schemas-upnp-org:device:InternetGatewayDevice:2",
358 "urn:schemas-upnp-org:service:WANIPConnection:2",
360 "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
361 "urn:schemas-upnp-org:service:WANIPConnection:1",
362 "urn:schemas-upnp-org:service:WANPPPConnection:1",
367 char bufr
[1536]; /* reception and emission buffer */
370 struct sockaddr_storage sockudp_r
;
372 #ifdef NO_GETADDRINFO
373 struct sockaddr_storage sockudp_w
;
376 struct addrinfo hints
, *servinfo
, *p
;
379 MIB_IPFORWARDROW ip_forward
;
384 *error
= UPNPDISCOVER_UNKNOWN_ERROR
;
385 #if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
386 /* first try to get infos from minissdpd ! */
388 minissdpdsock
= "/var/run/minissdpd.sock";
389 while(!devlist
&& deviceList
[deviceIndex
]) {
390 devlist
= getDevicesFromMiniSSDPD(deviceList
[deviceIndex
],
392 /* We return what we have found if it was not only a rootdevice */
393 if(devlist
&& !strstr(deviceList
[deviceIndex
], "rootdevice")) {
395 *error
= UPNPDISCOVER_SUCCESS
;
402 /* fallback to direct discovery */
404 sudp
= socket(ipv6
? PF_INET6
: PF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
406 sudp
= socket(ipv6
? PF_INET6
: PF_INET
, SOCK_DGRAM
, 0);
411 *error
= UPNPDISCOVER_SOCKET_ERROR
;
412 PRINT_SOCKET_ERROR("socket");
416 memset(&sockudp_r
, 0, sizeof(struct sockaddr_storage
));
418 struct sockaddr_in6
* p
= (struct sockaddr_in6
*)&sockudp_r
;
419 p
->sin6_family
= AF_INET6
;
421 p
->sin6_port
= htons(PORT
);
422 p
->sin6_addr
= in6addr_any
; /* in6addr_any is not available with MinGW32 3.4.2 */
424 struct sockaddr_in
* p
= (struct sockaddr_in
*)&sockudp_r
;
425 p
->sin_family
= AF_INET
;
427 p
->sin_port
= htons(PORT
);
428 p
->sin_addr
.s_addr
= INADDR_ANY
;
431 /* This code could help us to use the right Network interface for
432 * SSDP multicast traffic */
433 /* Get IP associated with the index given in the ip_forward struct
434 * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
436 && (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward
) == NO_ERROR
)) {
438 PMIB_IPADDRTABLE pIPAddrTable
;
445 printf("ifIndex=%lu nextHop=%lx \n", ip_forward
.dwForwardIfIndex
, ip_forward
.dwForwardNextHop
);
447 pIPAddrTable
= (MIB_IPADDRTABLE
*) malloc(sizeof (MIB_IPADDRTABLE
));
448 if (GetIpAddrTable(pIPAddrTable
, &dwSize
, 0) == ERROR_INSUFFICIENT_BUFFER
) {
450 pIPAddrTable
= (MIB_IPADDRTABLE
*) malloc(dwSize
);
453 dwRetVal
= GetIpAddrTable( pIPAddrTable
, &dwSize
, 0 );
455 printf("\tNum Entries: %ld\n", pIPAddrTable
->dwNumEntries
);
457 for (i
=0; i
< (int) pIPAddrTable
->dwNumEntries
; i
++) {
459 printf("\n\tInterface Index[%d]:\t%ld\n", i
, pIPAddrTable
->table
[i
].dwIndex
);
460 IPAddr
.S_un
.S_addr
= (u_long
) pIPAddrTable
->table
[i
].dwAddr
;
461 printf("\tIP Address[%d]: \t%s\n", i
, inet_ntoa(IPAddr
) );
462 IPAddr
.S_un
.S_addr
= (u_long
) pIPAddrTable
->table
[i
].dwMask
;
463 printf("\tSubnet Mask[%d]: \t%s\n", i
, inet_ntoa(IPAddr
) );
464 IPAddr
.S_un
.S_addr
= (u_long
) pIPAddrTable
->table
[i
].dwBCastAddr
;
465 printf("\tBroadCast[%d]: \t%s (%ld)\n", i
, inet_ntoa(IPAddr
), pIPAddrTable
->table
[i
].dwBCastAddr
);
466 printf("\tReassembly size[%d]:\t%ld\n", i
, pIPAddrTable
->table
[i
].dwReasmSize
);
467 printf("\tType and State[%d]:", i
);
470 if (pIPAddrTable
->table
[i
].dwIndex
== ip_forward
.dwForwardIfIndex
) {
471 /* Set the address of this interface to be used */
472 struct in_addr mc_if
;
473 memset(&mc_if
, 0, sizeof(mc_if
));
474 mc_if
.s_addr
= pIPAddrTable
->table
[i
].dwAddr
;
475 if(setsockopt(sudp
, IPPROTO_IP
, IP_MULTICAST_IF
, (const char *)&mc_if
, sizeof(mc_if
)) < 0) {
476 PRINT_SOCKET_ERROR("setsockopt");
478 ((struct sockaddr_in
*)&sockudp_r
)->sin_addr
.s_addr
= pIPAddrTable
->table
[i
].dwAddr
;
491 if (setsockopt(sudp
, SOL_SOCKET
, SO_REUSEADDR
, (const char *)&opt
, sizeof (opt
)) < 0)
493 if (setsockopt(sudp
, SOL_SOCKET
, SO_REUSEADDR
, &opt
, sizeof (opt
)) < 0)
497 *error
= UPNPDISCOVER_SOCKET_ERROR
;
498 PRINT_SOCKET_ERROR("setsockopt");
506 /* according to MSDN, if_nametoindex() is supported since
507 * MS Windows Vista and MS Windows Server 2008.
508 * http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */
509 unsigned int ifindex
= if_nametoindex(multicastif
); /* eth0, etc. */
510 if(setsockopt(sudp
, IPPROTO_IPV6
, IPV6_MULTICAST_IF
, &ifindex
, sizeof(&ifindex
)) < 0)
512 PRINT_SOCKET_ERROR("setsockopt");
516 printf("Setting of multicast interface not supported in IPv6 under Windows.\n");
520 struct in_addr mc_if
;
521 mc_if
.s_addr
= inet_addr(multicastif
); /* ex: 192.168.x.x */
522 if(mc_if
.s_addr
!= INADDR_NONE
)
524 ((struct sockaddr_in
*)&sockudp_r
)->sin_addr
.s_addr
= mc_if
.s_addr
;
525 if(setsockopt(sudp
, IPPROTO_IP
, IP_MULTICAST_IF
, (const char *)&mc_if
, sizeof(mc_if
)) < 0)
527 PRINT_SOCKET_ERROR("setsockopt");
531 /* was not an ip address, try with an interface name */
532 struct ip_mreqn reqn
; /* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */
533 memset(&reqn
, 0, sizeof(struct ip_mreqn
));
534 reqn
.imr_ifindex
= if_nametoindex(multicastif
);
535 if(setsockopt(sudp
, IPPROTO_IP
, IP_MULTICAST_IF
, (const char *)&reqn
, sizeof(reqn
)) < 0)
537 PRINT_SOCKET_ERROR("setsockopt");
541 printf("Setting of multicast interface not supported with interface name.\n");
548 /* Before sending the packed, we first "bind" in order to be able
549 * to receive the response */
550 if (bind(sudp
, (const struct sockaddr
*)&sockudp_r
,
551 ipv6
? sizeof(struct sockaddr_in6
) : sizeof(struct sockaddr_in
)) != 0)
554 *error
= UPNPDISCOVER_SOCKET_ERROR
;
555 PRINT_SOCKET_ERROR("bind");
561 *error
= UPNPDISCOVER_SUCCESS
;
562 /* Calculating maximum response time in seconds */
563 mx
= ((unsigned int)delay
) / 1000u;
568 /* receiving SSDP response packet */
569 for(n
= 0; deviceList
[deviceIndex
]; deviceIndex
++)
573 /* sending the SSDP M-SEARCH packet */
574 n
= snprintf(bufr
, sizeof(bufr
),
577 (linklocal
? "[" UPNP_MCAST_LL_ADDR
"]" : "[" UPNP_MCAST_SL_ADDR
"]")
579 deviceList
[deviceIndex
], mx
);
581 printf("Sending %s", bufr
);
583 #ifdef NO_GETADDRINFO
584 /* the following code is not using getaddrinfo */
586 memset(&sockudp_w
, 0, sizeof(struct sockaddr_storage
));
588 struct sockaddr_in6
* p
= (struct sockaddr_in6
*)&sockudp_w
;
589 p
->sin6_family
= AF_INET6
;
590 p
->sin6_port
= htons(PORT
);
592 linklocal
? UPNP_MCAST_LL_ADDR
: UPNP_MCAST_SL_ADDR
,
595 struct sockaddr_in
* p
= (struct sockaddr_in
*)&sockudp_w
;
596 p
->sin_family
= AF_INET
;
597 p
->sin_port
= htons(PORT
);
598 p
->sin_addr
.s_addr
= inet_addr(UPNP_MCAST_ADDR
);
600 n
= sendto(sudp
, bufr
, n
, 0,
602 ipv6
? sizeof(struct sockaddr_in6
) : sizeof(struct sockaddr_in
));
605 *error
= UPNPDISCOVER_SOCKET_ERROR
;
606 PRINT_SOCKET_ERROR("sendto");
609 #else /* #ifdef NO_GETADDRINFO */
610 memset(&hints
, 0, sizeof(hints
));
611 hints
.ai_family
= AF_UNSPEC
; /* AF_INET6 or AF_INET */
612 hints
.ai_socktype
= SOCK_DGRAM
;
613 /*hints.ai_flags = */
614 if ((rv
= getaddrinfo(ipv6
615 ? (linklocal
? UPNP_MCAST_LL_ADDR
: UPNP_MCAST_SL_ADDR
)
617 XSTR(PORT
), &hints
, &servinfo
)) != 0) {
619 *error
= UPNPDISCOVER_SOCKET_ERROR
;
621 fprintf(stderr
, "getaddrinfo() failed: %d\n", rv
);
623 fprintf(stderr
, "getaddrinfo: %s\n", gai_strerror(rv
));
627 for(p
= servinfo
; p
; p
= p
->ai_next
) {
628 n
= sendto(sudp
, bufr
, n
, 0, p
->ai_addr
, p
->ai_addrlen
);
631 char hbuf
[NI_MAXHOST
], sbuf
[NI_MAXSERV
];
632 if (getnameinfo(p
->ai_addr
, p
->ai_addrlen
, hbuf
, sizeof(hbuf
), sbuf
,
633 sizeof(sbuf
), NI_NUMERICHOST
| NI_NUMERICSERV
) == 0) {
634 fprintf(stderr
, "host:%s port:%s\n", hbuf
, sbuf
);
637 PRINT_SOCKET_ERROR("sendto");
641 freeaddrinfo(servinfo
);
644 *error
= UPNPDISCOVER_SOCKET_ERROR
;
647 #endif /* #ifdef NO_GETADDRINFO */
649 /* Waiting for SSDP REPLY packet to M-SEARCH */
650 n
= receivedata(sudp
, bufr
, sizeof(bufr
), delay
, &scope_id
);
654 *error
= UPNPDISCOVER_SOCKET_ERROR
;
657 /* no data or Time Out */
659 /* no more device type to look for... */
661 *error
= UPNPDISCOVER_SUCCESS
;
673 const char * descURL
=NULL
;
675 const char * st
=NULL
;
677 /*printf("%d byte(s) :\n%s\n", n, bufr);*/ /* affichage du message */
678 parseMSEARCHReply(bufr
, n
, &descURL
, &urlsize
, &st
, &stsize
);
682 printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
683 stsize
, st
, urlsize
, descURL
);
685 for(tmp
=devlist
; tmp
; tmp
= tmp
->pNext
) {
686 if(memcmp(tmp
->descURL
, descURL
, urlsize
) == 0 &&
687 tmp
->descURL
[urlsize
] == '\0' &&
688 memcmp(tmp
->st
, st
, stsize
) == 0 &&
689 tmp
->st
[stsize
] == '\0')
692 /* at the exit of the loop above, tmp is null if
693 * no duplicate device was found */
696 tmp
= (struct UPNPDev
*)malloc(sizeof(struct UPNPDev
)+urlsize
+stsize
);
698 /* memory allocation error */
700 *error
= UPNPDISCOVER_MEMORY_ERROR
;
703 tmp
->pNext
= devlist
;
704 tmp
->descURL
= tmp
->buffer
;
705 tmp
->st
= tmp
->buffer
+ 1 + urlsize
;
706 memcpy(tmp
->buffer
, descURL
, urlsize
);
707 tmp
->buffer
[urlsize
] = '\0';
708 memcpy(tmp
->buffer
+ urlsize
+ 1, st
, stsize
);
709 tmp
->buffer
[urlsize
+1+stsize
] = '\0';
710 tmp
->scope_id
= scope_id
;
719 /* freeUPNPDevlist() should be used to
720 * free the chained list returned by upnpDiscover() */
721 LIBSPEC
void freeUPNPDevlist(struct UPNPDev
* devlist
)
723 struct UPNPDev
* next
;
726 next
= devlist
->pNext
;
733 url_cpy_or_cat(char * dst
, const char * src
, int n
)
743 strncpy(dst
, src
, n
);
751 strncpy(dst
+ l
, src
, n
- l
);
755 /* Prepare the Urls for usage...
758 GetUPNPUrls(struct UPNPUrls
* urls
, struct IGDdatas
* data
,
759 const char * descURL
, unsigned int scope_id
)
764 char ifname
[IF_NAMESIZE
];
769 n1
= strlen(data
->urlbase
);
771 n1
= strlen(descURL
);
774 if(if_indextoname(scope_id
, ifname
)) {
775 n1
+= 3 + strlen(ifname
); /* 3 == strlen(%25) */
778 /* under windows, scope is numerical */
779 snprintf(scope_str
, sizeof(scope_str
), "%u", scope_id
);
782 n1
+= 2; /* 1 byte more for Null terminator, 1 byte for '/' if needed */
783 n2
= n1
; n3
= n1
; n4
= n1
;
784 n1
+= strlen(data
->first
.scpdurl
);
785 n2
+= strlen(data
->first
.controlurl
);
786 n3
+= strlen(data
->CIF
.controlurl
);
787 n4
+= strlen(data
->IPv6FC
.controlurl
);
789 /* allocate memory to store URLs */
790 urls
->ipcondescURL
= (char *)malloc(n1
);
791 urls
->controlURL
= (char *)malloc(n2
);
792 urls
->controlURL_CIF
= (char *)malloc(n3
);
793 urls
->controlURL_6FC
= (char *)malloc(n4
);
796 urls
->rootdescURL
= strdup(descURL
);
798 /* get description of WANIPConnection */
799 if(data
->urlbase
[0] != '\0')
800 strncpy(urls
->ipcondescURL
, data
->urlbase
, n1
);
802 strncpy(urls
->ipcondescURL
, descURL
, n1
);
803 p
= strchr(urls
->ipcondescURL
+7, '/');
806 if(0 == memcmp(urls
->ipcondescURL
, "http://[fe80:", 13)) {
807 /* this is a linklocal IPv6 address */
808 p
= strchr(urls
->ipcondescURL
, ']');
810 /* insert %25<scope> into URL */
812 memmove(p
+ 3 + strlen(ifname
), p
, strlen(p
) + 1);
814 memcpy(p
+ 3, ifname
, strlen(ifname
));
816 memmove(p
+ 3 + strlen(scope_str
), p
, strlen(p
) + 1);
818 memcpy(p
+ 3, scope_str
, strlen(scope_str
));
823 strncpy(urls
->controlURL
, urls
->ipcondescURL
, n2
);
824 strncpy(urls
->controlURL_CIF
, urls
->ipcondescURL
, n3
);
825 strncpy(urls
->controlURL_6FC
, urls
->ipcondescURL
, n4
);
827 url_cpy_or_cat(urls
->ipcondescURL
, data
->first
.scpdurl
, n1
);
829 url_cpy_or_cat(urls
->controlURL
, data
->first
.controlurl
, n2
);
831 url_cpy_or_cat(urls
->controlURL_CIF
, data
->CIF
.controlurl
, n3
);
833 url_cpy_or_cat(urls
->controlURL_6FC
, data
->IPv6FC
.controlurl
, n4
);
836 printf("urls->ipcondescURL='%s' %u n1=%d\n", urls
->ipcondescURL
,
837 (unsigned)strlen(urls
->ipcondescURL
), n1
);
838 printf("urls->controlURL='%s' %u n2=%d\n", urls
->controlURL
,
839 (unsigned)strlen(urls
->controlURL
), n2
);
840 printf("urls->controlURL_CIF='%s' %u n3=%d\n", urls
->controlURL_CIF
,
841 (unsigned)strlen(urls
->controlURL_CIF
), n3
);
842 printf("urls->controlURL_6FC='%s' %u n4=%d\n", urls
->controlURL_6FC
,
843 (unsigned)strlen(urls
->controlURL_6FC
), n4
);
848 FreeUPNPUrls(struct UPNPUrls
* urls
)
852 free(urls
->controlURL
);
853 urls
->controlURL
= 0;
854 free(urls
->ipcondescURL
);
855 urls
->ipcondescURL
= 0;
856 free(urls
->controlURL_CIF
);
857 urls
->controlURL_CIF
= 0;
858 free(urls
->controlURL_6FC
);
859 urls
->controlURL_6FC
= 0;
860 free(urls
->rootdescURL
);
861 urls
->rootdescURL
= 0;
865 UPNPIGD_IsConnected(struct UPNPUrls
* urls
, struct IGDdatas
* data
)
870 UPNP_GetStatusInfo(urls
->controlURL
, data
->first
.servicetype
,
871 status
, &uptime
, NULL
);
872 if(0 == strcmp("Connected", status
))
881 /* UPNP_GetValidIGD() :
883 * -1 = Internal error
885 * 1 = A valid connected IGD has been found
886 * 2 = A valid IGD has been found but it reported as
888 * 3 = an UPnP device has been found but was not recognized as an IGD
890 * In any positive non zero return case, the urls and data structures
891 * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
892 * free allocated memory.
895 UPNP_GetValidIGD(struct UPNPDev
* devlist
,
896 struct UPNPUrls
* urls
,
897 struct IGDdatas
* data
,
898 char * lanaddr
, int lanaddrlen
)
905 struct UPNPDev
* dev
;
908 int state
= -1; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
914 printf("Empty devlist\n");
918 /* counting total number of devices in the list */
919 for(dev
= devlist
; dev
; dev
= dev
->pNext
)
923 desc
= calloc(ndev
, sizeof(struct xml_desc
));
925 return -1; /* memory allocation error */
927 /* Step 1 : downloading descriptions and testing type */
928 for(dev
= devlist
, i
= 0; dev
; dev
= dev
->pNext
, i
++)
930 /* we should choose an internet gateway device.
931 * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
932 desc
[i
].xml
= miniwget_getaddr(dev
->descURL
, &(desc
[i
].size
),
938 printf("error getting XML description %s\n", dev
->descURL
);
943 memset(data
, 0, sizeof(struct IGDdatas
));
944 memset(urls
, 0, sizeof(struct UPNPUrls
));
945 parserootdesc(desc
[i
].xml
, desc
[i
].size
, data
);
946 if(0==strcmp(data
->CIF
.servicetype
,
947 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"))
954 /* iterate the list to find a device depending on state */
955 for(state
= 1; state
<= 3; state
++)
957 for(dev
= devlist
, i
= 0; dev
; dev
= dev
->pNext
, i
++)
961 memset(data
, 0, sizeof(struct IGDdatas
));
962 memset(urls
, 0, sizeof(struct UPNPUrls
));
963 parserootdesc(desc
[i
].xml
, desc
[i
].size
, data
);
964 if(desc
[i
].is_igd
|| state
>= 3 )
966 GetUPNPUrls(urls
, data
, dev
->descURL
, dev
->scope_id
);
968 /* in state 2 and 3 we dont test if device is connected ! */
970 goto free_and_return
;
972 printf("UPNPIGD_IsConnected(%s) = %d\n",
974 UPNPIGD_IsConnected(urls
, data
));
976 /* checks that status is connected AND there is a external IP address assigned */
977 if(UPNPIGD_IsConnected(urls
, data
)
978 && (UPNP_GetExternalIPAddress(urls
->controlURL
, data
->first
.servicetype
, extIpAddr
) == 0))
979 goto free_and_return
;
981 if(data
->second
.servicetype
[0] != '\0') {
983 printf("We tried %s, now we try %s !\n",
984 data
->first
.servicetype
, data
->second
.servicetype
);
986 /* swaping WANPPPConnection and WANIPConnection ! */
987 memcpy(&data
->tmp
, &data
->first
, sizeof(struct IGDdatas_service
));
988 memcpy(&data
->first
, &data
->second
, sizeof(struct IGDdatas_service
));
989 memcpy(&data
->second
, &data
->tmp
, sizeof(struct IGDdatas_service
));
990 GetUPNPUrls(urls
, data
, dev
->descURL
, dev
->scope_id
);
992 printf("UPNPIGD_IsConnected(%s) = %d\n",
994 UPNPIGD_IsConnected(urls
, data
));
996 if(UPNPIGD_IsConnected(urls
, data
)
997 && (UPNP_GetExternalIPAddress(urls
->controlURL
, data
->first
.servicetype
, extIpAddr
) == 0))
998 goto free_and_return
;
1002 memset(data
, 0, sizeof(struct IGDdatas
));
1009 for(i
= 0; i
< ndev
; i
++) {
1019 /* UPNP_GetIGDFromUrl()
1020 * Used when skipping the discovery process.
1025 UPNP_GetIGDFromUrl(const char * rootdescurl
,
1026 struct UPNPUrls
* urls
,
1027 struct IGDdatas
* data
,
1028 char * lanaddr
, int lanaddrlen
)
1031 int descXMLsize
= 0;
1032 descXML
= miniwget_getaddr(rootdescurl
, &descXMLsize
,
1033 lanaddr
, lanaddrlen
, 0);
1035 memset(data
, 0, sizeof(struct IGDdatas
));
1036 memset(urls
, 0, sizeof(struct UPNPUrls
));
1037 parserootdesc(descXML
, descXMLsize
, data
);
1040 GetUPNPUrls(urls
, data
, rootdescurl
, 0);