1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
24 Change History (most recent first):
26 $Log: LegacyNATTraversal.c,v $
27 Revision 1.11 2004/12/03 03:34:20 ksekar
28 <rdar://problem/3882674> LegacyNATTraversal.c leaks threads
30 Revision 1.10 2004/12/01 02:43:49 cheshire
31 Update copyright message
33 Revision 1.9 2004/10/27 02:25:05 cheshire
34 <rdar://problem/3816029> Random memory smashing bug
36 Revision 1.8 2004/10/27 02:17:21 cheshire
37 Turn off "safe_close: ERROR" error messages -- there are too many of them
39 Revision 1.7 2004/10/26 21:15:40 cheshire
40 <rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0
41 Additional fixes: Code should set fds to -1 after closing sockets.
43 Revision 1.6 2004/10/26 20:59:20 cheshire
44 <rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0
46 Revision 1.5 2004/10/26 01:01:35 cheshire
47 Use "#if 0" instead of commenting out code
49 Revision 1.4 2004/10/10 06:51:36 cheshire
50 Declared some strings "const" as appropriate
52 Revision 1.3 2004/09/21 23:40:12 ksekar
53 <rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure
55 Revision 1.2 2004/09/17 01:08:52 cheshire
56 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
57 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
58 declared in that file are ONLY appropriate to single-address-space embedded applications.
59 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
61 Revision 1.1 2004/08/18 17:35:41 ksekar
62 <rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways
67 #include "mDNSEmbeddedAPI.h"
68 #include "mDNSMacOSX.h"
80 #include <sys/types.h>
81 #include <sys/socket.h>
82 #include <netinet/in.h>
83 #include <netinet/tcp.h>
86 #include <sys/ioctl.h>
88 #include <netinet/in.h>
89 #include <arpa/inet.h>
90 #include <sys/sysctl.h>
91 #include <net/route.h>
94 #include <arpa/inet.h>
101 //#include "netaddr.h"
103 // TODO: remove later and do variable length
104 #define MAX_SOAPMSGSIZE 65536
106 static int safe_close(int fd
)
108 if (fd
< 3) { /* LogMsg("safe_close: ERROR sd %d < 3", fd); */ return(-1); }
112 #define close safe_close
114 ////////////////////////////////////////////////////////////////////////
116 ////////////////////////////////////////////////////////////////////////
119 #define NA_E_SUCCESS (0)
120 #define NA_E_INTERNAL_ERROR (-1) /* somewhere something wrong */
121 #define NA_E_INVALID_PARAMETER (-2) /* bad params */
122 #define NA_E_OPERATION_FAILED (-3) /* can't fulfill request */
123 #define NA_E_TIMEOUT (-4) /* operation timed out */
124 #define NA_E_THREAD_ERROR (-5) /* some error related to threads */
125 #define NA_E_PARSE_ERROR (-6) /* a parsing error occured */
126 #define NA_E_NOT_READY (-7) /* this op can't proceed yet */
127 #define NA_E_NOT_FOUND (-8) /* resource/prereq not found */
128 #define NA_E_NOT_AVAILABLE (-9) /* service not available */
129 #define NA_E_EXISTS (-10) /* can't modify existing item */
130 #define NA_E_AGAIN (-11) /* something wrong - try again */
131 #define NA_E_NOT_SUPPORTED (-12) /* wait until next version */
132 #define NA_E_ABORT (-14) /* operation aborted */
133 #define NA_E_NET (-15) /* network layer problem */
135 // Logging flags - log types (increasing degree of detail)
136 #define NALOG_ERROR (1UL) /* error messages */
137 #define NALOG_ALERT (2UL) /* useful warning/alerts */
138 #define NALOG_INFO0 (4UL) /* info - potential problem */
139 #define NALOG_INFO1 (8UL) /* extra info */
140 #define NALOG_DUMP (16UL) /* data dumps */
142 #define NALOG_RSRV1 (32UL) /* reserved */
143 #define NALOG_RSRV2 (64UL) /* reserved */
144 #define NALOG_RSRV3 (128UL) /* reserved */
146 // Logging flags - component (not used for now)
147 #define NALOG_UPNP (256) /* UPnP */
149 // Default Logging levels
150 #define NALOG_LEVEL0 (0)
151 #define NALOG_LEVEL1 (NALOG_UPNP | NALOG_ERROR)
152 #define NALOG_LEVEL2 (NALOG_LEVEL1 | NALOG_ALERT)
153 #define NALOG_LEVEL3 (NALOG_LEVEL2 | NALOG_INFO0)
154 #define NALOG_LEVEL4 (NALOG_LEVEL3 | NALOG_INFO1)
155 #define NALOG_LEVEL5 (NALOG_LEVEL4 | NALOG_DUMP)
156 #define NALOG_DEFAULT_LEVEL (NALOG_LEVEL2)
158 // Default timeout values (in m-seconds (milli))
159 // 50 milliseconds for function timeout
160 #define NA_DEFAULT_FUNCTION_TIMEOUT (50)
162 ////////////////////////////////////////////////////////////////////////
164 ////////////////////////////////////////////////////////////////////////
165 #define SSDP_IP "239.255.255.250"
166 #define SSDP_PORT 1900
170 #define H_CRLF "\r\n"
171 // SOAP message's CRLF:
172 //#define S_CRLF "\r\n"
175 // standard 200 ok msg
176 #define HTTP200OK "HTTP/1.1 200 OK\r\n\r\n"
177 #define HTTP200OKLEN (sizeof(HTTP200OK) - 1)
179 // maximum time to wait for an event (in microseconds)
180 #define MAX_EXPECTEVENTTIME (10000)
182 ////////////////////////////////////////////////////////////////////////
184 ////////////////////////////////////////////////////////////////////////
185 typedef struct tagProperty
{
189 } Property
, *PProperty
;
191 typedef struct tagHTTPResponse
{
195 Property aHeaders
[30]; // assume at most this many headers
201 } HTTPResponse
, *PHTTPResponse
, **PPHTTPResponse
;
203 ////////////////////////////////////////////////////////////////////////
205 ////////////////////////////////////////////////////////////////////////
206 static const char szSSDPMsgDiscoverRoot
[] =
207 "M-SEARCH * HTTP/1.1\r\n"
208 "Host:239.255.255.250:1900\r\n"
209 "ST:upnp:rootdevice\r\n"
210 "Man:\"ssdp:discover\"\r\n"
214 static const char szSSDPMsgDiscoverIGD
[] =
215 "M-SEARCH * HTTP/1.1\r\n"
216 "Host:239.255.255.250:1900\r\n"
217 "ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
218 "Man:\"ssdp:discover\"\r\n"
222 static const char szSSDPMsgDiscoverNAT
[] =
223 "M-SEARCH * HTTP/1.1\r\n"
224 "Host:239.255.255.250:1900\r\n"
225 "ST:urn:schemas-upnp-org:service:WANIPConnection:1\r\n"
226 "Man:\"ssdp:discover\"\r\n"
230 //// Subscribe message
232 // 2$s: local's host/port ("host:port")
233 // 3$s: router's host/port ("host:port")
234 // 4$d: subscription timeout in seconds
235 static const char szEventMsgSubscribeFMT
[] =
236 "SUBSCRIBE %1$s HTTP/1.1\r\n"
238 "Callback: <http://%2$s/notify>\r\n"
239 "Timeout: Second-%4$d\r\n"
240 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
242 "Content-Length: 0\r\n"
243 "Pragma: no-cache\r\n"
246 //// Unsubscribe message
248 // 2$s: SID (some uuid passed back during subscribe)
249 // 3$s: router's host ("host")
251 static const char szEventMsgUnsubscribeFMT
[] =
252 "UNSUBSCRIBE %1$s HTTP/1.1\r\n"
254 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
256 "Content-Length: 0\r\n"
257 "Pragma: no-cache\r\n"
261 //// Generic SOAP Control:Action request messages
263 // 2$s: router's host/port ("host:port")
264 // 3$s: action (string)
265 // 4$d: content-length
266 static const char szSOAPMsgControlAHeaderFMT
[] =
267 //"M-POST %1$s HTTP/1.1\r\n"
268 "POST %1$s HTTP/1.1\r\n"
269 "Content-Type: text/xml; charset=\"utf-8\"\r\n"
270 //"TEST: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
271 //"Man: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
272 //"01-SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#%3$s\"\r\n"
273 "SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#%3$s\"\r\n"
274 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n"
276 "Content-Length: %4$d\r\n"
277 "Connection: close\r\n"
278 // "Connection: Keep-Alive\r\n"
279 "Pragma: no-cache\r\n"
282 // 1$: action (string)
284 static const char szSOAPMsgControlABodyFMT
[] =
285 "<?xml version=\"1.0\"?>" CRLF
286 "<SOAP-ENV:Envelope" S_CRLF
287 " xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"" S_CRLF
288 " SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" S_CRLF
289 "<SOAP-ENV:Body>" S_CRLF
291 " xmlns:m=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" S_CRLF
294 "</SOAP-ENV:Body>" S_CRLF
295 "</SOAP-ENV:Envelope>" S_CRLF
302 // 2$: argument value
303 static const char szSOAPMsgControlAArgumentFMT
[] =
304 "<%1$s>%2$s</%1$s>" S_CRLF
;
307 // 2$: argument value
309 static const char szSOAPMsgControlAArgumentFMT_t
[] =
311 " xmlns:dt=\"urn:schemas-microsoft-com:datatypes\""
312 " dt:dt=\"%3$s\">%2$s</%1$s>" S_CRLF
;
315 //// Generic SOAP Control:Query request messages
317 // 2$s: router's host/port ("host:port")
318 // 3$d: content-length
319 static const char szSOAPMsgControlQHeaderFMT
[] =
320 "M-POST %1$s HTTP/1.1\r\n"
321 //"POST %1$s HTTP/1.1\r\n"
323 "Content-Length: %3$d\r\n"
324 "Content-Type: text/xml; charset-\"utf-8\"\r\n"
325 //"Man: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
326 //"SOAPAction: \"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"\r\n"
327 "01-SOAPAction: \"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"\r\n"
331 static const char szSOAPMsgControlQBodyFMT
[] =
333 " xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"" S_CRLF
334 " s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" S_CRLF
336 "<u:QueryStateVariable xmlns:u=\"urn:schemas-upnp-org:control-1-0\"" S_CRLF
337 "<u:varName>%s</u:varName>" S_CRLF
338 "</u:QueryStateVariable>" S_CRLF
340 "</s:Envelope>" S_CRLF
343 // 1$: device description URL
345 static const char szSSDPMsgDescribeDeviceFMT
[] =
346 "GET %s HTTP/1.1\r\n"
347 "Accept: text/xml, application/xml\r\n"
348 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
350 "Connection: close\r\n"
351 // "Connection: Keep-Alive\r\n"
354 ////////////////////////////////////////////////////////////////////////
356 ////////////////////////////////////////////////////////////////////////
358 static int g_fFirstInit
= TRUE
;
359 static int g_fQuit
= FALSE
;
361 static int g_fLogging
;
363 // Globally-accessible UDP socket
364 static int g_sUDP
= -1;
365 static int g_sUDPCancel
= -1;
367 // Globally-accessible TCP socket
368 static int g_sTCP
= -1;
369 static int g_sTCPCancel
= -1;
372 static int g_fEventEnabled
= FALSE
;
373 static unsigned short g_wEventPort
;
374 static struct sockaddr_in g_saddrRouterEvent
;
375 static char g_szRouterHostPortEvent
[1024];
376 static char g_szEventURL
[1024];
379 static char g_szFriendlyName
[1024];
380 static char g_szManufacturer
[1024];
381 static char g_szModelName
[1024];
382 static char g_szModelDescription
[1024];
385 static struct sockaddr_in g_saddrRouterBase
;
386 static char g_szRouterHostPortBase
[1024];
389 static pthread_t g_UDPthread
= NULL
;
390 static pthread_t g_TCPthread
= NULL
;
393 static unsigned long g_dwLocalIP
= 0;
395 // Globally accessible info about the router/UPnP
396 static int g_fUPnPEnabled
= FALSE
;
397 static char g_szUSN
[1024];
399 static struct sockaddr_in g_saddrRouterDesc
;
400 static char g_szRouterHostPortDesc
[1024];
401 static char g_szNATDevDescURL
[1024];
403 static struct sockaddr_in g_saddrRouterSOAP
;
404 static char g_szRouterHostPortSOAP
[1024];
405 static char g_szControlURL
[1024];
406 static int g_fControlURLSet
= FALSE
;
408 // Lock/condvar for synchronous upnp calls
409 static pthread_mutex_t g_xUPnP
;
410 static pthread_mutex_t g_xUPnPMsg
;
411 static pthread_cond_t g_condUPnP
;
412 static pthread_cond_t g_condUPnPControlURL
;
413 static struct timeval g_tvUPnPInitTime
;
414 static struct timeval g_tvLastUpdateTime
;
416 // timeout values in seconds
417 static int g_iFunctionTimeout
= NA_DEFAULT_FUNCTION_TIMEOUT
;
419 static void GetDeviceDescription(void);
420 static void SetLocalIP(void);
422 ////////////////////////////////////////////////////////////////////////
424 ////////////////////////////////////////////////////////////////////////
429 #define IFNAMELEN 16 /* Interface Name Length */
430 #define IPLEN 16 /* 16 bytes(128 bits) for IPv6 */
432 typedef struct tagIPINFO
435 char szIfName
[IFNAMELEN
]; /* Interface name */
436 unsigned char abIP
[IPLEN
]; /* IP in host byte order */
437 unsigned short wPort
;
438 } IPINFO
, *PIPINFO
, **PPIPINFO
;
440 typedef struct hostent HOSTENT
, *PHOSTENT
;
442 static unsigned long GetNATIPNetmask(unsigned long dwIP
)
444 if ((dwIP
& 0xFF000000) == 0x0A000000) return 0xFF000000;
445 if ((dwIP
& 0xFFF00000) == 0xAC100000) return 0xFFF00000;
446 if ((dwIP
& 0xFFFF0000) == 0xC0a80000) return 0xFFFF0000;
448 return 0; /* No NAT IP */
451 static int GetIPInfo(PPIPINFO ppIPInfo
)
454 int iLastLen
, iLen
, iNum
= 0, iMax
= 0;
456 char *pcBuf
, *pcTemp
;
457 PIPINFO pIPInfo
= NULL
;
459 struct ifreq
*ifr
, ifrcopy
;
461 if (ppIPInfo
== NULL
) return 0;
463 fd
= socket(AF_INET
, SOCK_DGRAM
, 0);
466 iLen
= 100 * sizeof(struct ifreq
);
470 pcBuf
= (char *)malloc(iLen
);
474 if (ioctl(fd
, SIOCGIFCONF
, &ifc
) < 0)
476 if (errno
!= EINVAL
|| iLastLen
!= -1)
478 // DbgPrint(ELL_ERROR, "ioctl failed(%d)\n", errno);
486 if (ifc
.ifc_len
== iLastLen
) break;
487 iLastLen
= ifc
.ifc_len
;
490 iLen
+= 10 * sizeof(struct ifreq
);
494 for (pcTemp
= pcBuf
; pcTemp
< pcBuf
+ ifc
.ifc_len
; )
501 pIPInfoNew
= (PIPINFO
)realloc(pIPInfo
, sizeof(IPINFO
) * iMax
);
502 if (pIPInfoNew
== NULL
)
509 else pIPInfo
= pIPInfoNew
;
511 memset(pIPInfo
+ (iMax
- 10), 0, sizeof(IPINFO
) * 10);
514 ifr
= (struct ifreq
*)pcTemp
;
516 pcTemp
+= sizeof(ifr
->ifr_name
) + ifr
->ifr_addr
.sa_len
;
518 /* discard invalid address families & loopback */
519 if ((ifr
->ifr_addr
.sa_family
!= AF_INET
&&
520 ifr
->ifr_addr
.sa_family
!= AF_INET6
) ||
521 strncmp(ifr
->ifr_name
, "lo", 2) == 0) continue;
524 ioctl(fd
, SIOCGIFFLAGS
, &ifrcopy
);
525 if ((ifrcopy
.ifr_flags
& IFF_UP
) == 0) continue;
527 switch (ifr
->ifr_addr
.sa_family
)
530 memcpy(pIPInfo
[iNum
].szIfName
, ifr
->ifr_name
, IFNAMELEN
);
532 ntohl(((struct sockaddr_in
*)&ifr
->ifr_addr
)->sin_addr
.s_addr
);
533 memcpy(pIPInfo
[iNum
].abIP
, &dwIP
, sizeof(unsigned long));
534 if (ifrcopy
.ifr_flags
& IFF_POINTOPOINT
)
535 pIPInfo
[iNum
].iFlags
|= ISPPP
;
540 memcpy(pIPInfo
[iNum
].szIfName
, ifr
->ifr_name
, IFNAMELEN
);
541 memcpy(pIPInfo
[iNum
].abIP
,
542 ((struct sockaddr_in6
*)&(ifr
->ifr_addr
))-> sin6_addr
.s6_addr
,
544 pIPInfo
[iNum
].iFlags
|= ISIPV6
;
545 if (ifrcopy
.ifr_flags
& IFF_POINTOPOINT
)
546 pIPInfo
[iNum
].iFlags
|= ISPPP
;
563 static void FreeIPInfo(PIPINFO pIPInfo
)
565 if (pIPInfo
!= NULL
) free(pIPInfo
);
569 ////////////////////////////////////////////////////////////////////////
570 // Function Definitions
571 ////////////////////////////////////////////////////////////////////////
573 static void SendDiscoveryMsg();
576 // Creates a UDP multicast socket and listens to the SSDP IP/PORT
578 // -1 on error, or the socket descriptor if success
579 static int SSDPListen()
584 struct sockaddr_in saddr
;
587 // IPPROTO_IP == 0; IPPROTO_TCP == 6; IPPROTO_UDP == 17; etc.
588 sd
= socket(AF_INET
, SOCK_DGRAM
, 0);
590 if (g_fLogging
& NALOG_ERROR
)
591 fprintf(g_log
, "Can't create socket! SSDPListen exiting\n");
595 // sock options values
596 fLoop
= 0; // false - don't send copy to self
599 // bind to listen to ssdp multicast address
600 bzero(&saddr
, sizeof(saddr
));
601 saddr
.sin_len
= sizeof(saddr
);
602 saddr
.sin_family
= AF_INET
;
603 //saddr.sin_addr.s_addr = inet_addr(SSDP_IP);
604 //saddr.sin_port = htons(SSDP_PORT);
605 saddr
.sin_addr
.s_addr
= htonl(g_dwLocalIP
);
608 // and set the multicast add_member structure
609 // (TODO: need to find interfaces later - ioctl, with:
610 // SIOCFIFCONF to find if's, SIOCGIFADDR to get addr, and SIOCFIFFLAGS
611 // to check for IFF_MULTICAST flag for multicast support on an if)
612 bzero(&mreq
, sizeof(mreq
));
613 mreq
.imr_interface
.s_addr
= g_dwLocalIP
;
614 mreq
.imr_multiaddr
.s_addr
= inet_addr(SSDP_IP
);
617 bind(sd
, (struct sockaddr
*)&saddr
, sizeof(saddr
)) //||
618 //setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &fLoop, sizeof(fLoop)) ||
619 //setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &iTTL, sizeof(iTTL)) ||
620 //setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))
622 if (g_fLogging
& NALOG_ERROR
)
624 "bind/setsockopt for multicast failed... errno = %d\n", errno
);
632 static int EventListen()
634 struct sockaddr_in saddr
;
637 // try 5 ports before failing completely
638 for (g_wEventPort
= 5000; g_wEventPort
< 5005; g_wEventPort
++)
640 sd
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
642 if (g_fLogging
& NALOG_ERROR
)
643 fprintf(g_log
, "Can't create socket! EventListen exiting\n");
647 bzero(&saddr
, sizeof(saddr
));
648 saddr
.sin_len
= sizeof(saddr
);
649 saddr
.sin_family
= AF_INET
;
650 saddr
.sin_addr
.s_addr
= htonl(g_dwLocalIP
);
651 saddr
.sin_port
= htons(g_wEventPort
);
654 if (bind(sd
, (struct sockaddr
*)&saddr
, sizeof(saddr
)) == 0)
657 ////TracePrint(ELL_TRACE, "UPnP: EventListen @%u\n", g_wEventPort);
661 // unsuccessful - close sd and try again
662 if (g_fLogging
& NALOG_ERROR
)
664 "bind TCP port %u failed: errno = %d\n", g_wEventPort
, errno
);
671 static void *TCPProc(void *in
);
673 static int EventInit()
678 if (g_fEventEnabled
== FALSE
)
680 // initialize TCP socket for Eventing
681 g_sTCP
= EventListen();
683 if (g_fLogging
& NALOG_ERROR
)
684 fprintf(g_log
, "EventInit - Failed to init tcp socket.\n");
685 return NA_E_INTERNAL_ERROR
;
689 pthread_attr_init(&attr
);
690 iRet
= pthread_create(&g_TCPthread
, &attr
, TCPProc
, 0);
694 if (g_fLogging
& NALOG_ERROR
)
695 fprintf(g_log
, "EventInit: TCPProc create failed(%d)\n", iRet
);
696 return NA_E_THREAD_ERROR
;
700 g_fEventEnabled
= TRUE
;
705 static void DumpHex(char *buf
, int len
)
712 if (g_fLogging
& NALOG_DUMP
) {
713 if (buf
== NULL
) return;
714 if (len
<= 0) return;
716 for (i
= 0; i
< len
; i
= nexti
) {
717 fprintf(g_log
, "%04x: ", i
);
719 endj
= (nexti
> len
) ? len
: nexti
;
720 for (j
= i
; j
< endj
; j
++)
721 fprintf(g_log
, "%02x ", buf
[j
] & 0xff);
724 char pad
[3 * 16 + 1]; // don't need the last 3 bytes anyway
725 j
= (16 - (j
% 16)) * 3;
731 for (j
= i
; j
< endj
; j
++)
732 isprint(buf
[j
]) ? fputc(buf
[j
], g_log
) : fputc('.', g_log
);
739 // FindHTTPHeaderNewLine
740 // Returns a pointer to the beginning of a CRLF, that is not a
741 // part of LWS. (LWS is CRLF followed by a space or tab, and in
742 // HTTP, considered as equivalent to a single space) (LWS stands
743 // for "linear white space")
744 // Returns a pointer the beginning of CRLF, and sets the EOH flag to
745 // whether this is the last header in the HTTP header section.
746 // Also, if pbuf is NULL, or if there isn't any CRLF found in the
747 // string, or if the HTTP syntax is wrong, NULL is returned, and
748 // the EOH flag is not touched.
749 static char *FindHTTPHeaderNewLine(char *pbuf
, int iBufSize
, int *pfEOH
)
754 if (pbuf
== NULL
) return NULL
;
757 result
= memchr(pbuf
, '\r', iBufSize
);
758 if (result
== NULL
) {
759 if (g_fLogging
& NALOG_INFO0
) {
760 fprintf(g_log
, "FindHTTPHeaderNewLine: er @(%d)\n", i
);
767 // decrement iBufSize, and move pbuf forward
768 iBufSize
-= (result
- pbuf
);
771 ++pbuf
; // now pointing right after "\r"
773 if (*pbuf
== '\0') break;
774 if (*pbuf
!= '\n') continue;
776 ++pbuf
; // now pointing after "\r\n"
778 if (*pbuf
== '\0') break;
779 if ((*pbuf
== ' ') || (*pbuf
== '\t')) continue;
781 // at this point we know we're at the end of a header field,
782 // and there's more stuff coming...
784 // just need to check if this is the last header
785 if ((pbuf
[0] == '\r') && (pbuf
[1] == '\n'))
796 // NewHTTPResponse_sz
797 // Creates an HTTPResponse structure from a string (sz). Set
798 // fDestroyOriginal to TRUE if the buffer passed in can be overwritten.
799 // Otherwise, NewHTTPResponse_sz will duplicate the buffer.
800 // Returns the created HTTPResponse structure if successful, or if an
801 // error occured (out of memory, or bad http syntax), returns NULL.
802 // NOTE: ALWAYS call DeleteHTTPResponse after using the HTTPResponse structure.
803 // NOTE: The input is assumed to be correct. If there're HTTP syntax errors,
804 // and the pszHTTPResponse is not null-terminated, result may be undefined.
805 // (to be fixed next version)
806 static PHTTPResponse
NewHTTPResponse_sz(
807 char *pszHTTPResponse
,
809 int fDestroyOriginal
)
811 PHTTPResponse pResponse
;
817 if ((pResponse
= (PHTTPResponse
)malloc(sizeof(HTTPResponse
))) == NULL
) {
818 if (g_fLogging
& NALOG_INFO0
) {
819 fprintf(g_log
, "NewHTTPResponse_sz: er 1\n");
825 // make copy of buffer now
826 if (fDestroyOriginal
) {
827 pResponse
->buf
= NULL
;
828 pBuf
= pszHTTPResponse
;
831 int len
= strlen(pszHTTPResponse
);
832 if ((len
+1) > iBufferSize
) {
833 if (g_fLogging
& NALOG_INFO0
)
834 fprintf(g_log
, "Length: %d > %d\n", len
+1, iBufferSize
);
837 if ((pResponse
->buf
= (char *)malloc(iBufferSize
)) == NULL
) {
839 if (g_fLogging
& NALOG_INFO0
) {
840 fprintf(g_log
, "NewHTTPResponse_sz: er 2\n");
845 memcpy(pResponse
->buf
, pszHTTPResponse
, iBufferSize
);
846 pBuf
= pResponse
->buf
;
849 // get the first line
850 pszEOL
= FindHTTPHeaderNewLine(pBuf
, iBufferSize
, &fEOH
);
851 if (pszEOL
== NULL
) {
852 if (g_fLogging
& NALOG_INFO0
) {
853 fprintf(g_log
, "NewHTTPResponse_sz: er 3\n");
859 *pszEOL
= '\0'; // terminate the status line
860 pszEOL
+= 2; // point to the rest of the buffer
862 // set the status string first
863 pResponse
->pszStatus
= strchr(pBuf
, ' ');
864 if (pResponse
->pszStatus
== NULL
) {
865 if (g_fLogging
& NALOG_INFO0
) {
866 fprintf(g_log
, "NewHTTPResponse_sz: er 4\n");
869 goto cleanup
; // syntax error
872 pResponse
->pszStatus
++; // point to the actual status
874 pResponse
->pszReason
= strchr(pResponse
->pszStatus
, ' ');
875 if (pResponse
->pszReason
== NULL
) {
876 if (g_fLogging
& NALOG_INFO0
) {
877 fprintf(g_log
, "NewHTTPResponse_sz: er 5\n");
880 goto cleanup
; // syntax error
883 pResponse
->pszReason
[0] = '\0'; // terminate status string
884 pResponse
->pszReason
++; // point to the reason string
886 iNumHeaders
= 0; // initialize to 0 headers
888 // parse header fields line by line (while not end of headers)
890 PProperty pHeader
= &(pResponse
->aHeaders
[iNumHeaders
]);
891 // point header field name to the first char of the line
892 pHeader
->pszName
= pszEOL
;
894 // search for the end of line
895 pszEOL
= FindHTTPHeaderNewLine(pszEOL
,
896 iBufferSize
- (pszEOL
- pBuf
), // remainder size
898 if (pszEOL
== NULL
) goto cleanup
; // syntax error
900 *pszEOL
= '\0'; // terminate this string
901 pszEOL
+= 2; // point to beginning of next line
903 pHeader
->pszValue
= strchr(pHeader
->pszName
, ':');
904 if (pHeader
->pszValue
== NULL
) {
905 if (g_fLogging
& NALOG_INFO0
) {
906 fprintf(g_log
, "NewHTTPResponse_sz: er 6\n");
909 goto cleanup
; // syntax error (header field has no ":")
912 pHeader
->pszValue
[0] = '\0'; // terminate the header name string
913 pHeader
->pszValue
++; // point after the ":"
914 // get rid of leading spaces for the value part
916 (pHeader
->pszValue
[0] == ' ') ||
917 (pHeader
->pszValue
[0] == '\t') ||
918 (pHeader
->pszValue
[0] == '\r') ||
919 (pHeader
->pszValue
[0] == '\n')
921 pHeader
->pszValue
++; // skip the space
924 iNumHeaders
++; // added one more header
925 pHeader
++; // point to the next header in pResponse->aHeaders
928 pResponse
->iNumHeaders
= iNumHeaders
; // remember to set it in pResponse
930 pResponse
->pszBody
= pszEOL
+ 2; // point after the empty line
935 if (pResponse
->buf
!= NULL
) free(pResponse
->buf
);
940 // DeleteHTTPResponse
941 // Deallocates stuff in the HTTPResponse structure, effectively returning
942 // memory to the system and destroying the structure.
943 // NOTE: The pointer pResponse WILL BE FREED, and will be unusable after
944 // the call to DeleteHTTPResponse.
945 static void DeleteHTTPResponse(PHTTPResponse pResponse
)
949 if (pResponse
== NULL
) return;
951 // Current impl is just simple array - no need to free()
952 //for (i = 0; i < pResponse->iNumHeaders; i++) {
953 // free(pResponse->aHeaders[i]);
956 if (pResponse
->buf
!= NULL
)
957 free(pResponse
->buf
);
961 //typedef struct tagHTTPResponse {
965 // Property aHeaders[30]; // assume at most this many headers
971 //} HTTPResponse, *PHTTPResponse, **PPHTTPResponse;
973 static void PrintHTTPResponse(PHTTPResponse pResponse
)
977 if (g_fLogging
& (NALOG_INFO1
)) {
978 if (pResponse
== NULL
) return;
979 fprintf(g_log
, " *** HTTP response begin *** \n");
980 fprintf(g_log
, " * status = [%s], reason = [%s] *\n",
981 pResponse
->pszStatus
, pResponse
->pszReason
);
982 for (i
= 0; i
< pResponse
->iNumHeaders
; i
++) {
983 fprintf(g_log
, " * Header \"%s\" = [%s]\n",
984 pResponse
->aHeaders
[i
].pszName
,
985 pResponse
->aHeaders
[i
].pszValue
);
987 if (g_fLogging
& NALOG_DUMP
)
988 fprintf(g_log
, " * body = [%s] *\n", pResponse
->pszBody
);
989 fprintf(g_log
, " *** HTTP response end *** \n");
993 static int DiscoverRouter(PHTTPResponse pResponse
)
996 int fLocation
= FALSE
;
998 int fIsNATDevice
= FALSE
;
1001 if (strcmp(pResponse
->pszStatus
, "200") != 0)
1005 if (pResponse
== NULL
) {
1006 if (g_fLogging
& NALOG_INFO0
)
1007 fprintf(g_log
, "DiscoverRouter: pResponse == NULL\n");
1011 // check to see if this is a relevant packet
1012 for (i
= 0; i
< pResponse
->iNumHeaders
; i
++) {
1013 PProperty pHeader
= &(pResponse
->aHeaders
[i
]);
1015 if ((strcasecmp(pHeader
->pszName
, "ST") == 0) ||
1016 (strcasecmp(pHeader
->pszName
, "NT") == 0)) {
1017 if ((strcmp(pHeader
->pszValue
,
1018 "urn:schemas-upnp-org:service:WANIPConnection:1") == 0) ||
1019 (strcmp(pHeader
->pszValue
,
1020 "urn:schemas-upnp-org:device:InternetGatewayDevice:1") == 0)) {
1021 fIsNATDevice
= TRUE
;
1026 // leave the message alone if we don't need it
1030 // Now that we know we're looking at the message about the NAT device:
1031 pthread_mutex_lock(&g_xUPnP
);
1033 // set upnp to be unconfigured for now
1034 g_fUPnPEnabled
= FALSE
;
1036 // loop through the headers
1037 for (i
= 0; i
< pResponse
->iNumHeaders
; i
++) {
1038 PProperty pHeader
= &(pResponse
->aHeaders
[i
]);
1040 if (strcasecmp(pHeader
->pszName
, "Location") == 0) {
1044 if (g_fLogging
& NALOG_INFO1
)
1045 fprintf(g_log
, "Checking Location...\n");
1046 p
= pHeader
->pszValue
;
1047 if (strncmp(p
, "http://", 7) != 0)
1048 continue; // hope for another Location header to correct it
1049 p
+= 7; // skip over "http://"
1052 // set the control URL first
1054 g_szNATDevDescURL
[0] = '/';
1055 g_szNATDevDescURL
[1] = '\0';
1058 strncpy(g_szNATDevDescURL
, q
, sizeof(g_szNATDevDescURL
) - 1);
1059 g_szNATDevDescURL
[sizeof(g_szNATDevDescURL
) - 1] = '\0';
1060 // terminate the host/port string
1064 if (g_fLogging
& NALOG_INFO1
)
1065 fprintf(g_log
, " Device Description URL set to[%s]...\n",
1068 // see if port is specified
1071 sprintf(g_szRouterHostPortDesc
, "%s", p
);
1073 g_saddrRouterDesc
.sin_addr
.s_addr
= inet_addr(p
);
1074 g_saddrRouterDesc
.sin_port
= htons(80);
1077 // don't include the ":80" - HTTP is by default port 80
1078 if (atoi(q
+1) == 80) *q
= '\0';
1080 strcpy(g_szRouterHostPortDesc
, p
);
1082 // terminate the host part and point to it
1086 g_saddrRouterDesc
.sin_addr
.s_addr
= inet_addr(p
);
1087 g_saddrRouterDesc
.sin_port
= htons(atoi(q
));
1090 g_saddrRouterDesc
.sin_family
= AF_INET
;
1092 if (g_fLogging
& NALOG_INFO1
)
1093 fprintf(g_log
, " Router Address set to[%s]...\n",
1094 g_szRouterHostPortDesc
);
1097 else if (strcasecmp(pHeader
->pszName
, "USN") == 0) {
1098 if (g_fLogging
& NALOG_INFO1
)
1099 fprintf(g_log
, "Checking USN...\n");
1100 strncpy(g_szUSN
, pHeader
->pszValue
, sizeof(g_szUSN
) - 1);
1101 g_szUSN
[sizeof(g_szUSN
) - 1] = '\0';
1105 ; // do nothing for other headers for now
1109 // now check flags and set enabled if all set
1110 if (fLocation
&& fUSN
) {
1111 if (g_fLogging
& NALOG_INFO1
) {
1113 "Description Host/port string: [%s]\n"
1114 "NATDevDescURL: [%s], USN: [%s]\n",
1115 g_szRouterHostPortDesc
,
1116 g_szNATDevDescURL
, g_szUSN
);
1117 if (g_fLogging
& NALOG_INFO1
)
1118 fprintf(g_log
, "Got router information\n");
1121 g_fUPnPEnabled
= TRUE
;
1122 pthread_cond_broadcast(&g_condUPnP
);
1125 // remember to unlock before return
1126 pthread_mutex_unlock(&g_xUPnP
);
1131 // granularity is specified as: granularity = 1/nth seconds
1132 #define UPNP_TIMEOUT_GRANULARITY (1000)
1133 #define U_TOGRAN UPNP_TIMEOUT_GRANULARITY
1136 static void TimevalSubtract(
1137 struct timeval
*result
,
1138 const struct timeval
*a
,
1139 const struct timeval
*b
)
1141 result
->tv_sec
= a
->tv_sec
- b
->tv_sec
;
1143 if (b
->tv_usec
> a
->tv_usec
) {
1145 result
->tv_usec
= 1000000 + a
->tv_usec
- b
->tv_usec
;
1148 result
->tv_usec
= a
->tv_usec
- b
->tv_usec
;
1151 // elapsed = end - start
1152 static void GetTimeElapsed(
1153 const struct timeval
*tv_start
,
1154 const struct timeval
*tv_end
,
1155 struct timeval
*tv_elapsed
)
1157 TimevalSubtract(tv_elapsed
, tv_end
, tv_start
);
1159 tv_elapsed
->tv_sec
= tv_end
->tv_sec
- tv_start
->tv_sec
;
1161 if (tv_start
->tv_usec
> tv_end
->tv_usec
) {
1162 tv_elapsed
->tv_sec
--;
1163 tv_elapsed
->tv_usec
= 1000000 + tv_end
->tv_usec
- tv_start
->tv_usec
;
1166 tv_elapsed
->tv_usec
= tv_end
->tv_usec
- tv_start
->tv_usec
;
1170 // returns +1, 0, or -1, if a>b, a==b, a<b, respectively
1171 static int CompareTime(
1172 const struct timeval
*a
,
1173 const struct timeval
*b
1176 if ((a
->tv_sec
== b
->tv_sec
) &&
1177 (a
->tv_usec
== b
->tv_usec
)) return 0;
1179 if (a
->tv_sec
> b
->tv_sec
) return 1;
1180 else if (a
->tv_sec
< b
->tv_sec
) return -1;
1182 // if seconds are equal...
1183 if (a
->tv_usec
> b
->tv_usec
) return 1;
1187 static int WaitControlURLSet(double timeout
)
1191 struct timeval tv_start
;
1193 long to_sec
= (int) (timeout
/ U_TOGRAN
);
1195 (int) (((timeout
/ U_TOGRAN
) - to_sec
) * 1000000.0);
1196 //long to_sec = (int) timeout;
1197 //long to_usec = (int) ((timeout - to_sec) * 1000000.0);
1198 struct timeval elapsed
;
1200 // get function start time
1201 gettimeofday(&tv_start
, NULL
);
1203 pthread_mutex_lock(&g_xUPnP
);
1206 // if last update is too long ago then wait for it
1207 GetTimeElapsed(&g_tvLastUpdateTime
, &tv_start
, &elapsed
);
1208 if ((elapsed
.tv_sec
+ (elapsed
.tv_usec
/ 1000000.0)) >
1209 (((double) g_iUPnPTimeout
) / U_TOGRAN
))
1210 g_fControlURLSet
= 0;
1213 while (!g_fControlURLSet
) {
1215 gettimeofday(&tv
, NULL
);
1218 for now ignore device timeout
1219 // see if we've past the device's timeout first
1220 GetTimeElapsed(&g_tvUPnPInitTime
, &tv
, &elapsed
);
1221 if ((elapsed
.tv_sec
> g_timeout_sec
) ||
1222 ( (elapsed
.tv_sec
== g_timeout_sec
) &&
1223 (elapsed
.tv_usec
> g_timeout_usec
)
1226 pthread_mutex_unlock(&g_xUPnP
);
1231 // calculate ts to sleep till
1232 ts
.tv_sec
= tv
.tv_sec
+ to_sec
;
1233 ts
.tv_nsec
= (tv
.tv_usec
+ to_usec
) * 1000;
1234 if (ts
.tv_nsec
> 1000000000) {
1235 ts
.tv_nsec
-= 1000000000;
1239 // now get how long we've been in this function already and deduct
1240 GetTimeElapsed(&tv_start
, &tv
, &elapsed
);
1241 ts
.tv_sec
-= elapsed
.tv_sec
;
1242 if (ts
.tv_nsec
< (elapsed
.tv_usec
* 1000)) {
1244 ts
.tv_nsec
= 1000000000 + ts
.tv_nsec
- (elapsed
.tv_usec
* 1000);
1247 ts
.tv_nsec
-= (elapsed
.tv_usec
* 1000);
1250 iRet
= pthread_cond_timedwait(&g_condUPnPControlURL
, &g_xUPnP
, &ts
);
1252 // if timeout then return false
1255 pthread_mutex_unlock(&g_xUPnP
);
1259 pthread_mutex_unlock(&g_xUPnP
);
1264 static int WaitUPnPFunction()
1266 struct timeval start
;
1267 // struct timeval end;
1269 // struct timeval elapsed;
1271 gettimeofday(&start
, NULL
);
1273 wait2
= (double)g_iFunctionTimeout
;
1275 WaitControlURLSet(wait2
);
1277 //gettimeofday(&end, NULL);
1278 //GetTimeElapsed(&start, &end, &elapsed);
1279 //fprintf(stderr, "== wait2: (%f) %d.%06d\n",
1280 // wait2/U_TOGRAN, elapsed.tv_sec, elapsed.tv_usec);
1282 return g_fControlURLSet
;
1285 static void SetLocalIP();
1287 static int SendTCPMsg_saddr_parse(
1288 char *msg
, int iLen
,
1289 char *result
, int resultSize
,
1290 struct sockaddr_in
*saHost
);
1292 static void *TCPProc(void *in
)
1295 unsigned char buf
[MAX_SOAPMSGSIZE
];
1300 //TracePrint(ELL_TRACE, "UPnP: Begin TCPProc\n");
1302 // do the subscription
1305 char response
[2000];
1308 sprintf(callback
, "%lu.%lu.%lu.%lu:%u",
1309 (g_dwLocalIP
>> 24) & 0xFF,
1310 (g_dwLocalIP
>> 16) & 0xFF,
1311 (g_dwLocalIP
>> 8) & 0xFF,
1312 (g_dwLocalIP
>> 0) & 0xFF,
1316 szEventMsgSubscribeFMT
,
1318 callback
, g_szRouterHostPortEvent
, 1800);
1320 memset(response
, 0, 2000);
1321 n
= SendTCPMsg_saddr_parse(
1324 &g_saddrRouterEvent
);
1328 resp
= NewHTTPResponse_sz(buf
, n
, TRUE
);
1331 ////TracePrint(ELL_TRACE, "UPnP Subscribe returns %s/%d\n", resp->pszStatus, n);
1335 ////TracePrint(ELL_TRACE, "UPnP Subscribe not enough response (%d) \n[%s]\n",
1338 DeleteHTTPResponse(resp
);
1342 ////TracePrint(ELL_TRACE, "UPnP Subscribe failed (%d)\n", n);
1347 //TracePrint(ELL_TRACE, "UPnP: TCPProc begin loop\n");
1354 struct sockaddr_in recvaddr
;
1357 struct timeval timeout
;
1362 // for after responding to long(?) TCP event
1366 if (g_sTCPCancel
!= -1) close(g_sTCPCancel
);
1367 sMax
= g_sTCPCancel
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
1368 if (sMax
< g_sTCP
) sMax
= g_sTCP
;
1371 FD_SET(g_sTCP
, &readfds
);
1372 FD_SET(g_sTCPCancel
, &readfds
);
1373 iRet
= select(sMax
+1, &readfds
, NULL
, NULL
, NULL
);
1377 //TracePrint(ELL_TRACE, "UPnP Event select failed (%d)\n", errno);
1381 recvaddrlen
= sizeof(recvaddr
);
1382 sEvent
= accept(g_sTCP
, (struct sockaddr
*)&recvaddr
, &recvaddrlen
);
1383 // not likely - (system's descriptor/file table full)
1384 if (sEvent
<= 0) continue;
1386 ////TracePrint(ELL_TRACE, "UPnP receiving event..\n");
1388 // read all we could from this event
1394 FD_SET(sEvent
, &readfds
);
1396 timeout
.tv_usec
= 400000; // long cause we're dealing with input
1397 iRet
= select(sEvent
+1, &readfds
, NULL
, NULL
, &timeout
);
1408 iRet
= recv(sEvent
, buf
+ iBufLen
, MAX_SOAPMSGSIZE
- iBufLen
, 0);
1411 // something is wrong
1424 iTemp
= send(sEvent
, HTTP200OK
, HTTP200OKLEN
, 0);
1425 shutdown(sEvent
, 1);
1430 // now send 200 OK and be done
1433 ////TracePrint(ELL_TRACE, "UPnP event (%d) received (%d)\n", g_fExpectEvent, iBufLen);
1435 // and parse the XML here.
1436 if (iBufLen
< MAX_SOAPMSGSIZE
)
1438 buf
[iBufLen
] = '\0';
1439 // for now do nothing
1443 buf
[MAX_SOAPMSGSIZE
- 1] = '\0';
1448 //TracePrint(ELL_TRACE, "UPnP: TCPProc end\n");
1451 g_fEventEnabled
= FALSE
;
1452 if (g_sTCPCancel
!= -1) close(g_sTCPCancel
);
1457 static void *UDPProc(void *in
)
1459 // char fLoop = 0; // false - don't send copy to self
1460 // int iTTL = SSDP_TTL;
1462 // struct ip_mreq mreq;
1463 // struct sockaddr_in saddr;
1464 unsigned char buf
[65536];
1465 // FILE *log = g_log;
1466 static time_t last_getdevicedesc_t
= 0;
1469 pthread_mutex_lock(&g_xUPnP
);
1470 gettimeofday(&g_tvUPnPInitTime
, NULL
);
1471 pthread_mutex_unlock(&g_xUPnP
);
1475 struct sockaddr_in recvaddr
;
1478 //struct timeval timeout;
1482 if (g_sUDPCancel
< g_sUDP
) sMax
= g_sUDP
;
1483 else sMax
= g_sUDPCancel
;
1486 FD_SET(g_sUDP
, &readfds
);
1487 FD_SET(g_sUDPCancel
, &readfds
);
1488 iRet
= select(sMax
+1, &readfds
, NULL
, NULL
, NULL
);
1494 close(g_sUDPCancel
);
1502 if (!FD_ISSET(g_sUDP
, &readfds
)) continue;
1503 recvaddrlen
= sizeof(recvaddr
);
1504 n
= recvfrom(g_sUDP
, buf
, sizeof(buf
), 0,
1505 (struct sockaddr
*)&recvaddr
, &recvaddrlen
);
1507 if (g_fLogging
& NALOG_ERROR
)
1508 fprintf(g_log
, "recv failed (%d)\n", errno
);
1510 close(g_sUDPCancel
);
1516 if (strncmp(buf
, "HTTP/1.1", 8) == 0) {
1517 PHTTPResponse pResponse
= NewHTTPResponse_sz(buf
, n
, TRUE
);
1518 PrintHTTPResponse(pResponse
);
1519 if (DiscoverRouter(pResponse
) == 0)
1521 time_t now
= time(NULL
);
1522 if (!g_fControlURLSet
||
1523 ((now
- last_getdevicedesc_t
) > 5))
1525 GetDeviceDescription();
1527 last_getdevicedesc_t
= now
;
1530 DeleteHTTPResponse(pResponse
);
1532 else if (strncmp(buf
, "NOTIFY * HTTP/1.1", 7) == 0) {
1533 // temporarily use this to fudge - will have the exact same
1534 // parsing, only status/reason set to "*" and "HTTP/1.1".
1535 // TODO: add support for HTTP requests
1536 PHTTPResponse pResponse
= NewHTTPResponse_sz(buf
, n
, TRUE
);
1537 if (DiscoverRouter(pResponse
) == 0)
1539 time_t now
= time(NULL
);
1540 if (!g_fControlURLSet
||
1541 ((now
- last_getdevicedesc_t
) > 5))
1543 GetDeviceDescription();
1545 last_getdevicedesc_t
= now
;
1548 DeleteHTTPResponse(pResponse
);
1551 if (g_fLogging
& NALOG_DUMP
)
1552 fprintf(g_log
, "(%ld) Buffer: \n[%s]\n", time(NULL
), buf
);
1561 static void SendUDPMsg(const char *msg
) {
1562 struct sockaddr_in saSendTo
;
1566 bzero(&saSendTo
, sizeof(saSendTo
));
1567 saSendTo
.sin_family
= AF_INET
;
1568 saSendTo
.sin_addr
.s_addr
= inet_addr(SSDP_IP
);
1569 saSendTo
.sin_port
= htons(SSDP_PORT
);
1573 if (g_fLogging
& NALOG_DUMP
)
1574 fprintf(g_log
, "SendUDP: [%s]\n", msg
);
1576 iRet
= sendto(g_sUDP
, msg
, iLen
, 0,
1577 (struct sockaddr
*)&saSendTo
, sizeof(saSendTo
));
1581 if (g_fLogging
& NALOG_ALERT
)
1583 "SendUDPMsg: iRet(%d) != strlen(msg)(%d)! (errno %d)\n",
1587 // strstr, case insensitive, and is limited by len
1588 static char *strcasestr_n(const char *big
, const char *little
, int len
)
1595 if (little
== NULL
) return (char *)big
;
1596 if (big
== NULL
) return NULL
;
1598 //bigLen = strlen(big);
1600 littleLen
= strlen(little
);
1602 if (bigLen
< littleLen
) return NULL
;
1604 end
= bigLen
- littleLen
;
1605 for (i
= 0; i
<= end
; (i
++), (big
++)) {
1606 if (strncasecmp(big
, little
, littleLen
) == 0)
1613 // this is strnstr, only portable
1614 static char *strstr_n(const char *big
, const char *little
, int len
)
1619 (void)len
; // unused
1621 if ((big
== NULL
) || (little
== NULL
)) return NULL
;
1623 iBigLen
= strlen(big
);
1624 iLittleLen
= strlen(little
);
1626 // this part is basically strnstr, except this is portable
1628 if (iBigLen
< iLittleLen
)
1630 if (strncmp(big
, little
, iLittleLen
) == 0)
1637 // returns -1 for "not found"
1638 static int FindContentLength(char *pbuf
, int iLen
)
1640 // non reusable HTTP header parsing code:
1641 // ----------------------------------------------
1645 // find content length header
1646 p
= strcasestr_n(pbuf
, "\r\nContent-Length:", iLen
);
1647 if (p
== NULL
) return -1;
1649 p
+= sizeof("\r\nContent-Length:") - 1; // minus '\0'
1654 // ----------------------------------------------
1657 // returns -1 for "not found"
1658 static int FindBody(char *pbuf
, int iLen
)
1660 // non reusable HTTP header parsing code:
1661 // ----------------------------------------------
1665 // find the empty line
1666 p
= strstr_n(pbuf
, "\r\n\r\n", iLen
);
1667 if (p
== NULL
) return -1;
1669 p
+= sizeof("\r\n\r\n") - 1; // minus '\0'
1672 // ----------------------------------------------
1675 static int SendTCPMsg_saddr_2part(
1676 char *msg
, int iLen
,
1677 char *msg2
, int iLen2
,
1678 char *result
, int resultSize
,
1679 struct sockaddr_in
*saHost
)
1682 struct sockaddr_in saSendTo
;
1691 struct timeval tv_start
;
1692 struct timeval tv_end
;
1693 struct timeval tv_elapsed
;
1695 int iContentLength
= -1;
1696 int iBodyOffset
= -1;
1698 gettimeofday(&tv_start
, NULL
);
1700 if (g_fUPnPEnabled
!= TRUE
) {
1701 //TracePrint(ELL_TRACE, "UPnP not enabled\n");
1702 if (g_fLogging
& NALOG_ERROR
)
1703 fprintf(g_log
, "UPnP not enabled (no UPnP device found yet)\n");
1704 return NA_E_NOT_AVAILABLE
;
1707 s
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
1709 if (g_fLogging
& NALOG_ERROR
)
1710 fprintf(g_log
, "Can't get TCP socket (%d)\n", errno
);
1715 if (setsockopt(s
, IPPROTO_IP
, TCP_NODELAY
, &fND
, sizeof(fND
)) != 0) {
1716 if (g_fLogging
& NALOG_ERROR
)
1717 fprintf(g_log
, "SendTCPMsg/2part: Can't set TCP_NODELAY option!\n");
1718 iRetcode
= NA_E_NET
;
1723 fcntl_flags
= fcntl(s
, F_GETFL
, 0);
1724 fcntl_flags
|= O_NONBLOCK
;
1725 if (fcntl(s
, F_SETFL
, fcntl_flags
) != 0) {
1726 if (g_fLogging
& NALOG_ERROR
)
1727 fprintf(g_log
, "SendTCPMsg/2part: Can't set O_NONBLOCK option!\n");
1728 iRetcode
= NA_E_NET
;
1733 memcpy(&saSendTo
, &g_saddrRouterDesc
, sizeof(saSendTo
));
1735 memcpy(&saSendTo
, saHost
, sizeof(saSendTo
));
1737 iRet
= connect(s
, (struct sockaddr
*) &saSendTo
, sizeof(saSendTo
));
1738 if ((iRet
< 0) && (errno
!= EINPROGRESS
)) {
1739 //TracePrint(ELL_TRACE, "UPnP connect failed\n");
1740 if (g_fLogging
& NALOG_ERROR
)
1741 fprintf(g_log
, "SendTCPMsg/2part: connect failed (%d)\n", errno
);
1742 iRetcode
= NA_E_NET
;
1746 if (g_fLogging
& NALOG_INFO1
)
1748 "- Before Sending TCP Msg1: %d == %lu?\n", iLen
, strlen(msg
));
1749 if (g_fLogging
& NALOG_DUMP
)
1750 fprintf(g_log
, "Sending TCP msg part 1:\n[%s]\n", msg
);
1752 tv
.tv_sec
= g_iFunctionTimeout
/ UPNP_TIMEOUT_GRANULARITY
;
1753 tv
.tv_usec
= (g_iFunctionTimeout
% U_TOGRAN
) * 1000000 / U_TOGRAN
;
1755 FD_SET(s
, &writefds
);
1756 iRet
= select(s
+1, 0, &writefds
, 0, &tv
);
1758 if (g_fLogging
& NALOG_ERROR
)
1759 fprintf(g_log
, "SendTCPMsg/2part: select failed (%d)\n", errno
);
1760 iRetcode
= NA_E_NET
;
1764 if (g_fLogging
& NALOG_ERROR
)
1765 fprintf(g_log
, "SendTCPMsg/2part: select timed out\n");
1766 iRetcode
= NA_E_TIMEOUT
;
1767 gettimeofday(&tv_end
, NULL
);
1768 GetTimeElapsed(&tv_start
, &tv_end
, &tv_elapsed
);
1769 //TracePrint(ELL_TRACE, "UPnP 2part: timeout @1st after %lu.%06lu secs\n",
1770 // tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1774 iRet
= send(s
, msg
, iLen
, 0);
1777 if (g_fLogging
& NALOG_ALERT
)
1778 fprintf(g_log
, "SendTCPMsg/2part: iRet(%d) != strlen(msg)(%d)!\n",
1781 //TracePrint(ELL_TRACE, "UPnP 2part: 1st %d == %d (%d) (%d)?\n", iRet, iLen, strlen(msg), errno);
1783 tv
.tv_sec
= g_iFunctionTimeout
/ UPNP_TIMEOUT_GRANULARITY
;
1784 tv
.tv_usec
= (g_iFunctionTimeout
% U_TOGRAN
) * 1000000 / U_TOGRAN
;
1786 FD_SET(s
, &writefds
);
1787 // calculate how much time elapsed
1788 gettimeofday(&tv_end
, NULL
);
1789 GetTimeElapsed(&tv_start
, &tv_end
, &tv_elapsed
);
1790 if (CompareTime(&tv_elapsed
, &tv
) > 0) {
1792 return NA_E_TIMEOUT
;
1797 // subtract that from timeout accordingly
1798 tv
.tv_sec
-= tv_elapsed
.tv_sec
;
1799 if (tv
.tv_usec
< tv_elapsed
.tv_usec
) {
1801 tv
.tv_usec
= 1000000 + tv
.tv_usec
- tv_elapsed
.tv_usec
;
1804 tv
.tv_usec
= tv
.tv_usec
- tv_elapsed
.tv_usec
;
1806 iRet
= select(s
+1, 0, &writefds
, 0, &tv
);
1808 if (g_fLogging
& NALOG_ERROR
)
1809 fprintf(g_log
, "SendTCPMsg/2part: select2 failed (%d)\n", errno
);
1810 iRetcode
= NA_E_NET
;
1814 if (g_fLogging
& NALOG_ERROR
)
1815 fprintf(g_log
, "SendTCPMsg/2part: select2 timed out\n");
1816 iRetcode
= NA_E_TIMEOUT
;
1817 gettimeofday(&tv_end
, NULL
);
1818 GetTimeElapsed(&tv_start
, &tv_end
, &tv_elapsed
);
1819 //TracePrint(ELL_TRACE, "UPnP 2part: timeout @2nd after %lu.%06lu secs\n",
1820 // tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1824 iRet
= send(s
, msg2
, iLen2
, 0);
1825 if (g_fLogging
& NALOG_INFO1
)
1827 "SendTCPMsg/parse: Before Sending TCP Msg2: %d == %lu?\n",
1828 iLen2
, strlen(msg2
));
1829 if (g_fLogging
& NALOG_DUMP
)
1830 fprintf(g_log
, "Sending TCP msg part 2:\n[%s]\n", msg2
);
1832 //TracePrint(ELL_TRACE, "UPnP 2part: 2nd %d == %d (%d) (%d)?\n", iRet, iLen2, strlen(msg2), errno);
1836 if (g_fLogging
& NALOG_ALERT
)
1837 fprintf(g_log
, "SendTCPMsg/2part: iRet(%d) != strlen(msg2)(%d)!\n",
1840 if (result
== NULL
) { // if caller just want to send/display msgs
1841 if (g_fLogging
& NALOG_DUMP
)
1842 fprintf(g_log
, "TCP Buffer: [");
1845 if (g_fLogging
& NALOG_INFO1
)
1846 fprintf(g_log
, "start recv @%lu\n", time(NULL
));
1849 iContentLength
= -1;
1853 struct timeval timeout
;
1857 FD_SET(s
, &readfds
);
1858 //timeout.tv_sec = g_iFunctionTimeout / U_TOGRAN;
1859 //timeout.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN;
1860 // just do flat 2 sec now, since connection already established
1862 timeout
.tv_usec
= 0;
1865 iRet
= select(s
+1, &readfds
, NULL
, NULL
, &timeout
);
1868 //TracePrint(ELL_TRACE, "UPnP 2part: select timeout? (%d, %d)\n",
1873 //gettimeofday(&tv_end, NULL);
1874 //GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed);
1875 //fprintf(stderr, "2 == loop: %d.%06d\n", tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1877 // if only sending messages
1878 if (result
== NULL
) {
1880 i
= recv(s
, t
, 1000-1, 0); // leave room for '\0' for dump
1882 if (g_fLogging
& NALOG_DUMP
) {
1884 fprintf(g_log
, "%s", t
);
1889 // EO result buf: discard extra bytes
1890 if (resultSize
<= iBufLen
) {
1892 i
= recv(s
, &t
, 1000, 0);
1894 // Note that there's no dump here - prevents DoS attack from
1895 // flooding the logs/diskspace
1899 i
= recv(s
, result
+ iBufLen
, resultSize
- iBufLen
, 0);
1901 //TracePrint(ELL_TRACE, "UPnP 2part: recv done %d (%d, %d)\n",
1902 // iBufLen, i, errno);
1908 // parse and see if we can find content-length to quit early
1909 iContentLength
= FindContentLength(result
, iBufLen
);
1911 // now if we're still in header, see if we can find body
1912 iBodyOffset
= FindBody(result
, iBufLen
);
1914 // now check if we can leave early. conditions are:
1915 // past headers, and we've already recv'ed content-length of body
1916 if ((iBodyOffset
>= 0) &&
1917 (iContentLength
>= 0) &&
1918 ((iBufLen
- iBodyOffset
) >= iContentLength
))
1920 //TracePrint(ELL_TRACE, "UPnP 2part: read all specified %d (%d, %d) (%d, %d)\n",
1921 // iBufLen, i, errno, iBodyOffset, iContentLength);
1926 //fprintf(stderr, "2 -- \n");
1928 if (g_fLogging
& NALOG_INFO1
)
1929 fprintf(g_log
, "done recv @%lu\n", time(NULL
));
1931 if (result
== NULL
) { // if caller just want to send/display msgs
1932 if (g_fLogging
& NALOG_DUMP
)
1933 fprintf(g_log
, "]\n");
1944 static int SendTCPMsg_saddr_parse(
1945 char *msg
, int iLen
,
1946 char *result
, int resultSize
,
1947 struct sockaddr_in
*saHost
)
1950 struct sockaddr_in saSendTo
;
1957 struct timeval tv_start
;
1958 // struct timeval tv_end;
1959 // struct timeval tv_elapsed;
1961 // HTTP parsing vars
1969 select(0, NULL
, NULL
, NULL
, &tv
);
1971 pthread_mutex_lock(&g_xUPnPMsg
);
1973 gettimeofday(&tv_start
, NULL
);
1975 if (g_fUPnPEnabled
!= TRUE
) {
1976 //TracePrint(ELL_TRACE, "UPnP not enabled\n");
1977 if (g_fLogging
& NALOG_ERROR
)
1978 fprintf(g_log
, "UPnP not enabled (no UPnP device found yet)\n");
1979 pthread_mutex_unlock(&g_xUPnPMsg
);
1980 return NA_E_NOT_AVAILABLE
;
1983 s
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
1985 if (g_fLogging
& NALOG_ERROR
)
1986 fprintf(g_log
, "Can't get TCP socket (%d)\n", errno
);
1987 pthread_mutex_unlock(&g_xUPnPMsg
);
1992 fcntl_flags
= fcntl(s
, F_GETFL
, 0);
1993 fcntl_flags
|= O_NONBLOCK
;
1994 if (fcntl(s
, F_SETFL
, fcntl_flags
) != 0) {
1995 if (g_fLogging
& NALOG_ERROR
)
1996 fprintf(g_log
, "SendTCPMsg/parse: Can't set O_NONBLOCK option!\n");
1998 pthread_mutex_unlock(&g_xUPnPMsg
);
2003 memcpy(&saSendTo
, &g_saddrRouterDesc
, sizeof(saSendTo
));
2005 memcpy(&saSendTo
, saHost
, sizeof(saSendTo
));
2007 iRet
= connect(s
, (struct sockaddr
*) &saSendTo
, sizeof(saSendTo
));
2008 if ((iRet
< 0) && (errno
!= EINPROGRESS
)) {
2009 //TracePrint(ELL_TRACE, "UPnP connect failed\n");
2010 if (g_fLogging
& NALOG_ERROR
)
2011 fprintf(g_log
, "SendTCPMsg/parse: connect failed (%d)\n", errno
);
2013 pthread_mutex_unlock(&g_xUPnPMsg
);
2017 if (g_fLogging
& NALOG_INFO1
)
2018 fprintf(g_log
, "SendTCPMsg/parse: Before Sending TCP Msg: %d == %lu?\n",
2020 if (g_fLogging
& NALOG_DUMP
)
2021 fprintf(g_log
,"Sending TCP msg:\n[%s]\n", msg
);
2023 tv
.tv_sec
= g_iFunctionTimeout
/ UPNP_TIMEOUT_GRANULARITY
;
2024 tv
.tv_usec
= (g_iFunctionTimeout
% U_TOGRAN
) * 1000000 / U_TOGRAN
;
2026 FD_SET(s
, &writefds
);
2027 iRet
= select(s
+1, 0, &writefds
, 0, &tv
);
2029 if (g_fLogging
& NALOG_ERROR
)
2030 fprintf(g_log
, "SendTCPMsg/parse: select failed (%d)\n", errno
);
2032 pthread_mutex_unlock(&g_xUPnPMsg
);
2036 if (g_fLogging
& NALOG_ERROR
)
2037 fprintf(g_log
, "SendTCPMsg/parse: select timed out\n");
2039 pthread_mutex_unlock(&g_xUPnPMsg
);
2040 return NA_E_TIMEOUT
;
2043 iRet
= send(s
, msg
, iLen
, 0);
2047 if (g_fLogging
& NALOG_ALERT
)
2048 fprintf(g_log
, "SendTCPMsg: iRet (%d) != strlen(msg) (%d)!\n",
2051 if (result
== NULL
) { // if caller just want to send/display msgs
2052 if (g_fLogging
& NALOG_DUMP
)
2053 fprintf(g_log
, "TCP Buffer: [");
2056 if (g_fLogging
& NALOG_INFO1
)
2057 fprintf(g_log
, "start recv @%lu\n", time(NULL
));
2061 iContentLength
= -1;
2065 struct timeval timeout
;
2069 FD_SET(s
, &readfds
);
2070 //timeout.tv_sec = g_iFunctionTimeout / U_TOGRAN;
2071 //timeout.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN;
2072 // just do flat 2 sec now, since connection already established
2074 timeout
.tv_usec
= 0;
2076 iRet
= select(s
+1, &readfds
, NULL
, NULL
, &timeout
);
2078 //fprintf(stderr, "**********: select failed (%d/%d)\n", iRet, errno);
2082 //gettimeofday(&tv_end, NULL);
2083 //GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed);
2084 //fprintf(stderr, "p == loop: %d.%06d\n", tv_elapsed.tv_sec, tv_elapsed.tv_usec);
2086 // if only sending messages
2087 if (result
== NULL
) {
2089 i
= recv(s
, t
, 1000-1, 0); // leave room for '\0' for dump
2091 if (g_fLogging
& NALOG_DUMP
) {
2093 fprintf(g_log
, "%s", t
);
2098 // EO result buf: discard extra bytes
2099 if (resultSize
<= iBufLen
) {
2101 i
= recv(s
, &t
, 1000, 0);
2103 // Note that there's no dump here - prevents DoS attack from
2104 // flooding the logs/diskspace
2108 i
= recv(s
, result
+ iBufLen
, resultSize
- iBufLen
, 0);
2114 if (EAGAIN
== errno
) continue;
2120 // parse and see if we can find content-length to quit early
2121 iContentLength
= FindContentLength(result
, iBufLen
);
2123 // now if we're still in header, see if we can find body
2124 iBodyOffset
= FindBody(result
, iBufLen
);
2128 //fprintf(stderr, "p -- \n");
2130 if (g_fLogging
& NALOG_INFO1
)
2131 fprintf(g_log
, "done recv @%lu\n", time(NULL
));
2133 if (result
== NULL
) { // if caller just want to send/display msgs
2134 if (g_fLogging
& NALOG_DUMP
)
2135 fprintf(g_log
, "]\n");
2139 pthread_mutex_unlock(&g_xUPnPMsg
);
2145 // szSOAPMsgControlAHeaderFMT - 4 args (ctrl_url, host/port, action, length)
2146 // szSOAPMsgControlABodyFMT - 2 args (action, args string)
2147 // szSOAPMsgControlAArgumentFMT - 2 args (name/value)
2148 static PHTTPResponse
SendSOAPMsgControlAction(
2154 //char outBuffer[65536];
2155 //char outBufferBody[65536];
2156 //char outBufferArgs[65536];
2157 char *outBuffer
= NULL
;
2158 char *outBufferBody
= NULL
;
2159 char *outBufferArgs
= NULL
;
2160 char *inBuffer
= NULL
;
2168 PHTTPResponse pResponse
= NULL
;
2171 if (!WaitUPnPFunction())
2174 if ((outBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2175 if (g_fLogging
& NALOG_ERROR
)
2176 fprintf(g_log
, "can't malloc for outBuffer\n");
2179 if ((outBufferBody
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2180 if (g_fLogging
& NALOG_ERROR
)
2181 fprintf(g_log
, "can't malloc for outBufferBody\n");
2184 if ((outBufferArgs
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2185 if (g_fLogging
& NALOG_ERROR
)
2186 fprintf(g_log
, "can't malloc for outBufferArgs\n");
2189 if ((inBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2190 if (g_fLogging
& NALOG_ERROR
)
2191 fprintf(g_log
, "can't malloc for inBuffer\n");
2197 for (i
=0; i
<argc
; i
++) {
2199 if (args
[i
].pszType
== NULL
) {
2200 n
= sprintf(outBufferArgs
+ iArgsLen
,
2201 szSOAPMsgControlAArgumentFMT
,
2202 args
[i
].pszName
, args
[i
].pszValue
);
2205 n
= sprintf(outBufferArgs
+ iArgsLen
,
2206 szSOAPMsgControlAArgumentFMT_t
,
2207 args
[i
].pszName
, args
[i
].pszValue
, args
[i
].pszType
);
2211 outBufferArgs
[iArgsLen
] = '\0';
2213 iBodyLen
= sprintf(outBufferBody
, szSOAPMsgControlABodyFMT
,
2214 action
, outBufferArgs
);
2216 iHeaderLen
= sprintf(outBuffer
, szSOAPMsgControlAHeaderFMT
,
2217 g_szControlURL
, g_szRouterHostPortSOAP
, action
, iBodyLen
);
2220 DumpHex(outBuffer
, iHeaderLen
+1);
2221 DumpHex(outBufferBody
, iBodyLen
+1);
2222 iResultLen
= SendTCPMsg_saddr_2part(
2223 outBuffer
, iHeaderLen
,
2224 outBufferBody
, iBodyLen
,
2225 inBuffer
, MAX_SOAPMSGSIZE
,
2226 &g_saddrRouterSOAP
);
2229 strcpy(outBuffer
+ iHeaderLen
, outBufferBody
);
2230 iLen
= iHeaderLen
+ iBodyLen
;
2232 DumpHex(outBuffer
, iLen
+1);
2234 //strcat(outBuffer, CRLF "0" CRLF CRLF);
2237 iResultLen
= SendTCPMsg_saddr_parse(
2239 inBuffer
, MAX_SOAPMSGSIZE
,
2240 &g_saddrRouterSOAP
);
2243 if (iResultLen
> 0) {
2244 if (iResultLen
> MAX_SOAPMSGSIZE
) {
2245 if (g_fLogging
& NALOG_ALERT
)
2246 fprintf(g_log
, "result truncated..\n");
2247 iResultLen
= MAX_SOAPMSGSIZE
;
2249 pResponse
= NewHTTPResponse_sz(inBuffer
, iResultLen
, FALSE
);
2250 if (pResponse
!= NULL
) {
2251 PrintHTTPResponse(pResponse
);
2252 //DeleteHTTPResponse(pResponse);
2253 // - return response to caller
2257 if (g_fLogging
& NALOG_ERROR
)
2258 fprintf(g_log
, "No TCP Response\n");
2259 //TracePrint(ELL_TRACE, "UPnP SendSOAPMsg got no TCP response (%d)\n",
2264 if (outBuffer
!= NULL
) free(outBuffer
);
2265 if (outBufferBody
!= NULL
) free(outBufferBody
);
2266 if (outBufferArgs
!= NULL
) free(outBufferArgs
);
2267 if (inBuffer
!= NULL
) free(inBuffer
);
2272 static int FindURLBase(char *pbuf
, int iLen
, char *szURLBase
)
2274 // non reusable XML parsing code:
2275 // ----------------------------------------------
2279 // now skip after end of this tag, then skip until controlURL tag
2280 p
= strstr_n(pbuf
, "<URLBase>", iLen
);
2281 if (p
== NULL
) return -1;
2283 // skip to the actual stuff
2284 p
+= sizeof("<URLBase>") - 1; // minus '\0'
2286 // skip white spaces (just in case)
2290 // copy into szURLBase
2291 while ((*p
!= '\0') && (*p
!= '<') && !isspace(*p
)) {
2292 if (i
++ > 1000) break;
2300 // ----------------------------------------------
2304 static int FindDescInfo(
2307 const char *szParentName
,
2316 // find the device within pbuf
2328 // now skip after end of this tag, then skip until manufacturer tag
2329 iSearchLen
= sprintf(szSearch
, "<%s>", szName
);
2330 p
= strstr_n(pbuf
, szSearch
, iLen
);
2331 if (p
== NULL
) return -1;
2334 // skip white spaces (just in case)
2338 // copy into szValue
2339 while ((*p
!= '\0') && (*p
!= '<')) {
2340 if (i
++ > 1000) break;
2350 static int FindIGDInfo(char *pbuf
, int iLen
, const char *szName
, char *szValue
)
2352 return FindDescInfo(
2354 "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
2358 static int FindManufacturer(char *pbuf
, int iLen
, char *szManuf
)
2360 return FindIGDInfo(pbuf
, iLen
, "manufacturer", szManuf
);
2363 static int FindFriendlyName(char *pbuf
, int iLen
, char *szValue
)
2365 return FindIGDInfo(pbuf
, iLen
, "friendlyName", szValue
);
2368 static int FindModelName(char *pbuf
, int iLen
, char *szValue
)
2370 return FindIGDInfo(pbuf
, iLen
, "modelName", szValue
);
2373 static int FindModelDescription(char *pbuf
, int iLen
, char *szValue
)
2375 return FindIGDInfo(pbuf
, iLen
, "modelDescription", szValue
);
2378 static int FindWANIPInfo(char *pbuf
, int iLen
, const char *szName
, char *szValue
)
2380 return FindDescInfo(
2382 "urn:schemas-upnp-org:service:WANIPConnection:1",
2386 static int FindControlURL(char *pbuf
, int iLen
, char *szControlURL
)
2388 return FindWANIPInfo(pbuf
, iLen
, "controlURL", szControlURL
);
2391 static int FindEventURL(char *pbuf
, int iLen
, char *szEventURL
)
2393 return FindWANIPInfo(pbuf
, iLen
, "eventSubURL", szEventURL
);
2396 static int FindRouterInfo(char *inBuffer
, int iLen
)
2398 if (FindManufacturer(inBuffer
, iLen
, g_szManufacturer
) != 0)
2399 g_szManufacturer
[0] = '\0';
2401 if (FindFriendlyName(inBuffer
, iLen
, g_szFriendlyName
) != 0)
2402 g_szFriendlyName
[0] = '\0';
2404 if (FindModelName(inBuffer
, iLen
, g_szModelName
) != 0)
2405 g_szModelName
[0] = '\0';
2407 if (FindModelDescription(inBuffer
, iLen
, g_szModelDescription
) != 0)
2408 g_szModelDescription
[0] = '\0';
2410 //TracePrint(ELL_TRACE,
2411 // "UPnP Router Info:\n"
2412 // " - manufacturer [%s]\n"
2413 // " - friendly name [%s]\n"
2414 // " - model name [%s]\n"
2415 // " - model desc [%s]\n",
2416 // g_szManufacturer, g_szFriendlyName, g_szModelName, g_szModelDescription);
2421 static void ParseURL(
2422 const char *szBuf
, char *pszHostPort
,
2423 struct sockaddr_in
*psaddr
, char *pszPath
)
2428 unsigned short port
;
2433 if (0 == strncmp(p
, "http://", 7))
2449 // find the port separetor
2455 // HTTP's by default port 80, so don't have it in the "Host:" header
2456 if (80 == port
) *q
= '\0';
2459 if (pszHostPort
) strcpy(pszHostPort
, p
);
2461 if (NULL
!= q
) *q
= '\0';
2463 if (NULL
!= psaddr
) {
2464 psaddr
->sin_family
= AF_INET
;
2465 psaddr
->sin_addr
.s_addr
= inet_addr(p
);
2466 psaddr
->sin_port
= htons(port
);
2469 //TracePrint(ELL_TRACE, "ParseURL [%s] -> [%s][%s] %lu.%lu.%lu.%lu:%u\n",
2471 pszHostPort
?pszHostPort
:"",
2473 (psaddr
->sin_addr
.s_addr
>> 24) & 0xff,
2474 (psaddr
->sin_addr
.s_addr
>> 16) & 0xff,
2475 (psaddr
->sin_addr
.s_addr
>> 8) & 0xff,
2476 (psaddr
->sin_addr
.s_addr
>> 0) & 0xff,
2481 static void GetDeviceDescription(void)
2483 char *outBuffer
= NULL
;
2484 char *inBuffer
= NULL
;
2487 char szURLBase
[1024];
2488 char szControlURL
[1024];
2489 char szEventURL
[1024];
2491 if (!g_fUPnPEnabled
) {
2492 if (g_fLogging
& NALOG_ERROR
)
2493 fprintf(g_log
, "GetDeviceDescription: upnp not enabled\n");
2497 if ((outBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2498 if (g_fLogging
& NALOG_ERROR
)
2499 fprintf(g_log
, "can't malloc for outBuffer\n");
2502 if ((inBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2503 if (g_fLogging
& NALOG_ERROR
)
2504 fprintf(g_log
, "can't malloc for inBuffer\n");
2508 iBufLen
= sprintf(outBuffer
, szSSDPMsgDescribeDeviceFMT
, g_szNATDevDescURL
,
2509 g_szRouterHostPortDesc
);
2511 if (g_fLogging
& NALOG_INFO1
)
2512 fprintf(g_log
, "Describe Device: [%s]\n", outBuffer
);
2513 iLen
= SendTCPMsg_saddr_parse(outBuffer
, iBufLen
, inBuffer
, MAX_SOAPMSGSIZE
,
2514 &g_saddrRouterDesc
);
2516 g_fControlURLSet
= FALSE
;
2518 if (FindControlURL(inBuffer
, iLen
, szControlURL
) != 0) {
2519 if (g_fLogging
& NALOG_ERROR
)
2520 fprintf(g_log
, "GetDeviceDesc: can't find control URL\n");
2524 // start modifying global
2525 pthread_mutex_lock(&g_xUPnP
);
2528 // now see if there's the URLBase
2529 if (FindURLBase(inBuffer
, iLen
, szURLBase
) != 0) {
2530 // not there? try default numbers from device description
2531 memcpy(&g_saddrRouterBase
, &g_saddrRouterDesc
,
2532 sizeof(g_saddrRouterBase
));
2533 strcpy(g_szRouterHostPortBase
, g_szRouterHostPortDesc
);
2537 g_szRouterHostPortBase
, &g_saddrRouterBase
, NULL
);
2539 if ((strlen(g_szRouterHostPortBase
) == 0) ||
2540 (g_saddrRouterBase
.sin_addr
.s_addr
== INADDR_NONE
)) {
2541 memcpy(&g_saddrRouterBase
, &g_saddrRouterDesc
,
2542 sizeof(g_saddrRouterBase
));
2543 strcpy(g_szRouterHostPortBase
, g_szRouterHostPortDesc
);
2548 ParseURL(szControlURL
,
2549 g_szRouterHostPortSOAP
, &g_saddrRouterSOAP
, g_szControlURL
);
2550 if ((strlen(g_szRouterHostPortSOAP
) == 0) ||
2551 (g_saddrRouterSOAP
.sin_addr
.s_addr
== INADDR_NONE
)) {
2552 memcpy(&g_saddrRouterSOAP
, &g_saddrRouterBase
,
2553 sizeof(g_saddrRouterSOAP
));
2554 strcpy(g_szRouterHostPortSOAP
, g_szRouterHostPortBase
);
2558 ////TracePrint(ELL_TRACE, "UPnP Control URL set to[%s][%s]...\n",
2559 // g_szRouterHostPortSOAP, g_szControlURL);
2561 g_fControlURLSet
= TRUE
;
2562 gettimeofday(&g_tvLastUpdateTime
, NULL
);
2563 pthread_cond_broadcast(&g_condUPnPControlURL
);
2565 if (g_fLogging
& NALOG_INFO1
)
2566 fprintf(g_log
, "Got Device Description\n");
2569 FindRouterInfo(inBuffer
, iLen
);
2571 if (FindEventURL(inBuffer
, iLen
, szEventURL
) != 0) {
2572 szEventURL
[0] = '\0';
2575 ParseURL(szEventURL
,
2576 g_szRouterHostPortEvent
, &g_saddrRouterEvent
, g_szEventURL
);
2577 if ((strlen(g_szRouterHostPortEvent
) == 0) ||
2578 (g_saddrRouterEvent
.sin_addr
.s_addr
== INADDR_NONE
)) {
2579 memcpy(&g_saddrRouterEvent
, &g_saddrRouterBase
,
2580 sizeof(g_saddrRouterEvent
));
2581 strcpy(g_szRouterHostPortEvent
, g_szRouterHostPortBase
);
2588 if (outBuffer
!= NULL
) free(outBuffer
);
2589 if (inBuffer
!= NULL
) free(inBuffer
);
2591 pthread_mutex_unlock(&g_xUPnP
);
2595 static void GetIPByName(char *hostname
, unsigned long *ip_ret
)
2599 ip
= inet_addr(hostname
);
2600 if (ip
== INADDR_NONE
) {
2601 struct hostent
*pHEnt
;
2602 pHEnt
= gethostbyname(hostname
);
2603 if (pHEnt
== NULL
) {
2604 if (g_fLogging
& NALOG_ALERT
)
2605 fprintf(g_log
, "Can't translate [%s] to IP...\n", hostname
);
2606 g_dwLocalIP
= htonl(INADDR_ANY
);
2609 ip
= ntohl(*(unsigned long *)(pHEnt
->h_addr
));
2610 if (g_fLogging
& NALOG_INFO1
)
2611 fprintf(g_log
, "hostname [%s] to ip: %ld.%ld.%ld.%ld\n",
2621 static void SetLocalIP()
2623 PIPINFO pIPInfo
= NULL
;
2624 int count
= GetIPInfo(&pIPInfo
);
2625 if (NULL
!= pIPInfo
)
2627 // choose first non IPV6 address
2628 // iterate through array and set port information
2630 unsigned long dwFirst
= 0;
2631 for(i
= 0; i
< count
; i
++)
2633 if (!(pIPInfo
[i
].iFlags
& ISIPV6
) &&
2634 (strncmp(pIPInfo
[i
].szIfName
, "ppp", 3) != 0))
2636 unsigned long dwTemp
;
2638 memcpy(&dwTemp
, pIPInfo
[i
].abIP
, sizeof(unsigned long));
2640 if (0 != GetNATIPNetmask(dwTemp
)) {
2641 g_dwLocalIP
= dwTemp
;
2650 g_dwLocalIP
= dwFirst
;
2651 FreeIPInfo(pIPInfo
);
2656 static int FindTagContent(const char *text
, const char *tagname
, char *buf
)
2660 p
= strstr(text
, tagname
);
2662 if (g_fLogging
& NALOG_INFO0
)
2663 fprintf(g_log
, "FindTagContent: can't find %s\n", tagname
);
2664 return NA_E_PARSE_ERROR
;
2667 if (sscanf(p
, "%*[^>]> %[^ <] <", buf
) < 1) {
2668 if (g_fLogging
& NALOG_INFO0
)
2669 fprintf(g_log
, "FindTagContent: Can't parse tag %s\n", tagname
);
2670 return NA_E_PARSE_ERROR
;
2673 return NA_E_SUCCESS
;
2676 mStatus
LNT_UnmapPort(mDNSIPPort PubPort
, mDNSBool tcp
)
2680 //char szRemoteHost[1024];
2681 //unsigned long dwIP;
2682 Property propArgs
[3];
2684 unsigned short port
= PubPort
.NotAnInteger
;
2685 int protocol
= tcp
? IPPROTO_TCP
: IPPROTO_UDP
;
2686 sprintf(szEPort
, "%u", port
);
2688 bzero(propArgs
, sizeof(propArgs
));
2689 propArgs
[0].pszName
= "NewRemoteHost";
2690 propArgs
[0].pszValue
= "";
2691 propArgs
[0].pszType
= "string";
2692 propArgs
[1].pszName
= "NewExternalPort";
2693 propArgs
[1].pszValue
= szEPort
;
2694 propArgs
[1].pszType
= "ui2";
2695 propArgs
[2].pszName
= "NewProtocol";
2696 if (protocol
== IPPROTO_TCP
) {
2697 propArgs
[2].pszValue
= "TCP";
2699 else if (protocol
== IPPROTO_UDP
) {
2700 propArgs
[2].pszValue
= "UDP";
2705 propArgs
[2].pszType
= "string";
2707 resp
= SendSOAPMsgControlAction(
2708 "DeletePortMapping", 3, propArgs
, FALSE
);
2710 return mStatus_NATTraversal
;
2713 if (strcmp(resp
->pszStatus
, "200") != 0) {
2714 DeleteHTTPResponse(resp
);
2715 return mStatus_NATTraversal
;
2718 DeleteHTTPResponse(resp
);
2719 return mStatus_NoError
;
2723 static int GetMappingUnused(unsigned short eport
, int protocol
);
2725 extern mStatus
LNT_MapPort(mDNSIPPort priv
, mDNSIPPort pub
, mDNSBool tcp
)
2732 Property propArgs
[8];
2734 unsigned short iport
= priv
.NotAnInteger
;
2735 unsigned short eport
= pub
.NotAnInteger
;
2736 int protocol
= tcp
? IPPROTO_TCP
: IPPROTO_UDP
;
2739 if (NA_E_EXISTS
== GetMappingUnused(eport
, protocol
))
2740 return mStatus_AlreadyRegistered
;
2742 //DeletePortMapping(eport, protocol);
2744 sprintf(szEPort
, "%u", eport
);
2746 sprintf(szIPort
, "%u", iport
);
2749 sprintf(szLocalIP
, "%u.%u.%u.%u",
2750 (unsigned int)((dwIP
>> 24) & 0xff),
2751 (unsigned int)((dwIP
>> 16) & 0xff),
2752 (unsigned int)((dwIP
>> 8) & 0xff),
2753 (unsigned int)((dwIP
>> 0) & 0xff));
2755 bzero(propArgs
, sizeof(propArgs
));
2756 propArgs
[0].pszName
= "NewRemoteHost";
2757 propArgs
[0].pszValue
= "";
2758 propArgs
[0].pszType
= "string";
2759 propArgs
[1].pszName
= "NewExternalPort";
2760 propArgs
[1].pszValue
= szEPort
;
2761 propArgs
[1].pszType
= "ui2";
2762 propArgs
[2].pszName
= "NewProtocol";
2763 if (protocol
== IPPROTO_TCP
) {
2764 propArgs
[2].pszValue
= "TCP";
2766 else if (protocol
== IPPROTO_UDP
) {
2767 propArgs
[2].pszValue
= "UDP";
2770 return mStatus_BadParamErr
;
2772 propArgs
[2].pszType
= "string";
2773 propArgs
[3].pszName
= "NewInternalPort";
2774 propArgs
[3].pszValue
= szIPort
;
2775 propArgs
[3].pszType
= "ui2";
2776 propArgs
[4].pszName
= "NewInternalClient";
2777 propArgs
[4].pszValue
= szLocalIP
;
2778 propArgs
[4].pszType
= "string";
2779 propArgs
[5].pszName
= "NewEnabled";
2780 propArgs
[5].pszValue
= "1";
2781 propArgs
[5].pszType
= "boolean";
2782 propArgs
[6].pszName
= "NewPortMappingDescription";
2783 sprintf(descr
, "iC%u", eport
);
2784 //propArgs[6].pszValue = "V";
2785 propArgs
[6].pszValue
= descr
;
2786 propArgs
[6].pszType
= "string";
2787 propArgs
[7].pszName
= "NewLeaseDuration";
2788 propArgs
[7].pszValue
= "0";
2789 propArgs
[7].pszType
= "ui4";
2791 resp
= SendSOAPMsgControlAction(
2792 "AddPortMapping", 8, propArgs
, FALSE
);
2795 return mStatus_NATTraversal
;
2798 if (strcmp(resp
->pszStatus
, "200") != 0) {
2799 DeleteHTTPResponse(resp
);
2800 return mStatus_NATTraversal
;
2803 DeleteHTTPResponse(resp
);
2804 return mStatus_NoError
;
2807 static int GetMappingUnused(unsigned short eport
, int protocol
)
2811 Property propArgs
[3];
2815 sprintf( szPort
, "%u", eport
);
2817 bzero(&propArgs
, sizeof(propArgs
));
2818 propArgs
[0].pszName
= "NewRemoteHost";
2819 propArgs
[0].pszValue
= "";
2820 propArgs
[0].pszType
= "string";
2821 propArgs
[1].pszName
= "NewExternalPort";
2822 propArgs
[1].pszValue
= szPort
;
2823 propArgs
[1].pszType
= "ui2";
2824 propArgs
[2].pszName
= "NewProtocol";
2825 if (protocol
== IPPROTO_TCP
) {
2826 propArgs
[2].pszValue
= "TCP";
2828 else if (protocol
== IPPROTO_UDP
) {
2829 propArgs
[2].pszValue
= "UDP";
2832 return NA_E_INVALID_PARAMETER
;
2834 propArgs
[2].pszType
= "string";
2836 resp
= SendSOAPMsgControlAction(
2837 "GetSpecificPortMappingEntry", 3, propArgs
, FALSE
);
2839 if ((strcmp(resp
->pszStatus
, "200") == 0) &&
2840 (FindTagContent(resp
->pszBody
, "NewInternalClient", buf
) == 0))
2842 GetIPByName(buf
, &ip
);
2843 if (ip
== g_dwLocalIP
) {
2844 // (perhaps we let it go?)
2845 DeleteHTTPResponse(resp
);
2846 return NA_E_SUCCESS
;
2849 DeleteHTTPResponse(resp
);
2853 DeleteHTTPResponse(resp
);
2856 return NA_E_SUCCESS
;
2859 mStatus
LNT_GetPublicIP(mDNSOpaque32
*IpPtr
)
2863 static struct timeval tvLastGoodIP
= {0,0};
2864 static unsigned long dwLastGoodIP
;
2866 unsigned long *ip
= (unsigned long *)IpPtr
;
2867 if (ip
== NULL
) return mStatus_BadParamErr
;
2869 gettimeofday(&tv
, NULL
);
2870 GetTimeElapsed(&tvLastGoodIP
, &tv
, &tv
);
2873 return dwLastGoodIP
;
2876 resp
= SendSOAPMsgControlAction(
2877 "GetExternalIPAddress", 0, NULL
, FALSE
);
2880 return mStatus_NATTraversal
;
2882 if (FindTagContent(resp
->pszBody
, "NewExternalIPAddress", buf
) == 0) {
2883 if (g_fLogging
& NALOG_INFO1
)
2884 fprintf(g_log
, "Mapped remote host = %s\n", buf
);
2885 *ip
= inet_addr(buf
);
2886 DeleteHTTPResponse(resp
);
2888 gettimeofday(&tvLastGoodIP
, NULL
);
2891 return mStatus_NoError
;
2894 DeleteHTTPResponse(resp
);
2895 return mStatus_NATTraversal
;
2898 static void SendDiscoveryMsg()
2900 // do it twice to avoid lost packet
2901 //SendUDPMsg(szSSDPMsgDiscoverNAT);
2902 SendUDPMsg(szSSDPMsgDiscoverRoot
);
2903 SendUDPMsg(szSSDPMsgDiscoverIGD
);
2904 SendUDPMsg(szSSDPMsgDiscoverNAT
);
2907 // Set up threads for upnp responses, etc.
2908 int LegacyNATInit(void)
2910 //pthread_t UDPthread;
2911 pthread_attr_t attr
;
2913 //struct timeval tv;
2915 static int fFirstInitLocks
= TRUE
;
2925 if (fFirstInitLocks
)
2928 if (pthread_mutex_init(&g_xUPnP
, NULL
)) {
2929 if (g_fLogging
& NALOG_ERROR
)
2930 fprintf(log
, "UpnpInit - mutex init failed\n");
2931 return NA_E_INTERNAL_ERROR
;
2933 if (pthread_cond_init(&g_condUPnP
, NULL
)) {
2934 pthread_mutex_destroy(&g_xUPnP
);
2935 if (g_fLogging
& NALOG_ERROR
)
2936 fprintf(log
, "UpnpInit - cond init failed\n");
2937 return NA_E_INTERNAL_ERROR
;
2939 if (pthread_cond_init(&g_condUPnPControlURL
, NULL
)) {
2940 pthread_mutex_destroy(&g_xUPnP
);
2941 pthread_cond_destroy(&g_condUPnP
);
2942 if (g_fLogging
& NALOG_ERROR
)
2943 fprintf(log
, "UpnpInit - cond init failed\n");
2944 return NA_E_INTERNAL_ERROR
;
2946 if (pthread_mutex_init(&g_xUPnPMsg
, NULL
)) {
2947 pthread_mutex_destroy(&g_xUPnP
);
2948 pthread_cond_destroy(&g_condUPnP
);
2949 pthread_cond_destroy(&g_condUPnPControlURL
);
2950 if (g_fLogging
& NALOG_ERROR
)
2951 fprintf(log
, "UpnpInit - mutex init failed\n");
2952 return NA_E_INTERNAL_ERROR
;
2955 fFirstInitLocks
= FALSE
;
2960 // initialize UDP socket for SSDP
2961 g_sUDP
= SSDPListen();
2962 g_sUDPCancel
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
); // sock to signal canccelation to UDP thread
2963 if (g_sUDP
< 0 || g_sUDPCancel
< 0) {
2964 if (g_fLogging
& NALOG_ERROR
)
2965 fprintf(log
, "UpnpInit - Failed to init multicast socket.\n");
2966 return NA_E_INTERNAL_ERROR
;
2970 pthread_attr_init(&attr
);
2971 iRet
= pthread_create(&g_UDPthread
, &attr
, UDPProc
, log
);
2973 g_fFirstInit
= TRUE
; // so we'll redo this part next time
2976 if (g_fLogging
& NALOG_ERROR
)
2977 fprintf(log
, "UpnpInit - pthread create failed (%d)\n", iRet
);
2978 return NA_E_THREAD_ERROR
;
2981 // set this to FALSE only if first call succeeded
2982 g_fFirstInit
= FALSE
;
2984 //TracePrint(ELL_TRACE, "UPnP init passed\n");
2987 //tv.tv_usec = 20000; // wait 20ms for thread/udp/multicast init
2988 //select(0, 0, 0, 0, &tv);
2991 // send discovery message
2994 return NA_E_SUCCESS
;
2997 int LegacyNATDestroy()
2999 void *UDPThreadRetVal
;
3001 if (g_sTCPCancel
>= 0) close(g_sTCPCancel
);
3002 if (g_sUDPCancel
>= 0) close(g_sUDPCancel
);
3003 pthread_join(g_UDPthread
, &UDPThreadRetVal
);
3006 g_fFirstInit
= TRUE
;
3007 g_fUPnPEnabled
= FALSE
;
3008 g_fControlURLSet
= FALSE
;
3009 return NA_E_SUCCESS
;