]>
git.ipfire.org Git - thirdparty/freeswitch.git/blob - libs/miniupnpc/miniupnpc.c
1 /* $Id: miniupnpc.c,v 1.57 2008/12/18 17:46:36 nanard Exp $ */
3 * Author : Thomas BERNARD
4 * copyright (c) 2005-2007 Thomas Bernard
5 * This software is subjet to the conditions detailed in the
6 * provided LICENCE file. */
7 #define __EXTENSIONS__ 1
10 #if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__) && !defined(__NetBSD__)
12 #define _XOPEN_SOURCE 600
16 #define __BSD_VISIBLE 1
23 /* Win32 Specific includes and defines */
28 #define snprintf _snprintf
30 #if defined(_MSC_VER) && (_MSC_VER >= 1400)
31 #define strncasecmp _memicmp
33 #define strncasecmp memicmp
35 #define MAXHOSTNAMELEN 64
37 /* Standard POSIX includes */
39 #include <sys/socket.h>
40 #include <sys/types.h>
41 #include <sys/param.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
47 #define closesocket close
49 #include "miniupnpc.h"
50 #include "minissdpc.h"
54 #include "upnpcommands.h"
57 #define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
59 #define PRINT_SOCKET_ERROR(x) perror(x)
62 #define SOAPPREFIX "s"
63 #define SERVICEPREFIX "u"
64 #define SERVICEPREFIX2 'u'
66 /* root description parsing */
67 void parserootdesc(const char * buffer
, int bufsize
, struct IGDdatas
* data
)
69 struct xmlparser parser
;
70 /* xmlparser object */
71 parser
.xmlstart
= buffer
;
72 parser
.xmlsize
= bufsize
;
74 parser
.starteltfunc
= IGDstartelt
;
75 parser
.endeltfunc
= IGDendelt
;
76 parser
.datafunc
= IGDdata
;
84 /* Content-length: nnn */
85 static int getcontentlenfromline(const char * p
, int n
)
87 static const char contlenstr
[] = "content-length";
88 const char * p2
= contlenstr
;
94 if(*p2
!= *p
&& *p2
!= (*p
+ 32))
109 while(*p
>= '0' && *p
<= '9')
113 a
= (a
* 10) + (*p
- '0');
120 getContentLengthAndHeaderLength(char * p
, int n
,
121 int * contentlen
, int * headerlen
)
130 while(line
[linelen
] != '\r' && line
[linelen
] != '\r')
132 if(line
+linelen
>= p
+n
)
136 r
= getcontentlenfromline(line
, linelen
);
139 line
= line
+ linelen
+ 2;
140 if(line
[0] == '\r' && line
[1] == '\n')
142 *headerlen
= (line
- p
) + 2;
148 /* simpleUPnPcommand :
153 int simpleUPnPcommand(int s
, const char * url
, const char * service
,
154 const char * action
, struct UPNParg
* args
,
155 char * buffer
, int * bufsize
)
157 struct sockaddr_in dest
;
158 char hostname
[MAXHOSTNAMELEN
+1];
159 unsigned short port
= 0;
166 int contentlen
, headerlen
; /* for the response */
167 snprintf(soapact
, sizeof(soapact
), "%s#%s", service
, action
);
170 /*soapbodylen = */snprintf(soapbody
, sizeof(soapbody
),
171 "<?xml version=\"1.0\"?>\r\n"
172 "<" SOAPPREFIX
":Envelope "
173 "xmlns:" SOAPPREFIX
"=\"http://schemas.xmlsoap.org/soap/envelope/\" "
174 SOAPPREFIX
":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
175 "<" SOAPPREFIX
":Body>"
176 "<" SERVICEPREFIX
":%s xmlns:" SERVICEPREFIX
"=\"%s\">"
177 "</" SERVICEPREFIX
":%s>"
178 "</" SOAPPREFIX
":Body></" SOAPPREFIX
":Envelope>"
179 "\r\n", action
, service
, action
);
184 const char * pe
, * pv
;
186 soapbodylen
= snprintf(soapbody
, sizeof(soapbody
),
187 "<?xml version=\"1.0\"?>\r\n"
188 "<" SOAPPREFIX
":Envelope "
189 "xmlns:" SOAPPREFIX
"=\"http://schemas.xmlsoap.org/soap/envelope/\" "
190 SOAPPREFIX
":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
191 "<" SOAPPREFIX
":Body>"
192 "<" SERVICEPREFIX
":%s xmlns:" SERVICEPREFIX
"=\"%s\">",
194 p
= soapbody
+ soapbodylen
;
197 /* check that we are never overflowing the string... */
198 if(soapbody
+ sizeof(soapbody
) <= p
+ 100)
200 /* we keep a margin of at least 100 bytes */
224 *(p
++) = SERVICEPREFIX2
;
229 strncpy(p
, "></" SOAPPREFIX
":Body></" SOAPPREFIX
":Envelope>\r\n",
230 soapbody
+ sizeof(soapbody
) - p
);
232 if(!parseURL(url
, hostname
, &port
, &path
)) return -1;
235 s
= socket(PF_INET
, SOCK_STREAM
, 0);
238 PRINT_SOCKET_ERROR("socket");
242 dest
.sin_family
= AF_INET
;
243 dest
.sin_port
= htons(port
);
244 dest
.sin_addr
.s_addr
= inet_addr(hostname
);
245 if(connect(s
, (struct sockaddr
*)&dest
, sizeof(struct sockaddr
))<0)
247 PRINT_SOCKET_ERROR("connect");
254 n
= soapPostSubmit(s
, path
, hostname
, port
, soapact
, soapbody
);
257 printf("Error sending SOAP request\n");
268 while ((n
= ReceiveData(s
, buf
, buffree
, 5000)) > 0) {
272 getContentLengthAndHeaderLength(buffer
, *bufsize
,
273 &contentlen
, &headerlen
);
275 printf("received n=%dbytes bufsize=%d ContLen=%d HeadLen=%d\n",
276 n
, *bufsize
, contentlen
, headerlen
);
278 /* break if we received everything */
279 if(contentlen
> 0 && headerlen
> 0 && *bufsize
>= contentlen
+headerlen
)
287 /* parseMSEARCHReply()
288 * the last 4 arguments are filled during the parsing :
289 * - location/locationsize : "location:" field of the SSDP reply packet
290 * - st/stsize : "st:" field of the SSDP reply packet.
291 * The strings are NOT null terminated */
293 parseMSEARCHReply(const char * reply
, int size
,
294 const char * * location
, int * locationsize
,
295 const char * * st
, int * stsize
)
299 a
= i
; /* start of the line */
308 b
= i
; /* end of the "header" */
320 /*for(j=b+1; j<i; j++)
325 do { b
++; } while(reply
[b
]==' ');
326 if(0==strncasecmp(reply
+a
, "location", 8))
331 else if(0==strncasecmp(reply
+a
, "st", 2))
347 /* port upnp discover : SSDP protocol */
349 #define XSTR(s) STR(s)
351 #define UPNP_MCAST_ADDR "239.255.255.250"
354 * return a chained list of all devices found or NULL if
355 * no devices was found.
356 * It is up to the caller to free the chained list
357 * delay is in millisecond (poll) */
358 struct UPNPDev
* upnpDiscover(int delay
, const char * multicastif
,
359 const char * minissdpdsock
, int sameport
)
361 struct UPNPDev
* tmp
;
362 struct UPNPDev
* devlist
= 0;
364 static const char MSearchMsgFmt
[] =
365 "M-SEARCH * HTTP/1.1\r\n"
366 "HOST: " UPNP_MCAST_ADDR
":" XSTR(PORT
) "\r\n"
368 "MAN: \"ssdp:discover\"\r\n"
371 static const char * const deviceList
[] = {
372 "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
373 "urn:schemas-upnp-org:service:WANIPConnection:1",
374 "urn:schemas-upnp-org:service:WANPPPConnection:1",
379 char bufr
[1536]; /* reception and emission buffer */
382 struct sockaddr_in sockudp_r
, sockudp_w
;
385 /* first try to get infos from minissdpd ! */
387 minissdpdsock
= "/var/run/minissdpd.sock";
388 while(!devlist
&& deviceList
[deviceIndex
]) {
389 devlist
= getDevicesFromMiniSSDPD(deviceList
[deviceIndex
],
391 /* We return what we have found if it was not only a rootdevice */
392 if(devlist
&& !strstr(deviceList
[deviceIndex
], "rootdevice"))
398 /* fallback to direct discovery */
400 sudp
= socket(PF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
402 sudp
= socket(PF_INET
, SOCK_DGRAM
, 0);
406 PRINT_SOCKET_ERROR("socket");
410 memset(&sockudp_r
, 0, sizeof(struct sockaddr_in
));
411 sockudp_r
.sin_family
= AF_INET
;
413 sockudp_r
.sin_port
= htons(PORT
);
414 sockudp_r
.sin_addr
.s_addr
= INADDR_ANY
;
416 memset(&sockudp_w
, 0, sizeof(struct sockaddr_in
));
417 sockudp_w
.sin_family
= AF_INET
;
418 sockudp_w
.sin_port
= htons(PORT
);
419 sockudp_w
.sin_addr
.s_addr
= inet_addr(UPNP_MCAST_ADDR
);
422 if (setsockopt(sudp
, SOL_SOCKET
, SO_REUSEADDR
, (const char *)&opt
, sizeof (opt
)) < 0)
424 if (setsockopt(sudp
, SOL_SOCKET
, SO_REUSEADDR
, &opt
, sizeof (opt
)) < 0)
427 PRINT_SOCKET_ERROR("setsockopt");
433 struct in_addr mc_if
;
434 mc_if
.s_addr
= inet_addr(multicastif
);
435 sockudp_r
.sin_addr
.s_addr
= mc_if
.s_addr
;
436 if(setsockopt(sudp
, IPPROTO_IP
, IP_MULTICAST_IF
, (const char *)&mc_if
, sizeof(mc_if
)) < 0)
438 PRINT_SOCKET_ERROR("setsockopt");
442 /* Avant d'envoyer le paquet on bind pour recevoir la reponse */
443 if (bind(sudp
, (struct sockaddr
*)&sockudp_r
, sizeof(struct sockaddr_in
)) != 0)
445 PRINT_SOCKET_ERROR("bind");
450 /* receiving SSDP response packet */
455 /* sending the SSDP M-SEARCH packet */
456 n
= snprintf(bufr
, sizeof(bufr
),
457 MSearchMsgFmt
, deviceList
[deviceIndex
++]);
458 /*printf("Sending %s", bufr);*/
459 n
= sendto(sudp
, bufr
, n
, 0,
460 (struct sockaddr
*)&sockudp_w
, sizeof(struct sockaddr_in
));
462 PRINT_SOCKET_ERROR("sendto");
467 /* Waiting for SSDP REPLY packet to M-SEARCH */
468 n
= ReceiveData(sudp
, bufr
, sizeof(bufr
), delay
);
474 /* no data or Time Out */
475 if (devlist
|| (deviceList
[deviceIndex
] == 0)) {
476 /* no more device type to look for... */
481 const char * descURL
=NULL
;
483 const char * st
=NULL
;
485 /*printf("%d byte(s) :\n%s\n", n, bufr);*/ /* affichage du message */
486 parseMSEARCHReply(bufr
, n
, &descURL
, &urlsize
, &st
, &stsize
);
489 /*printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
490 stsize, st, urlsize, descURL); */
491 tmp
= (struct UPNPDev
*)malloc(sizeof(struct UPNPDev
)+urlsize
+stsize
);
492 tmp
->pNext
= devlist
;
493 tmp
->descURL
= tmp
->buffer
;
494 tmp
->st
= tmp
->buffer
+ 1 + urlsize
;
495 memcpy(tmp
->buffer
, descURL
, urlsize
);
496 tmp
->buffer
[urlsize
] = '\0';
497 memcpy(tmp
->buffer
+ urlsize
+ 1, st
, stsize
);
498 tmp
->buffer
[urlsize
+1+stsize
] = '\0';
505 /* freeUPNPDevlist() should be used to
506 * free the chained list returned by upnpDiscover() */
507 void freeUPNPDevlist(struct UPNPDev
* devlist
)
509 struct UPNPDev
* next
;
512 next
= devlist
->pNext
;
519 url_cpy_or_cat(char * dst
, const char * src
, int n
)
529 strncpy(dst
, src
, n
);
537 strncpy(dst
+ l
, src
, n
- l
);
541 /* Prepare the Urls for usage...
543 void GetUPNPUrls(struct UPNPUrls
* urls
, struct IGDdatas
* data
,
544 const char * descURL
)
548 n1
= strlen(data
->urlbase
);
550 n1
= strlen(descURL
);
551 n1
+= 2; /* 1 byte more for Null terminator, 1 byte for '/' if needed */
553 n1
+= strlen(data
->scpdurl
);
554 n2
+= strlen(data
->controlurl
);
555 n3
+= strlen(data
->controlurl_CIF
);
557 urls
->ipcondescURL
= (char *)malloc(n1
);
558 urls
->controlURL
= (char *)malloc(n2
);
559 urls
->controlURL_CIF
= (char *)malloc(n3
);
560 /* maintenant on chope la desc du WANIPConnection */
561 if(data
->urlbase
[0] != '\0')
562 strncpy(urls
->ipcondescURL
, data
->urlbase
, n1
);
564 strncpy(urls
->ipcondescURL
, descURL
, n1
);
565 p
= strchr(urls
->ipcondescURL
+7, '/');
567 strncpy(urls
->controlURL
, urls
->ipcondescURL
, n2
);
568 strncpy(urls
->controlURL_CIF
, urls
->ipcondescURL
, n3
);
570 url_cpy_or_cat(urls
->ipcondescURL
, data
->scpdurl
, n1
);
572 url_cpy_or_cat(urls
->controlURL
, data
->controlurl
, n2
);
574 url_cpy_or_cat(urls
->controlURL_CIF
, data
->controlurl_CIF
, n3
);
577 printf("urls->ipcondescURL='%s' %d n1=%d\n", urls
->ipcondescURL
,
578 strlen(urls
->ipcondescURL
), n1
);
579 printf("urls->controlURL='%s' %d n2=%d\n", urls
->controlURL
,
580 strlen(urls
->controlURL
), n2
);
581 printf("urls->controlURL_CIF='%s' %d n3=%d\n", urls
->controlURL_CIF
,
582 strlen(urls
->controlURL_CIF
), n3
);
587 FreeUPNPUrls(struct UPNPUrls
* urls
)
591 free(urls
->controlURL
);
592 urls
->controlURL
= 0;
593 free(urls
->ipcondescURL
);
594 urls
->ipcondescURL
= 0;
595 free(urls
->controlURL_CIF
);
596 urls
->controlURL_CIF
= 0;
600 int ReceiveData(int socket
, char * data
, int length
, int timeout
)
604 struct pollfd fds
[1]; /* for the poll */
606 fds
[0].events
= POLLIN
;
607 n
= poll(fds
, 1, timeout
);
610 PRINT_SOCKET_ERROR("poll");
621 FD_SET(socket
, &socketSet
);
622 timeval
.tv_sec
= timeout
/ 1000;
623 timeval
.tv_usec
= (timeout
% 1000) * 1000;
624 /*n = select(0, &socketSet, NULL, NULL, &timeval);*/
625 n
= select(FD_SETSIZE
, &socketSet
, NULL
, NULL
, &timeval
);
628 PRINT_SOCKET_ERROR("select");
636 n
= recv(socket
, data
, length
, 0);
639 PRINT_SOCKET_ERROR("recv");
645 UPNPIGD_IsConnected(struct UPNPUrls
* urls
, struct IGDdatas
* data
)
650 UPNP_GetStatusInfo(urls
->controlURL
, data
->servicetype
,
651 status
, &uptime
, NULL
);
652 if(0 == strcmp("Connected", status
))
661 /* UPNP_GetValidIGD() :
664 * 1 = A valid connected IGD has been found
665 * 2 = A valid IGD has been found but it reported as
667 * 3 = an UPnP device has been found but was not recognized as an IGD
669 * In any non zero return case, the urls and data structures
670 * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
671 * free allocated memory.
674 UPNP_GetValidIGD(struct UPNPDev
* devlist
,
675 struct UPNPUrls
* urls
,
676 struct IGDdatas
* data
,
677 char * lanaddr
, int lanaddrlen
)
681 struct UPNPDev
* dev
;
682 int state
; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
686 printf("Empty devlist\n");
690 for(state
= 1; state
<= 3; state
++)
692 for(dev
= devlist
; dev
; dev
= dev
->pNext
)
694 /* we should choose an internet gateway device.
695 * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
696 descXML
= miniwget_getaddr(dev
->descURL
, &descXMLsize
,
697 lanaddr
, lanaddrlen
);
700 memset(data
, 0, sizeof(struct IGDdatas
));
701 memset(urls
, 0, sizeof(struct UPNPUrls
));
702 parserootdesc(descXML
, descXMLsize
, data
);
705 if(0==strcmp(data
->servicetype_CIF
,
706 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")
709 GetUPNPUrls(urls
, data
, dev
->descURL
);
712 printf("UPNPIGD_IsConnected(%s) = %d\n",
714 UPNPIGD_IsConnected(urls
, data
));
716 if((state
>= 2) || UPNPIGD_IsConnected(urls
, data
))
720 memset(data
, 0, sizeof(struct IGDdatas
));
725 printf("error getting XML description %s\n", dev
->descURL
);
733 /* UPNP_GetIGDFromUrl()
734 * Used when skipping the discovery process.
739 UPNP_GetIGDFromUrl(const char * rootdescurl
,
740 struct UPNPUrls
* urls
,
741 struct IGDdatas
* data
,
742 char * lanaddr
, int lanaddrlen
)
746 descXML
= miniwget_getaddr(rootdescurl
, &descXMLsize
,
747 lanaddr
, lanaddrlen
);
749 memset(data
, 0, sizeof(struct IGDdatas
));
750 memset(urls
, 0, sizeof(struct UPNPUrls
));
751 parserootdesc(descXML
, descXMLsize
, data
);
754 GetUPNPUrls(urls
, data
, rootdescurl
);