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.12 2005/07/22 21:36:16 ksekar
28 Fix GCC 4.0/Intel compiler warnings
30 Revision 1.11 2004/12/03 03:34:20 ksekar
31 <rdar://problem/3882674> LegacyNATTraversal.c leaks threads
33 Revision 1.10 2004/12/01 02:43:49 cheshire
34 Update copyright message
36 Revision 1.9 2004/10/27 02:25:05 cheshire
37 <rdar://problem/3816029> Random memory smashing bug
39 Revision 1.8 2004/10/27 02:17:21 cheshire
40 Turn off "safe_close: ERROR" error messages -- there are too many of them
42 Revision 1.7 2004/10/26 21:15:40 cheshire
43 <rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0
44 Additional fixes: Code should set fds to -1 after closing sockets.
46 Revision 1.6 2004/10/26 20:59:20 cheshire
47 <rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0
49 Revision 1.5 2004/10/26 01:01:35 cheshire
50 Use "#if 0" instead of commenting out code
52 Revision 1.4 2004/10/10 06:51:36 cheshire
53 Declared some strings "const" as appropriate
55 Revision 1.3 2004/09/21 23:40:12 ksekar
56 <rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure
58 Revision 1.2 2004/09/17 01:08:52 cheshire
59 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
60 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
61 declared in that file are ONLY appropriate to single-address-space embedded applications.
62 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
64 Revision 1.1 2004/08/18 17:35:41 ksekar
65 <rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways
70 #include "mDNSEmbeddedAPI.h"
71 #include "mDNSMacOSX.h"
83 #include <sys/types.h>
84 #include <sys/socket.h>
85 #include <netinet/in.h>
86 #include <netinet/tcp.h>
89 #include <sys/ioctl.h>
91 #include <netinet/in.h>
92 #include <arpa/inet.h>
93 #include <sys/sysctl.h>
94 #include <net/route.h>
97 #include <arpa/inet.h>
104 //#include "netaddr.h"
106 // TODO: remove later and do variable length
107 #define MAX_SOAPMSGSIZE 65536
109 static int safe_close(int fd
)
111 if (fd
< 3) { /* LogMsg("safe_close: ERROR sd %d < 3", fd); */ return(-1); }
115 #define close safe_close
117 ////////////////////////////////////////////////////////////////////////
119 ////////////////////////////////////////////////////////////////////////
122 #define NA_E_SUCCESS (0)
123 #define NA_E_INTERNAL_ERROR (-1) /* somewhere something wrong */
124 #define NA_E_INVALID_PARAMETER (-2) /* bad params */
125 #define NA_E_OPERATION_FAILED (-3) /* can't fulfill request */
126 #define NA_E_TIMEOUT (-4) /* operation timed out */
127 #define NA_E_THREAD_ERROR (-5) /* some error related to threads */
128 #define NA_E_PARSE_ERROR (-6) /* a parsing error occured */
129 #define NA_E_NOT_READY (-7) /* this op can't proceed yet */
130 #define NA_E_NOT_FOUND (-8) /* resource/prereq not found */
131 #define NA_E_NOT_AVAILABLE (-9) /* service not available */
132 #define NA_E_EXISTS (-10) /* can't modify existing item */
133 #define NA_E_AGAIN (-11) /* something wrong - try again */
134 #define NA_E_NOT_SUPPORTED (-12) /* wait until next version */
135 #define NA_E_ABORT (-14) /* operation aborted */
136 #define NA_E_NET (-15) /* network layer problem */
138 // Logging flags - log types (increasing degree of detail)
139 #define NALOG_ERROR (1UL) /* error messages */
140 #define NALOG_ALERT (2UL) /* useful warning/alerts */
141 #define NALOG_INFO0 (4UL) /* info - potential problem */
142 #define NALOG_INFO1 (8UL) /* extra info */
143 #define NALOG_DUMP (16UL) /* data dumps */
145 #define NALOG_RSRV1 (32UL) /* reserved */
146 #define NALOG_RSRV2 (64UL) /* reserved */
147 #define NALOG_RSRV3 (128UL) /* reserved */
149 // Logging flags - component (not used for now)
150 #define NALOG_UPNP (256) /* UPnP */
152 // Default Logging levels
153 #define NALOG_LEVEL0 (0)
154 #define NALOG_LEVEL1 (NALOG_UPNP | NALOG_ERROR)
155 #define NALOG_LEVEL2 (NALOG_LEVEL1 | NALOG_ALERT)
156 #define NALOG_LEVEL3 (NALOG_LEVEL2 | NALOG_INFO0)
157 #define NALOG_LEVEL4 (NALOG_LEVEL3 | NALOG_INFO1)
158 #define NALOG_LEVEL5 (NALOG_LEVEL4 | NALOG_DUMP)
159 #define NALOG_DEFAULT_LEVEL (NALOG_LEVEL2)
161 // Default timeout values (in m-seconds (milli))
162 // 50 milliseconds for function timeout
163 #define NA_DEFAULT_FUNCTION_TIMEOUT (50)
165 ////////////////////////////////////////////////////////////////////////
167 ////////////////////////////////////////////////////////////////////////
168 #define SSDP_IP "239.255.255.250"
169 #define SSDP_PORT 1900
173 #define H_CRLF "\r\n"
174 // SOAP message's CRLF:
175 //#define S_CRLF "\r\n"
178 // standard 200 ok msg
179 #define HTTP200OK "HTTP/1.1 200 OK\r\n\r\n"
180 #define HTTP200OKLEN (sizeof(HTTP200OK) - 1)
182 // maximum time to wait for an event (in microseconds)
183 #define MAX_EXPECTEVENTTIME (10000)
185 ////////////////////////////////////////////////////////////////////////
187 ////////////////////////////////////////////////////////////////////////
188 typedef struct tagProperty
{
192 } Property
, *PProperty
;
194 typedef struct tagHTTPResponse
{
198 Property aHeaders
[30]; // assume at most this many headers
204 } HTTPResponse
, *PHTTPResponse
, **PPHTTPResponse
;
206 ////////////////////////////////////////////////////////////////////////
208 ////////////////////////////////////////////////////////////////////////
209 static const char szSSDPMsgDiscoverRoot
[] =
210 "M-SEARCH * HTTP/1.1\r\n"
211 "Host:239.255.255.250:1900\r\n"
212 "ST:upnp:rootdevice\r\n"
213 "Man:\"ssdp:discover\"\r\n"
217 static const char szSSDPMsgDiscoverIGD
[] =
218 "M-SEARCH * HTTP/1.1\r\n"
219 "Host:239.255.255.250:1900\r\n"
220 "ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
221 "Man:\"ssdp:discover\"\r\n"
225 static const char szSSDPMsgDiscoverNAT
[] =
226 "M-SEARCH * HTTP/1.1\r\n"
227 "Host:239.255.255.250:1900\r\n"
228 "ST:urn:schemas-upnp-org:service:WANIPConnection:1\r\n"
229 "Man:\"ssdp:discover\"\r\n"
233 //// Subscribe message
235 // 2$s: local's host/port ("host:port")
236 // 3$s: router's host/port ("host:port")
237 // 4$d: subscription timeout in seconds
238 static const char szEventMsgSubscribeFMT
[] =
239 "SUBSCRIBE %1$s HTTP/1.1\r\n"
241 "Callback: <http://%2$s/notify>\r\n"
242 "Timeout: Second-%4$d\r\n"
243 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
245 "Content-Length: 0\r\n"
246 "Pragma: no-cache\r\n"
249 //// Unsubscribe message
251 // 2$s: SID (some uuid passed back during subscribe)
252 // 3$s: router's host ("host")
254 static const char szEventMsgUnsubscribeFMT
[] =
255 "UNSUBSCRIBE %1$s HTTP/1.1\r\n"
257 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
259 "Content-Length: 0\r\n"
260 "Pragma: no-cache\r\n"
264 //// Generic SOAP Control:Action request messages
266 // 2$s: router's host/port ("host:port")
267 // 3$s: action (string)
268 // 4$d: content-length
269 static const char szSOAPMsgControlAHeaderFMT
[] =
270 //"M-POST %1$s HTTP/1.1\r\n"
271 "POST %1$s HTTP/1.1\r\n"
272 "Content-Type: text/xml; charset=\"utf-8\"\r\n"
273 //"TEST: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
274 //"Man: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
275 //"01-SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#%3$s\"\r\n"
276 "SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#%3$s\"\r\n"
277 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n"
279 "Content-Length: %4$d\r\n"
280 "Connection: close\r\n"
281 // "Connection: Keep-Alive\r\n"
282 "Pragma: no-cache\r\n"
285 // 1$: action (string)
287 static const char szSOAPMsgControlABodyFMT
[] =
288 "<?xml version=\"1.0\"?>" CRLF
289 "<SOAP-ENV:Envelope" S_CRLF
290 " xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"" S_CRLF
291 " SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" S_CRLF
292 "<SOAP-ENV:Body>" S_CRLF
294 " xmlns:m=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" S_CRLF
297 "</SOAP-ENV:Body>" S_CRLF
298 "</SOAP-ENV:Envelope>" S_CRLF
305 // 2$: argument value
306 static const char szSOAPMsgControlAArgumentFMT
[] =
307 "<%1$s>%2$s</%1$s>" S_CRLF
;
310 // 2$: argument value
312 static const char szSOAPMsgControlAArgumentFMT_t
[] =
314 " xmlns:dt=\"urn:schemas-microsoft-com:datatypes\""
315 " dt:dt=\"%3$s\">%2$s</%1$s>" S_CRLF
;
318 //// Generic SOAP Control:Query request messages
320 // 2$s: router's host/port ("host:port")
321 // 3$d: content-length
322 static const char szSOAPMsgControlQHeaderFMT
[] =
323 "M-POST %1$s HTTP/1.1\r\n"
324 //"POST %1$s HTTP/1.1\r\n"
326 "Content-Length: %3$d\r\n"
327 "Content-Type: text/xml; charset-\"utf-8\"\r\n"
328 //"Man: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
329 //"SOAPAction: \"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"\r\n"
330 "01-SOAPAction: \"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"\r\n"
334 static const char szSOAPMsgControlQBodyFMT
[] =
336 " xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"" S_CRLF
337 " s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" S_CRLF
339 "<u:QueryStateVariable xmlns:u=\"urn:schemas-upnp-org:control-1-0\"" S_CRLF
340 "<u:varName>%s</u:varName>" S_CRLF
341 "</u:QueryStateVariable>" S_CRLF
343 "</s:Envelope>" S_CRLF
346 // 1$: device description URL
348 static const char szSSDPMsgDescribeDeviceFMT
[] =
349 "GET %s HTTP/1.1\r\n"
350 "Accept: text/xml, application/xml\r\n"
351 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
353 "Connection: close\r\n"
354 // "Connection: Keep-Alive\r\n"
357 ////////////////////////////////////////////////////////////////////////
359 ////////////////////////////////////////////////////////////////////////
361 static int g_fFirstInit
= TRUE
;
362 static int g_fQuit
= FALSE
;
364 static int g_fLogging
;
366 // Globally-accessible UDP socket
367 static int g_sUDP
= -1;
368 static int g_sUDPCancel
= -1;
370 // Globally-accessible TCP socket
371 static int g_sTCP
= -1;
372 static int g_sTCPCancel
= -1;
375 static int g_fEventEnabled
= FALSE
;
376 static unsigned short g_wEventPort
;
377 static struct sockaddr_in g_saddrRouterEvent
;
378 static char g_szRouterHostPortEvent
[1024];
379 static char g_szEventURL
[1024];
382 static char g_szFriendlyName
[1024];
383 static char g_szManufacturer
[1024];
384 static char g_szModelName
[1024];
385 static char g_szModelDescription
[1024];
388 static struct sockaddr_in g_saddrRouterBase
;
389 static char g_szRouterHostPortBase
[1024];
392 static pthread_t g_UDPthread
= NULL
;
393 static pthread_t g_TCPthread
= NULL
;
396 static unsigned long g_dwLocalIP
= 0;
398 // Globally accessible info about the router/UPnP
399 static int g_fUPnPEnabled
= FALSE
;
400 static char g_szUSN
[1024];
402 static struct sockaddr_in g_saddrRouterDesc
;
403 static char g_szRouterHostPortDesc
[1024];
404 static char g_szNATDevDescURL
[1024];
406 static struct sockaddr_in g_saddrRouterSOAP
;
407 static char g_szRouterHostPortSOAP
[1024];
408 static char g_szControlURL
[1024];
409 static int g_fControlURLSet
= FALSE
;
411 // Lock/condvar for synchronous upnp calls
412 static pthread_mutex_t g_xUPnP
;
413 static pthread_mutex_t g_xUPnPMsg
;
414 static pthread_cond_t g_condUPnP
;
415 static pthread_cond_t g_condUPnPControlURL
;
416 static struct timeval g_tvUPnPInitTime
;
417 static struct timeval g_tvLastUpdateTime
;
419 // timeout values in seconds
420 static int g_iFunctionTimeout
= NA_DEFAULT_FUNCTION_TIMEOUT
;
422 static void GetDeviceDescription(void);
423 static void SetLocalIP(void);
425 ////////////////////////////////////////////////////////////////////////
427 ////////////////////////////////////////////////////////////////////////
432 #define IFNAMELEN 16 /* Interface Name Length */
433 #define IPLEN 16 /* 16 bytes(128 bits) for IPv6 */
435 typedef struct tagIPINFO
438 char szIfName
[IFNAMELEN
]; /* Interface name */
439 unsigned char abIP
[IPLEN
]; /* IP in host byte order */
440 unsigned short wPort
;
441 } IPINFO
, *PIPINFO
, **PPIPINFO
;
443 typedef struct hostent HOSTENT
, *PHOSTENT
;
445 static unsigned long GetNATIPNetmask(unsigned long dwIP
)
447 if ((dwIP
& 0xFF000000) == 0x0A000000) return 0xFF000000;
448 if ((dwIP
& 0xFFF00000) == 0xAC100000) return 0xFFF00000;
449 if ((dwIP
& 0xFFFF0000) == 0xC0a80000) return 0xFFFF0000;
451 return 0; /* No NAT IP */
454 static int GetIPInfo(PPIPINFO ppIPInfo
)
457 int iLastLen
, iLen
, iNum
= 0, iMax
= 0;
459 char *pcBuf
, *pcTemp
;
460 PIPINFO pIPInfo
= NULL
;
462 struct ifreq
*ifr
, ifrcopy
;
464 if (ppIPInfo
== NULL
) return 0;
466 fd
= socket(AF_INET
, SOCK_DGRAM
, 0);
469 iLen
= 100 * sizeof(struct ifreq
);
473 pcBuf
= (char *)malloc(iLen
);
477 if (ioctl(fd
, SIOCGIFCONF
, &ifc
) < 0)
479 if (errno
!= EINVAL
|| iLastLen
!= -1)
481 // DbgPrint(ELL_ERROR, "ioctl failed(%d)\n", errno);
489 if (ifc
.ifc_len
== iLastLen
) break;
490 iLastLen
= ifc
.ifc_len
;
493 iLen
+= 10 * sizeof(struct ifreq
);
497 for (pcTemp
= pcBuf
; pcTemp
< pcBuf
+ ifc
.ifc_len
; )
504 pIPInfoNew
= (PIPINFO
)realloc(pIPInfo
, sizeof(IPINFO
) * iMax
);
505 if (pIPInfoNew
== NULL
)
512 else pIPInfo
= pIPInfoNew
;
514 memset(pIPInfo
+ (iMax
- 10), 0, sizeof(IPINFO
) * 10);
517 ifr
= (struct ifreq
*)pcTemp
;
519 pcTemp
+= sizeof(ifr
->ifr_name
) + ifr
->ifr_addr
.sa_len
;
521 /* discard invalid address families & loopback */
522 if ((ifr
->ifr_addr
.sa_family
!= AF_INET
&&
523 ifr
->ifr_addr
.sa_family
!= AF_INET6
) ||
524 strncmp(ifr
->ifr_name
, "lo", 2) == 0) continue;
527 ioctl(fd
, SIOCGIFFLAGS
, &ifrcopy
);
528 if ((ifrcopy
.ifr_flags
& IFF_UP
) == 0) continue;
530 switch (ifr
->ifr_addr
.sa_family
)
533 memcpy(pIPInfo
[iNum
].szIfName
, ifr
->ifr_name
, IFNAMELEN
);
535 ntohl(((struct sockaddr_in
*)&ifr
->ifr_addr
)->sin_addr
.s_addr
);
536 memcpy(pIPInfo
[iNum
].abIP
, &dwIP
, sizeof(unsigned long));
537 if (ifrcopy
.ifr_flags
& IFF_POINTOPOINT
)
538 pIPInfo
[iNum
].iFlags
|= ISPPP
;
543 memcpy(pIPInfo
[iNum
].szIfName
, ifr
->ifr_name
, IFNAMELEN
);
544 memcpy(pIPInfo
[iNum
].abIP
,
545 ((struct sockaddr_in6
*)&(ifr
->ifr_addr
))-> sin6_addr
.s6_addr
,
547 pIPInfo
[iNum
].iFlags
|= ISIPV6
;
548 if (ifrcopy
.ifr_flags
& IFF_POINTOPOINT
)
549 pIPInfo
[iNum
].iFlags
|= ISPPP
;
566 static void FreeIPInfo(PIPINFO pIPInfo
)
568 if (pIPInfo
!= NULL
) free(pIPInfo
);
572 ////////////////////////////////////////////////////////////////////////
573 // Function Definitions
574 ////////////////////////////////////////////////////////////////////////
576 static void SendDiscoveryMsg();
579 // Creates a UDP multicast socket and listens to the SSDP IP/PORT
581 // -1 on error, or the socket descriptor if success
582 static int SSDPListen()
587 struct sockaddr_in saddr
;
590 // IPPROTO_IP == 0; IPPROTO_TCP == 6; IPPROTO_UDP == 17; etc.
591 sd
= socket(AF_INET
, SOCK_DGRAM
, 0);
593 if (g_fLogging
& NALOG_ERROR
)
594 fprintf(g_log
, "Can't create socket! SSDPListen exiting\n");
598 // sock options values
599 fLoop
= 0; // false - don't send copy to self
602 // bind to listen to ssdp multicast address
603 bzero(&saddr
, sizeof(saddr
));
604 saddr
.sin_len
= sizeof(saddr
);
605 saddr
.sin_family
= AF_INET
;
606 //saddr.sin_addr.s_addr = inet_addr(SSDP_IP);
607 //saddr.sin_port = htons(SSDP_PORT);
608 saddr
.sin_addr
.s_addr
= htonl(g_dwLocalIP
);
611 // and set the multicast add_member structure
612 // (TODO: need to find interfaces later - ioctl, with:
613 // SIOCFIFCONF to find if's, SIOCGIFADDR to get addr, and SIOCFIFFLAGS
614 // to check for IFF_MULTICAST flag for multicast support on an if)
615 bzero(&mreq
, sizeof(mreq
));
616 mreq
.imr_interface
.s_addr
= g_dwLocalIP
;
617 mreq
.imr_multiaddr
.s_addr
= inet_addr(SSDP_IP
);
620 bind(sd
, (struct sockaddr
*)&saddr
, sizeof(saddr
)) //||
621 //setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &fLoop, sizeof(fLoop)) ||
622 //setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &iTTL, sizeof(iTTL)) ||
623 //setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))
625 if (g_fLogging
& NALOG_ERROR
)
627 "bind/setsockopt for multicast failed... errno = %d\n", errno
);
635 static int EventListen()
637 struct sockaddr_in saddr
;
640 // try 5 ports before failing completely
641 for (g_wEventPort
= 5000; g_wEventPort
< 5005; g_wEventPort
++)
643 sd
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
645 if (g_fLogging
& NALOG_ERROR
)
646 fprintf(g_log
, "Can't create socket! EventListen exiting\n");
650 bzero(&saddr
, sizeof(saddr
));
651 saddr
.sin_len
= sizeof(saddr
);
652 saddr
.sin_family
= AF_INET
;
653 saddr
.sin_addr
.s_addr
= htonl(g_dwLocalIP
);
654 saddr
.sin_port
= htons(g_wEventPort
);
657 if (bind(sd
, (struct sockaddr
*)&saddr
, sizeof(saddr
)) == 0)
660 ////TracePrint(ELL_TRACE, "UPnP: EventListen @%u\n", g_wEventPort);
664 // unsuccessful - close sd and try again
665 if (g_fLogging
& NALOG_ERROR
)
667 "bind TCP port %u failed: errno = %d\n", g_wEventPort
, errno
);
674 static void *TCPProc(void *in
);
676 static int EventInit()
681 if (g_fEventEnabled
== FALSE
)
683 // initialize TCP socket for Eventing
684 g_sTCP
= EventListen();
686 if (g_fLogging
& NALOG_ERROR
)
687 fprintf(g_log
, "EventInit - Failed to init tcp socket.\n");
688 return NA_E_INTERNAL_ERROR
;
692 pthread_attr_init(&attr
);
693 iRet
= pthread_create(&g_TCPthread
, &attr
, TCPProc
, 0);
697 if (g_fLogging
& NALOG_ERROR
)
698 fprintf(g_log
, "EventInit: TCPProc create failed(%d)\n", iRet
);
699 return NA_E_THREAD_ERROR
;
703 g_fEventEnabled
= TRUE
;
708 static void DumpHex(char *buf
, int len
)
715 if (g_fLogging
& NALOG_DUMP
) {
716 if (buf
== NULL
) return;
717 if (len
<= 0) return;
719 for (i
= 0; i
< len
; i
= nexti
) {
720 fprintf(g_log
, "%04x: ", i
);
722 endj
= (nexti
> len
) ? len
: nexti
;
723 for (j
= i
; j
< endj
; j
++)
724 fprintf(g_log
, "%02x ", buf
[j
] & 0xff);
727 char pad
[3 * 16 + 1]; // don't need the last 3 bytes anyway
728 j
= (16 - (j
% 16)) * 3;
734 for (j
= i
; j
< endj
; j
++)
735 isprint(buf
[j
]) ? fputc(buf
[j
], g_log
) : fputc('.', g_log
);
742 // FindHTTPHeaderNewLine
743 // Returns a pointer to the beginning of a CRLF, that is not a
744 // part of LWS. (LWS is CRLF followed by a space or tab, and in
745 // HTTP, considered as equivalent to a single space) (LWS stands
746 // for "linear white space")
747 // Returns a pointer the beginning of CRLF, and sets the EOH flag to
748 // whether this is the last header in the HTTP header section.
749 // Also, if pbuf is NULL, or if there isn't any CRLF found in the
750 // string, or if the HTTP syntax is wrong, NULL is returned, and
751 // the EOH flag is not touched.
752 static char *FindHTTPHeaderNewLine(char *pbuf
, int iBufSize
, int *pfEOH
)
757 if (pbuf
== NULL
) return NULL
;
760 result
= memchr(pbuf
, '\r', iBufSize
);
761 if (result
== NULL
) {
762 if (g_fLogging
& NALOG_INFO0
) {
763 fprintf(g_log
, "FindHTTPHeaderNewLine: er @(%d)\n", i
);
770 // decrement iBufSize, and move pbuf forward
771 iBufSize
-= (result
- pbuf
);
774 ++pbuf
; // now pointing right after "\r"
776 if (*pbuf
== '\0') break;
777 if (*pbuf
!= '\n') continue;
779 ++pbuf
; // now pointing after "\r\n"
781 if (*pbuf
== '\0') break;
782 if ((*pbuf
== ' ') || (*pbuf
== '\t')) continue;
784 // at this point we know we're at the end of a header field,
785 // and there's more stuff coming...
787 // just need to check if this is the last header
788 if ((pbuf
[0] == '\r') && (pbuf
[1] == '\n'))
799 // NewHTTPResponse_sz
800 // Creates an HTTPResponse structure from a string (sz). Set
801 // fDestroyOriginal to TRUE if the buffer passed in can be overwritten.
802 // Otherwise, NewHTTPResponse_sz will duplicate the buffer.
803 // Returns the created HTTPResponse structure if successful, or if an
804 // error occured (out of memory, or bad http syntax), returns NULL.
805 // NOTE: ALWAYS call DeleteHTTPResponse after using the HTTPResponse structure.
806 // NOTE: The input is assumed to be correct. If there're HTTP syntax errors,
807 // and the pszHTTPResponse is not null-terminated, result may be undefined.
808 // (to be fixed next version)
809 static PHTTPResponse
NewHTTPResponse_sz(
810 char *pszHTTPResponse
,
812 int fDestroyOriginal
)
814 PHTTPResponse pResponse
;
820 if ((pResponse
= (PHTTPResponse
)malloc(sizeof(HTTPResponse
))) == NULL
) {
821 if (g_fLogging
& NALOG_INFO0
) {
822 fprintf(g_log
, "NewHTTPResponse_sz: er 1\n");
828 // make copy of buffer now
829 if (fDestroyOriginal
) {
830 pResponse
->buf
= NULL
;
831 pBuf
= pszHTTPResponse
;
834 int len
= strlen(pszHTTPResponse
);
835 if ((len
+1) > iBufferSize
) {
836 if (g_fLogging
& NALOG_INFO0
)
837 fprintf(g_log
, "Length: %d > %d\n", len
+1, iBufferSize
);
840 if ((pResponse
->buf
= (char *)malloc(iBufferSize
)) == NULL
) {
842 if (g_fLogging
& NALOG_INFO0
) {
843 fprintf(g_log
, "NewHTTPResponse_sz: er 2\n");
848 memcpy(pResponse
->buf
, pszHTTPResponse
, iBufferSize
);
849 pBuf
= pResponse
->buf
;
852 // get the first line
853 pszEOL
= FindHTTPHeaderNewLine(pBuf
, iBufferSize
, &fEOH
);
854 if (pszEOL
== NULL
) {
855 if (g_fLogging
& NALOG_INFO0
) {
856 fprintf(g_log
, "NewHTTPResponse_sz: er 3\n");
862 *pszEOL
= '\0'; // terminate the status line
863 pszEOL
+= 2; // point to the rest of the buffer
865 // set the status string first
866 pResponse
->pszStatus
= strchr(pBuf
, ' ');
867 if (pResponse
->pszStatus
== NULL
) {
868 if (g_fLogging
& NALOG_INFO0
) {
869 fprintf(g_log
, "NewHTTPResponse_sz: er 4\n");
872 goto cleanup
; // syntax error
875 pResponse
->pszStatus
++; // point to the actual status
877 pResponse
->pszReason
= strchr(pResponse
->pszStatus
, ' ');
878 if (pResponse
->pszReason
== NULL
) {
879 if (g_fLogging
& NALOG_INFO0
) {
880 fprintf(g_log
, "NewHTTPResponse_sz: er 5\n");
883 goto cleanup
; // syntax error
886 pResponse
->pszReason
[0] = '\0'; // terminate status string
887 pResponse
->pszReason
++; // point to the reason string
889 iNumHeaders
= 0; // initialize to 0 headers
891 // parse header fields line by line (while not end of headers)
893 PProperty pHeader
= &(pResponse
->aHeaders
[iNumHeaders
]);
894 // point header field name to the first char of the line
895 pHeader
->pszName
= pszEOL
;
897 // search for the end of line
898 pszEOL
= FindHTTPHeaderNewLine(pszEOL
,
899 iBufferSize
- (pszEOL
- pBuf
), // remainder size
901 if (pszEOL
== NULL
) goto cleanup
; // syntax error
903 *pszEOL
= '\0'; // terminate this string
904 pszEOL
+= 2; // point to beginning of next line
906 pHeader
->pszValue
= strchr(pHeader
->pszName
, ':');
907 if (pHeader
->pszValue
== NULL
) {
908 if (g_fLogging
& NALOG_INFO0
) {
909 fprintf(g_log
, "NewHTTPResponse_sz: er 6\n");
912 goto cleanup
; // syntax error (header field has no ":")
915 pHeader
->pszValue
[0] = '\0'; // terminate the header name string
916 pHeader
->pszValue
++; // point after the ":"
917 // get rid of leading spaces for the value part
919 (pHeader
->pszValue
[0] == ' ') ||
920 (pHeader
->pszValue
[0] == '\t') ||
921 (pHeader
->pszValue
[0] == '\r') ||
922 (pHeader
->pszValue
[0] == '\n')
924 pHeader
->pszValue
++; // skip the space
927 iNumHeaders
++; // added one more header
928 pHeader
++; // point to the next header in pResponse->aHeaders
931 pResponse
->iNumHeaders
= iNumHeaders
; // remember to set it in pResponse
933 pResponse
->pszBody
= pszEOL
+ 2; // point after the empty line
938 if (pResponse
->buf
!= NULL
) free(pResponse
->buf
);
943 // DeleteHTTPResponse
944 // Deallocates stuff in the HTTPResponse structure, effectively returning
945 // memory to the system and destroying the structure.
946 // NOTE: The pointer pResponse WILL BE FREED, and will be unusable after
947 // the call to DeleteHTTPResponse.
948 static void DeleteHTTPResponse(PHTTPResponse pResponse
)
952 if (pResponse
== NULL
) return;
954 // Current impl is just simple array - no need to free()
955 //for (i = 0; i < pResponse->iNumHeaders; i++) {
956 // free(pResponse->aHeaders[i]);
959 if (pResponse
->buf
!= NULL
)
960 free(pResponse
->buf
);
964 //typedef struct tagHTTPResponse {
968 // Property aHeaders[30]; // assume at most this many headers
974 //} HTTPResponse, *PHTTPResponse, **PPHTTPResponse;
976 static void PrintHTTPResponse(PHTTPResponse pResponse
)
980 if (g_fLogging
& (NALOG_INFO1
)) {
981 if (pResponse
== NULL
) return;
982 fprintf(g_log
, " *** HTTP response begin *** \n");
983 fprintf(g_log
, " * status = [%s], reason = [%s] *\n",
984 pResponse
->pszStatus
, pResponse
->pszReason
);
985 for (i
= 0; i
< pResponse
->iNumHeaders
; i
++) {
986 fprintf(g_log
, " * Header \"%s\" = [%s]\n",
987 pResponse
->aHeaders
[i
].pszName
,
988 pResponse
->aHeaders
[i
].pszValue
);
990 if (g_fLogging
& NALOG_DUMP
)
991 fprintf(g_log
, " * body = [%s] *\n", pResponse
->pszBody
);
992 fprintf(g_log
, " *** HTTP response end *** \n");
996 static int DiscoverRouter(PHTTPResponse pResponse
)
999 int fLocation
= FALSE
;
1001 int fIsNATDevice
= FALSE
;
1004 if (strcmp(pResponse
->pszStatus
, "200") != 0)
1008 if (pResponse
== NULL
) {
1009 if (g_fLogging
& NALOG_INFO0
)
1010 fprintf(g_log
, "DiscoverRouter: pResponse == NULL\n");
1014 // check to see if this is a relevant packet
1015 for (i
= 0; i
< pResponse
->iNumHeaders
; i
++) {
1016 PProperty pHeader
= &(pResponse
->aHeaders
[i
]);
1018 if ((strcasecmp(pHeader
->pszName
, "ST") == 0) ||
1019 (strcasecmp(pHeader
->pszName
, "NT") == 0)) {
1020 if ((strcmp(pHeader
->pszValue
,
1021 "urn:schemas-upnp-org:service:WANIPConnection:1") == 0) ||
1022 (strcmp(pHeader
->pszValue
,
1023 "urn:schemas-upnp-org:device:InternetGatewayDevice:1") == 0)) {
1024 fIsNATDevice
= TRUE
;
1029 // leave the message alone if we don't need it
1033 // Now that we know we're looking at the message about the NAT device:
1034 pthread_mutex_lock(&g_xUPnP
);
1036 // set upnp to be unconfigured for now
1037 g_fUPnPEnabled
= FALSE
;
1039 // loop through the headers
1040 for (i
= 0; i
< pResponse
->iNumHeaders
; i
++) {
1041 PProperty pHeader
= &(pResponse
->aHeaders
[i
]);
1043 if (strcasecmp(pHeader
->pszName
, "Location") == 0) {
1047 if (g_fLogging
& NALOG_INFO1
)
1048 fprintf(g_log
, "Checking Location...\n");
1049 p
= pHeader
->pszValue
;
1050 if (strncmp(p
, "http://", 7) != 0)
1051 continue; // hope for another Location header to correct it
1052 p
+= 7; // skip over "http://"
1055 // set the control URL first
1057 g_szNATDevDescURL
[0] = '/';
1058 g_szNATDevDescURL
[1] = '\0';
1061 strncpy(g_szNATDevDescURL
, q
, sizeof(g_szNATDevDescURL
) - 1);
1062 g_szNATDevDescURL
[sizeof(g_szNATDevDescURL
) - 1] = '\0';
1063 // terminate the host/port string
1067 if (g_fLogging
& NALOG_INFO1
)
1068 fprintf(g_log
, " Device Description URL set to[%s]...\n",
1071 // see if port is specified
1074 sprintf(g_szRouterHostPortDesc
, "%s", p
);
1076 g_saddrRouterDesc
.sin_addr
.s_addr
= inet_addr(p
);
1077 g_saddrRouterDesc
.sin_port
= htons(80);
1080 // don't include the ":80" - HTTP is by default port 80
1081 if (atoi(q
+1) == 80) *q
= '\0';
1083 strcpy(g_szRouterHostPortDesc
, p
);
1085 // terminate the host part and point to it
1089 g_saddrRouterDesc
.sin_addr
.s_addr
= inet_addr(p
);
1090 g_saddrRouterDesc
.sin_port
= htons(atoi(q
));
1093 g_saddrRouterDesc
.sin_family
= AF_INET
;
1095 if (g_fLogging
& NALOG_INFO1
)
1096 fprintf(g_log
, " Router Address set to[%s]...\n",
1097 g_szRouterHostPortDesc
);
1100 else if (strcasecmp(pHeader
->pszName
, "USN") == 0) {
1101 if (g_fLogging
& NALOG_INFO1
)
1102 fprintf(g_log
, "Checking USN...\n");
1103 strncpy(g_szUSN
, pHeader
->pszValue
, sizeof(g_szUSN
) - 1);
1104 g_szUSN
[sizeof(g_szUSN
) - 1] = '\0';
1108 ; // do nothing for other headers for now
1112 // now check flags and set enabled if all set
1113 if (fLocation
&& fUSN
) {
1114 if (g_fLogging
& NALOG_INFO1
) {
1116 "Description Host/port string: [%s]\n"
1117 "NATDevDescURL: [%s], USN: [%s]\n",
1118 g_szRouterHostPortDesc
,
1119 g_szNATDevDescURL
, g_szUSN
);
1120 if (g_fLogging
& NALOG_INFO1
)
1121 fprintf(g_log
, "Got router information\n");
1124 g_fUPnPEnabled
= TRUE
;
1125 pthread_cond_broadcast(&g_condUPnP
);
1128 // remember to unlock before return
1129 pthread_mutex_unlock(&g_xUPnP
);
1134 // granularity is specified as: granularity = 1/nth seconds
1135 #define UPNP_TIMEOUT_GRANULARITY (1000)
1136 #define U_TOGRAN UPNP_TIMEOUT_GRANULARITY
1139 static void TimevalSubtract(
1140 struct timeval
*result
,
1141 const struct timeval
*a
,
1142 const struct timeval
*b
)
1144 result
->tv_sec
= a
->tv_sec
- b
->tv_sec
;
1146 if (b
->tv_usec
> a
->tv_usec
) {
1148 result
->tv_usec
= 1000000 + a
->tv_usec
- b
->tv_usec
;
1151 result
->tv_usec
= a
->tv_usec
- b
->tv_usec
;
1154 // elapsed = end - start
1155 static void GetTimeElapsed(
1156 const struct timeval
*tv_start
,
1157 const struct timeval
*tv_end
,
1158 struct timeval
*tv_elapsed
)
1160 TimevalSubtract(tv_elapsed
, tv_end
, tv_start
);
1162 tv_elapsed
->tv_sec
= tv_end
->tv_sec
- tv_start
->tv_sec
;
1164 if (tv_start
->tv_usec
> tv_end
->tv_usec
) {
1165 tv_elapsed
->tv_sec
--;
1166 tv_elapsed
->tv_usec
= 1000000 + tv_end
->tv_usec
- tv_start
->tv_usec
;
1169 tv_elapsed
->tv_usec
= tv_end
->tv_usec
- tv_start
->tv_usec
;
1173 // returns +1, 0, or -1, if a>b, a==b, a<b, respectively
1174 static int CompareTime(
1175 const struct timeval
*a
,
1176 const struct timeval
*b
1179 if ((a
->tv_sec
== b
->tv_sec
) &&
1180 (a
->tv_usec
== b
->tv_usec
)) return 0;
1182 if (a
->tv_sec
> b
->tv_sec
) return 1;
1183 else if (a
->tv_sec
< b
->tv_sec
) return -1;
1185 // if seconds are equal...
1186 if (a
->tv_usec
> b
->tv_usec
) return 1;
1190 static int WaitControlURLSet(double timeout
)
1194 struct timeval tv_start
;
1196 long to_sec
= (int) (timeout
/ U_TOGRAN
);
1198 (int) (((timeout
/ U_TOGRAN
) - to_sec
) * 1000000.0);
1199 //long to_sec = (int) timeout;
1200 //long to_usec = (int) ((timeout - to_sec) * 1000000.0);
1201 struct timeval elapsed
;
1203 // get function start time
1204 gettimeofday(&tv_start
, NULL
);
1206 pthread_mutex_lock(&g_xUPnP
);
1209 // if last update is too long ago then wait for it
1210 GetTimeElapsed(&g_tvLastUpdateTime
, &tv_start
, &elapsed
);
1211 if ((elapsed
.tv_sec
+ (elapsed
.tv_usec
/ 1000000.0)) >
1212 (((double) g_iUPnPTimeout
) / U_TOGRAN
))
1213 g_fControlURLSet
= 0;
1216 while (!g_fControlURLSet
) {
1218 gettimeofday(&tv
, NULL
);
1221 for now ignore device timeout
1222 // see if we've past the device's timeout first
1223 GetTimeElapsed(&g_tvUPnPInitTime
, &tv
, &elapsed
);
1224 if ((elapsed
.tv_sec
> g_timeout_sec
) ||
1225 ( (elapsed
.tv_sec
== g_timeout_sec
) &&
1226 (elapsed
.tv_usec
> g_timeout_usec
)
1229 pthread_mutex_unlock(&g_xUPnP
);
1234 // calculate ts to sleep till
1235 ts
.tv_sec
= tv
.tv_sec
+ to_sec
;
1236 ts
.tv_nsec
= (tv
.tv_usec
+ to_usec
) * 1000;
1237 if (ts
.tv_nsec
> 1000000000) {
1238 ts
.tv_nsec
-= 1000000000;
1242 // now get how long we've been in this function already and deduct
1243 GetTimeElapsed(&tv_start
, &tv
, &elapsed
);
1244 ts
.tv_sec
-= elapsed
.tv_sec
;
1245 if (ts
.tv_nsec
< (elapsed
.tv_usec
* 1000)) {
1247 ts
.tv_nsec
= 1000000000 + ts
.tv_nsec
- (elapsed
.tv_usec
* 1000);
1250 ts
.tv_nsec
-= (elapsed
.tv_usec
* 1000);
1253 iRet
= pthread_cond_timedwait(&g_condUPnPControlURL
, &g_xUPnP
, &ts
);
1255 // if timeout then return false
1258 pthread_mutex_unlock(&g_xUPnP
);
1262 pthread_mutex_unlock(&g_xUPnP
);
1267 static int WaitUPnPFunction()
1269 struct timeval start
;
1270 // struct timeval end;
1272 // struct timeval elapsed;
1274 gettimeofday(&start
, NULL
);
1276 wait2
= (double)g_iFunctionTimeout
;
1278 WaitControlURLSet(wait2
);
1280 //gettimeofday(&end, NULL);
1281 //GetTimeElapsed(&start, &end, &elapsed);
1282 //fprintf(stderr, "== wait2: (%f) %d.%06d\n",
1283 // wait2/U_TOGRAN, elapsed.tv_sec, elapsed.tv_usec);
1285 return g_fControlURLSet
;
1288 static void SetLocalIP();
1290 static int SendTCPMsg_saddr_parse(
1291 char *msg
, int iLen
,
1292 char *result
, int resultSize
,
1293 struct sockaddr_in
*saHost
);
1295 static void *TCPProc(void *in
)
1298 unsigned char buf
[MAX_SOAPMSGSIZE
];
1303 //TracePrint(ELL_TRACE, "UPnP: Begin TCPProc\n");
1305 // do the subscription
1308 char response
[2000];
1311 sprintf(callback
, "%lu.%lu.%lu.%lu:%u",
1312 (g_dwLocalIP
>> 24) & 0xFF,
1313 (g_dwLocalIP
>> 16) & 0xFF,
1314 (g_dwLocalIP
>> 8) & 0xFF,
1315 (g_dwLocalIP
>> 0) & 0xFF,
1318 n
= sprintf((char *)buf
,
1319 szEventMsgSubscribeFMT
,
1321 callback
, g_szRouterHostPortEvent
, 1800);
1323 memset(response
, 0, 2000);
1324 n
= SendTCPMsg_saddr_parse(
1327 &g_saddrRouterEvent
);
1331 resp
= NewHTTPResponse_sz((char *)buf
, n
, TRUE
);
1334 ////TracePrint(ELL_TRACE, "UPnP Subscribe returns %s/%d\n", resp->pszStatus, n);
1338 ////TracePrint(ELL_TRACE, "UPnP Subscribe not enough response (%d) \n[%s]\n",
1341 DeleteHTTPResponse(resp
);
1345 ////TracePrint(ELL_TRACE, "UPnP Subscribe failed (%d)\n", n);
1350 //TracePrint(ELL_TRACE, "UPnP: TCPProc begin loop\n");
1357 struct sockaddr_in recvaddr
;
1358 socklen_t recvaddrlen
;
1360 struct timeval timeout
;
1365 // for after responding to long(?) TCP event
1369 if (g_sTCPCancel
!= -1) close(g_sTCPCancel
);
1370 sMax
= g_sTCPCancel
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
1371 if (sMax
< g_sTCP
) sMax
= g_sTCP
;
1374 FD_SET(g_sTCP
, &readfds
);
1375 FD_SET(g_sTCPCancel
, &readfds
);
1376 iRet
= select(sMax
+1, &readfds
, NULL
, NULL
, NULL
);
1380 //TracePrint(ELL_TRACE, "UPnP Event select failed (%d)\n", errno);
1384 recvaddrlen
= sizeof(recvaddr
);
1385 sEvent
= accept(g_sTCP
, (struct sockaddr
*)&recvaddr
, &recvaddrlen
);
1386 // not likely - (system's descriptor/file table full)
1387 if (sEvent
<= 0) continue;
1389 ////TracePrint(ELL_TRACE, "UPnP receiving event..\n");
1391 // read all we could from this event
1397 FD_SET(sEvent
, &readfds
);
1399 timeout
.tv_usec
= 400000; // long cause we're dealing with input
1400 iRet
= select(sEvent
+1, &readfds
, NULL
, NULL
, &timeout
);
1411 iRet
= recv(sEvent
, buf
+ iBufLen
, MAX_SOAPMSGSIZE
- iBufLen
, 0);
1414 // something is wrong
1427 iTemp
= send(sEvent
, HTTP200OK
, HTTP200OKLEN
, 0);
1428 shutdown(sEvent
, 1);
1433 // now send 200 OK and be done
1436 ////TracePrint(ELL_TRACE, "UPnP event (%d) received (%d)\n", g_fExpectEvent, iBufLen);
1438 // and parse the XML here.
1439 if (iBufLen
< MAX_SOAPMSGSIZE
)
1441 buf
[iBufLen
] = '\0';
1442 // for now do nothing
1446 buf
[MAX_SOAPMSGSIZE
- 1] = '\0';
1451 //TracePrint(ELL_TRACE, "UPnP: TCPProc end\n");
1454 g_fEventEnabled
= FALSE
;
1455 if (g_sTCPCancel
!= -1) close(g_sTCPCancel
);
1460 static void *UDPProc(void *in
)
1462 // char fLoop = 0; // false - don't send copy to self
1463 // int iTTL = SSDP_TTL;
1465 // struct ip_mreq mreq;
1466 // struct sockaddr_in saddr;
1467 unsigned char buf
[65536];
1468 // FILE *log = g_log;
1469 static time_t last_getdevicedesc_t
= 0;
1472 pthread_mutex_lock(&g_xUPnP
);
1473 gettimeofday(&g_tvUPnPInitTime
, NULL
);
1474 pthread_mutex_unlock(&g_xUPnP
);
1478 struct sockaddr_in recvaddr
;
1479 socklen_t recvaddrlen
;
1481 //struct timeval timeout;
1485 if (g_sUDPCancel
< g_sUDP
) sMax
= g_sUDP
;
1486 else sMax
= g_sUDPCancel
;
1489 FD_SET(g_sUDP
, &readfds
);
1490 FD_SET(g_sUDPCancel
, &readfds
);
1491 iRet
= select(sMax
+1, &readfds
, NULL
, NULL
, NULL
);
1497 close(g_sUDPCancel
);
1505 if (!FD_ISSET(g_sUDP
, &readfds
)) continue;
1506 recvaddrlen
= sizeof(recvaddr
);
1507 n
= recvfrom(g_sUDP
, buf
, sizeof(buf
), 0,
1508 (struct sockaddr
*)&recvaddr
, &recvaddrlen
);
1510 if (g_fLogging
& NALOG_ERROR
)
1511 fprintf(g_log
, "recv failed (%d)\n", errno
);
1513 close(g_sUDPCancel
);
1519 if (strncmp((char *)buf
, "HTTP/1.1", 8) == 0) {
1520 PHTTPResponse pResponse
= NewHTTPResponse_sz((char *)buf
, n
, TRUE
);
1521 PrintHTTPResponse(pResponse
);
1522 if (DiscoverRouter(pResponse
) == 0)
1524 time_t now
= time(NULL
);
1525 if (!g_fControlURLSet
||
1526 ((now
- last_getdevicedesc_t
) > 5))
1528 GetDeviceDescription();
1530 last_getdevicedesc_t
= now
;
1533 DeleteHTTPResponse(pResponse
);
1535 else if (strncmp((char *)buf
, "NOTIFY * HTTP/1.1", 7) == 0) {
1536 // temporarily use this to fudge - will have the exact same
1537 // parsing, only status/reason set to "*" and "HTTP/1.1".
1538 // TODO: add support for HTTP requests
1539 PHTTPResponse pResponse
= NewHTTPResponse_sz((char *)buf
, n
, TRUE
);
1540 if (DiscoverRouter(pResponse
) == 0)
1542 time_t now
= time(NULL
);
1543 if (!g_fControlURLSet
||
1544 ((now
- last_getdevicedesc_t
) > 5))
1546 GetDeviceDescription();
1548 last_getdevicedesc_t
= now
;
1551 DeleteHTTPResponse(pResponse
);
1554 if (g_fLogging
& NALOG_DUMP
)
1555 fprintf(g_log
, "(%ld) Buffer: \n[%s]\n", time(NULL
), buf
);
1564 static void SendUDPMsg(const char *msg
) {
1565 struct sockaddr_in saSendTo
;
1569 bzero(&saSendTo
, sizeof(saSendTo
));
1570 saSendTo
.sin_family
= AF_INET
;
1571 saSendTo
.sin_addr
.s_addr
= inet_addr(SSDP_IP
);
1572 saSendTo
.sin_port
= htons(SSDP_PORT
);
1576 if (g_fLogging
& NALOG_DUMP
)
1577 fprintf(g_log
, "SendUDP: [%s]\n", msg
);
1579 iRet
= sendto(g_sUDP
, msg
, iLen
, 0,
1580 (struct sockaddr
*)&saSendTo
, sizeof(saSendTo
));
1584 if (g_fLogging
& NALOG_ALERT
)
1586 "SendUDPMsg: iRet(%d) != strlen(msg)(%d)! (errno %d)\n",
1590 // strstr, case insensitive, and is limited by len
1591 static char *strcasestr_n(const char *big
, const char *little
, int len
)
1598 if (little
== NULL
) return (char *)big
;
1599 if (big
== NULL
) return NULL
;
1601 //bigLen = strlen(big);
1603 littleLen
= strlen(little
);
1605 if (bigLen
< littleLen
) return NULL
;
1607 end
= bigLen
- littleLen
;
1608 for (i
= 0; i
<= end
; (i
++), (big
++)) {
1609 if (strncasecmp(big
, little
, littleLen
) == 0)
1616 // this is strnstr, only portable
1617 static char *strstr_n(const char *big
, const char *little
, int len
)
1622 (void)len
; // unused
1624 if ((big
== NULL
) || (little
== NULL
)) return NULL
;
1626 iBigLen
= strlen(big
);
1627 iLittleLen
= strlen(little
);
1629 // this part is basically strnstr, except this is portable
1631 if (iBigLen
< iLittleLen
)
1633 if (strncmp(big
, little
, iLittleLen
) == 0)
1640 // returns -1 for "not found"
1641 static int FindContentLength(char *pbuf
, int iLen
)
1643 // non reusable HTTP header parsing code:
1644 // ----------------------------------------------
1648 // find content length header
1649 p
= strcasestr_n(pbuf
, "\r\nContent-Length:", iLen
);
1650 if (p
== NULL
) return -1;
1652 p
+= sizeof("\r\nContent-Length:") - 1; // minus '\0'
1657 // ----------------------------------------------
1660 // returns -1 for "not found"
1661 static int FindBody(char *pbuf
, int iLen
)
1663 // non reusable HTTP header parsing code:
1664 // ----------------------------------------------
1668 // find the empty line
1669 p
= strstr_n(pbuf
, "\r\n\r\n", iLen
);
1670 if (p
== NULL
) return -1;
1672 p
+= sizeof("\r\n\r\n") - 1; // minus '\0'
1675 // ----------------------------------------------
1678 static int SendTCPMsg_saddr_2part(
1679 char *msg
, int iLen
,
1680 char *msg2
, int iLen2
,
1681 char *result
, int resultSize
,
1682 struct sockaddr_in
*saHost
)
1685 struct sockaddr_in saSendTo
;
1694 struct timeval tv_start
;
1695 struct timeval tv_end
;
1696 struct timeval tv_elapsed
;
1698 int iContentLength
= -1;
1699 int iBodyOffset
= -1;
1701 gettimeofday(&tv_start
, NULL
);
1703 if (g_fUPnPEnabled
!= TRUE
) {
1704 //TracePrint(ELL_TRACE, "UPnP not enabled\n");
1705 if (g_fLogging
& NALOG_ERROR
)
1706 fprintf(g_log
, "UPnP not enabled (no UPnP device found yet)\n");
1707 return NA_E_NOT_AVAILABLE
;
1710 s
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
1712 if (g_fLogging
& NALOG_ERROR
)
1713 fprintf(g_log
, "Can't get TCP socket (%d)\n", errno
);
1718 if (setsockopt(s
, IPPROTO_IP
, TCP_NODELAY
, &fND
, sizeof(fND
)) != 0) {
1719 if (g_fLogging
& NALOG_ERROR
)
1720 fprintf(g_log
, "SendTCPMsg/2part: Can't set TCP_NODELAY option!\n");
1721 iRetcode
= NA_E_NET
;
1726 fcntl_flags
= fcntl(s
, F_GETFL
, 0);
1727 fcntl_flags
|= O_NONBLOCK
;
1728 if (fcntl(s
, F_SETFL
, fcntl_flags
) != 0) {
1729 if (g_fLogging
& NALOG_ERROR
)
1730 fprintf(g_log
, "SendTCPMsg/2part: Can't set O_NONBLOCK option!\n");
1731 iRetcode
= NA_E_NET
;
1736 memcpy(&saSendTo
, &g_saddrRouterDesc
, sizeof(saSendTo
));
1738 memcpy(&saSendTo
, saHost
, sizeof(saSendTo
));
1740 iRet
= connect(s
, (struct sockaddr
*) &saSendTo
, sizeof(saSendTo
));
1741 if ((iRet
< 0) && (errno
!= EINPROGRESS
)) {
1742 //TracePrint(ELL_TRACE, "UPnP connect failed\n");
1743 if (g_fLogging
& NALOG_ERROR
)
1744 fprintf(g_log
, "SendTCPMsg/2part: connect failed (%d)\n", errno
);
1745 iRetcode
= NA_E_NET
;
1749 if (g_fLogging
& NALOG_INFO1
)
1751 "- Before Sending TCP Msg1: %d == %lu?\n", iLen
, strlen(msg
));
1752 if (g_fLogging
& NALOG_DUMP
)
1753 fprintf(g_log
, "Sending TCP msg part 1:\n[%s]\n", msg
);
1755 tv
.tv_sec
= g_iFunctionTimeout
/ UPNP_TIMEOUT_GRANULARITY
;
1756 tv
.tv_usec
= (g_iFunctionTimeout
% U_TOGRAN
) * 1000000 / U_TOGRAN
;
1758 FD_SET(s
, &writefds
);
1759 iRet
= select(s
+1, 0, &writefds
, 0, &tv
);
1761 if (g_fLogging
& NALOG_ERROR
)
1762 fprintf(g_log
, "SendTCPMsg/2part: select failed (%d)\n", errno
);
1763 iRetcode
= NA_E_NET
;
1767 if (g_fLogging
& NALOG_ERROR
)
1768 fprintf(g_log
, "SendTCPMsg/2part: select timed out\n");
1769 iRetcode
= NA_E_TIMEOUT
;
1770 gettimeofday(&tv_end
, NULL
);
1771 GetTimeElapsed(&tv_start
, &tv_end
, &tv_elapsed
);
1772 //TracePrint(ELL_TRACE, "UPnP 2part: timeout @1st after %lu.%06lu secs\n",
1773 // tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1777 iRet
= send(s
, msg
, iLen
, 0);
1780 if (g_fLogging
& NALOG_ALERT
)
1781 fprintf(g_log
, "SendTCPMsg/2part: iRet(%d) != strlen(msg)(%d)!\n",
1784 //TracePrint(ELL_TRACE, "UPnP 2part: 1st %d == %d (%d) (%d)?\n", iRet, iLen, strlen(msg), errno);
1786 tv
.tv_sec
= g_iFunctionTimeout
/ UPNP_TIMEOUT_GRANULARITY
;
1787 tv
.tv_usec
= (g_iFunctionTimeout
% U_TOGRAN
) * 1000000 / U_TOGRAN
;
1789 FD_SET(s
, &writefds
);
1790 // calculate how much time elapsed
1791 gettimeofday(&tv_end
, NULL
);
1792 GetTimeElapsed(&tv_start
, &tv_end
, &tv_elapsed
);
1793 if (CompareTime(&tv_elapsed
, &tv
) > 0) {
1795 return NA_E_TIMEOUT
;
1800 // subtract that from timeout accordingly
1801 tv
.tv_sec
-= tv_elapsed
.tv_sec
;
1802 if (tv
.tv_usec
< tv_elapsed
.tv_usec
) {
1804 tv
.tv_usec
= 1000000 + tv
.tv_usec
- tv_elapsed
.tv_usec
;
1807 tv
.tv_usec
= tv
.tv_usec
- tv_elapsed
.tv_usec
;
1809 iRet
= select(s
+1, 0, &writefds
, 0, &tv
);
1811 if (g_fLogging
& NALOG_ERROR
)
1812 fprintf(g_log
, "SendTCPMsg/2part: select2 failed (%d)\n", errno
);
1813 iRetcode
= NA_E_NET
;
1817 if (g_fLogging
& NALOG_ERROR
)
1818 fprintf(g_log
, "SendTCPMsg/2part: select2 timed out\n");
1819 iRetcode
= NA_E_TIMEOUT
;
1820 gettimeofday(&tv_end
, NULL
);
1821 GetTimeElapsed(&tv_start
, &tv_end
, &tv_elapsed
);
1822 //TracePrint(ELL_TRACE, "UPnP 2part: timeout @2nd after %lu.%06lu secs\n",
1823 // tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1827 iRet
= send(s
, msg2
, iLen2
, 0);
1828 if (g_fLogging
& NALOG_INFO1
)
1830 "SendTCPMsg/parse: Before Sending TCP Msg2: %d == %lu?\n",
1831 iLen2
, strlen(msg2
));
1832 if (g_fLogging
& NALOG_DUMP
)
1833 fprintf(g_log
, "Sending TCP msg part 2:\n[%s]\n", msg2
);
1835 //TracePrint(ELL_TRACE, "UPnP 2part: 2nd %d == %d (%d) (%d)?\n", iRet, iLen2, strlen(msg2), errno);
1839 if (g_fLogging
& NALOG_ALERT
)
1840 fprintf(g_log
, "SendTCPMsg/2part: iRet(%d) != strlen(msg2)(%d)!\n",
1843 if (result
== NULL
) { // if caller just want to send/display msgs
1844 if (g_fLogging
& NALOG_DUMP
)
1845 fprintf(g_log
, "TCP Buffer: [");
1848 if (g_fLogging
& NALOG_INFO1
)
1849 fprintf(g_log
, "start recv @%lu\n", time(NULL
));
1852 iContentLength
= -1;
1856 struct timeval timeout
;
1860 FD_SET(s
, &readfds
);
1861 //timeout.tv_sec = g_iFunctionTimeout / U_TOGRAN;
1862 //timeout.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN;
1863 // just do flat 2 sec now, since connection already established
1865 timeout
.tv_usec
= 0;
1868 iRet
= select(s
+1, &readfds
, NULL
, NULL
, &timeout
);
1871 //TracePrint(ELL_TRACE, "UPnP 2part: select timeout? (%d, %d)\n",
1876 //gettimeofday(&tv_end, NULL);
1877 //GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed);
1878 //fprintf(stderr, "2 == loop: %d.%06d\n", tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1880 // if only sending messages
1881 if (result
== NULL
) {
1883 i
= recv(s
, t
, 1000-1, 0); // leave room for '\0' for dump
1885 if (g_fLogging
& NALOG_DUMP
) {
1887 fprintf(g_log
, "%s", t
);
1892 // EO result buf: discard extra bytes
1893 if (resultSize
<= iBufLen
) {
1895 i
= recv(s
, &t
, 1000, 0);
1897 // Note that there's no dump here - prevents DoS attack from
1898 // flooding the logs/diskspace
1902 i
= recv(s
, result
+ iBufLen
, resultSize
- iBufLen
, 0);
1904 //TracePrint(ELL_TRACE, "UPnP 2part: recv done %d (%d, %d)\n",
1905 // iBufLen, i, errno);
1911 // parse and see if we can find content-length to quit early
1912 iContentLength
= FindContentLength(result
, iBufLen
);
1914 // now if we're still in header, see if we can find body
1915 iBodyOffset
= FindBody(result
, iBufLen
);
1917 // now check if we can leave early. conditions are:
1918 // past headers, and we've already recv'ed content-length of body
1919 if ((iBodyOffset
>= 0) &&
1920 (iContentLength
>= 0) &&
1921 ((iBufLen
- iBodyOffset
) >= iContentLength
))
1923 //TracePrint(ELL_TRACE, "UPnP 2part: read all specified %d (%d, %d) (%d, %d)\n",
1924 // iBufLen, i, errno, iBodyOffset, iContentLength);
1929 //fprintf(stderr, "2 -- \n");
1931 if (g_fLogging
& NALOG_INFO1
)
1932 fprintf(g_log
, "done recv @%lu\n", time(NULL
));
1934 if (result
== NULL
) { // if caller just want to send/display msgs
1935 if (g_fLogging
& NALOG_DUMP
)
1936 fprintf(g_log
, "]\n");
1947 static int SendTCPMsg_saddr_parse(
1948 char *msg
, int iLen
,
1949 char *result
, int resultSize
,
1950 struct sockaddr_in
*saHost
)
1953 struct sockaddr_in saSendTo
;
1960 struct timeval tv_start
;
1961 // struct timeval tv_end;
1962 // struct timeval tv_elapsed;
1964 // HTTP parsing vars
1972 select(0, NULL
, NULL
, NULL
, &tv
);
1974 pthread_mutex_lock(&g_xUPnPMsg
);
1976 gettimeofday(&tv_start
, NULL
);
1978 if (g_fUPnPEnabled
!= TRUE
) {
1979 //TracePrint(ELL_TRACE, "UPnP not enabled\n");
1980 if (g_fLogging
& NALOG_ERROR
)
1981 fprintf(g_log
, "UPnP not enabled (no UPnP device found yet)\n");
1982 pthread_mutex_unlock(&g_xUPnPMsg
);
1983 return NA_E_NOT_AVAILABLE
;
1986 s
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
1988 if (g_fLogging
& NALOG_ERROR
)
1989 fprintf(g_log
, "Can't get TCP socket (%d)\n", errno
);
1990 pthread_mutex_unlock(&g_xUPnPMsg
);
1995 fcntl_flags
= fcntl(s
, F_GETFL
, 0);
1996 fcntl_flags
|= O_NONBLOCK
;
1997 if (fcntl(s
, F_SETFL
, fcntl_flags
) != 0) {
1998 if (g_fLogging
& NALOG_ERROR
)
1999 fprintf(g_log
, "SendTCPMsg/parse: Can't set O_NONBLOCK option!\n");
2001 pthread_mutex_unlock(&g_xUPnPMsg
);
2006 memcpy(&saSendTo
, &g_saddrRouterDesc
, sizeof(saSendTo
));
2008 memcpy(&saSendTo
, saHost
, sizeof(saSendTo
));
2010 iRet
= connect(s
, (struct sockaddr
*) &saSendTo
, sizeof(saSendTo
));
2011 if ((iRet
< 0) && (errno
!= EINPROGRESS
)) {
2012 //TracePrint(ELL_TRACE, "UPnP connect failed\n");
2013 if (g_fLogging
& NALOG_ERROR
)
2014 fprintf(g_log
, "SendTCPMsg/parse: connect failed (%d)\n", errno
);
2016 pthread_mutex_unlock(&g_xUPnPMsg
);
2020 if (g_fLogging
& NALOG_INFO1
)
2021 fprintf(g_log
, "SendTCPMsg/parse: Before Sending TCP Msg: %d == %lu?\n",
2023 if (g_fLogging
& NALOG_DUMP
)
2024 fprintf(g_log
,"Sending TCP msg:\n[%s]\n", msg
);
2026 tv
.tv_sec
= g_iFunctionTimeout
/ UPNP_TIMEOUT_GRANULARITY
;
2027 tv
.tv_usec
= (g_iFunctionTimeout
% U_TOGRAN
) * 1000000 / U_TOGRAN
;
2029 FD_SET(s
, &writefds
);
2030 iRet
= select(s
+1, 0, &writefds
, 0, &tv
);
2032 if (g_fLogging
& NALOG_ERROR
)
2033 fprintf(g_log
, "SendTCPMsg/parse: select failed (%d)\n", errno
);
2035 pthread_mutex_unlock(&g_xUPnPMsg
);
2039 if (g_fLogging
& NALOG_ERROR
)
2040 fprintf(g_log
, "SendTCPMsg/parse: select timed out\n");
2042 pthread_mutex_unlock(&g_xUPnPMsg
);
2043 return NA_E_TIMEOUT
;
2046 iRet
= send(s
, msg
, iLen
, 0);
2050 if (g_fLogging
& NALOG_ALERT
)
2051 fprintf(g_log
, "SendTCPMsg: iRet (%d) != strlen(msg) (%d)!\n",
2054 if (result
== NULL
) { // if caller just want to send/display msgs
2055 if (g_fLogging
& NALOG_DUMP
)
2056 fprintf(g_log
, "TCP Buffer: [");
2059 if (g_fLogging
& NALOG_INFO1
)
2060 fprintf(g_log
, "start recv @%lu\n", time(NULL
));
2064 iContentLength
= -1;
2068 struct timeval timeout
;
2072 FD_SET(s
, &readfds
);
2073 //timeout.tv_sec = g_iFunctionTimeout / U_TOGRAN;
2074 //timeout.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN;
2075 // just do flat 2 sec now, since connection already established
2077 timeout
.tv_usec
= 0;
2079 iRet
= select(s
+1, &readfds
, NULL
, NULL
, &timeout
);
2081 //fprintf(stderr, "**********: select failed (%d/%d)\n", iRet, errno);
2085 //gettimeofday(&tv_end, NULL);
2086 //GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed);
2087 //fprintf(stderr, "p == loop: %d.%06d\n", tv_elapsed.tv_sec, tv_elapsed.tv_usec);
2089 // if only sending messages
2090 if (result
== NULL
) {
2092 i
= recv(s
, t
, 1000-1, 0); // leave room for '\0' for dump
2094 if (g_fLogging
& NALOG_DUMP
) {
2096 fprintf(g_log
, "%s", t
);
2101 // EO result buf: discard extra bytes
2102 if (resultSize
<= iBufLen
) {
2104 i
= recv(s
, &t
, 1000, 0);
2106 // Note that there's no dump here - prevents DoS attack from
2107 // flooding the logs/diskspace
2111 i
= recv(s
, result
+ iBufLen
, resultSize
- iBufLen
, 0);
2117 if (EAGAIN
== errno
) continue;
2123 // parse and see if we can find content-length to quit early
2124 iContentLength
= FindContentLength(result
, iBufLen
);
2126 // now if we're still in header, see if we can find body
2127 iBodyOffset
= FindBody(result
, iBufLen
);
2131 //fprintf(stderr, "p -- \n");
2133 if (g_fLogging
& NALOG_INFO1
)
2134 fprintf(g_log
, "done recv @%lu\n", time(NULL
));
2136 if (result
== NULL
) { // if caller just want to send/display msgs
2137 if (g_fLogging
& NALOG_DUMP
)
2138 fprintf(g_log
, "]\n");
2142 pthread_mutex_unlock(&g_xUPnPMsg
);
2148 // szSOAPMsgControlAHeaderFMT - 4 args (ctrl_url, host/port, action, length)
2149 // szSOAPMsgControlABodyFMT - 2 args (action, args string)
2150 // szSOAPMsgControlAArgumentFMT - 2 args (name/value)
2151 static PHTTPResponse
SendSOAPMsgControlAction(
2157 //char outBuffer[65536];
2158 //char outBufferBody[65536];
2159 //char outBufferArgs[65536];
2160 char *outBuffer
= NULL
;
2161 char *outBufferBody
= NULL
;
2162 char *outBufferArgs
= NULL
;
2163 char *inBuffer
= NULL
;
2171 PHTTPResponse pResponse
= NULL
;
2174 if (!WaitUPnPFunction())
2177 if ((outBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2178 if (g_fLogging
& NALOG_ERROR
)
2179 fprintf(g_log
, "can't malloc for outBuffer\n");
2182 if ((outBufferBody
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2183 if (g_fLogging
& NALOG_ERROR
)
2184 fprintf(g_log
, "can't malloc for outBufferBody\n");
2187 if ((outBufferArgs
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2188 if (g_fLogging
& NALOG_ERROR
)
2189 fprintf(g_log
, "can't malloc for outBufferArgs\n");
2192 if ((inBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2193 if (g_fLogging
& NALOG_ERROR
)
2194 fprintf(g_log
, "can't malloc for inBuffer\n");
2200 for (i
=0; i
<argc
; i
++) {
2202 if (args
[i
].pszType
== NULL
) {
2203 n
= sprintf(outBufferArgs
+ iArgsLen
,
2204 szSOAPMsgControlAArgumentFMT
,
2205 args
[i
].pszName
, args
[i
].pszValue
);
2208 n
= sprintf(outBufferArgs
+ iArgsLen
,
2209 szSOAPMsgControlAArgumentFMT_t
,
2210 args
[i
].pszName
, args
[i
].pszValue
, args
[i
].pszType
);
2214 outBufferArgs
[iArgsLen
] = '\0';
2216 iBodyLen
= sprintf(outBufferBody
, szSOAPMsgControlABodyFMT
,
2217 action
, outBufferArgs
);
2219 iHeaderLen
= sprintf(outBuffer
, szSOAPMsgControlAHeaderFMT
,
2220 g_szControlURL
, g_szRouterHostPortSOAP
, action
, iBodyLen
);
2223 DumpHex(outBuffer
, iHeaderLen
+1);
2224 DumpHex(outBufferBody
, iBodyLen
+1);
2225 iResultLen
= SendTCPMsg_saddr_2part(
2226 outBuffer
, iHeaderLen
,
2227 outBufferBody
, iBodyLen
,
2228 inBuffer
, MAX_SOAPMSGSIZE
,
2229 &g_saddrRouterSOAP
);
2232 strcpy(outBuffer
+ iHeaderLen
, outBufferBody
);
2233 iLen
= iHeaderLen
+ iBodyLen
;
2235 DumpHex(outBuffer
, iLen
+1);
2237 //strcat(outBuffer, CRLF "0" CRLF CRLF);
2240 iResultLen
= SendTCPMsg_saddr_parse(
2242 inBuffer
, MAX_SOAPMSGSIZE
,
2243 &g_saddrRouterSOAP
);
2246 if (iResultLen
> 0) {
2247 if (iResultLen
> MAX_SOAPMSGSIZE
) {
2248 if (g_fLogging
& NALOG_ALERT
)
2249 fprintf(g_log
, "result truncated..\n");
2250 iResultLen
= MAX_SOAPMSGSIZE
;
2252 pResponse
= NewHTTPResponse_sz(inBuffer
, iResultLen
, FALSE
);
2253 if (pResponse
!= NULL
) {
2254 PrintHTTPResponse(pResponse
);
2255 //DeleteHTTPResponse(pResponse);
2256 // - return response to caller
2260 if (g_fLogging
& NALOG_ERROR
)
2261 fprintf(g_log
, "No TCP Response\n");
2262 //TracePrint(ELL_TRACE, "UPnP SendSOAPMsg got no TCP response (%d)\n",
2267 if (outBuffer
!= NULL
) free(outBuffer
);
2268 if (outBufferBody
!= NULL
) free(outBufferBody
);
2269 if (outBufferArgs
!= NULL
) free(outBufferArgs
);
2270 if (inBuffer
!= NULL
) free(inBuffer
);
2275 static int FindURLBase(char *pbuf
, int iLen
, char *szURLBase
)
2277 // non reusable XML parsing code:
2278 // ----------------------------------------------
2282 // now skip after end of this tag, then skip until controlURL tag
2283 p
= strstr_n(pbuf
, "<URLBase>", iLen
);
2284 if (p
== NULL
) return -1;
2286 // skip to the actual stuff
2287 p
+= sizeof("<URLBase>") - 1; // minus '\0'
2289 // skip white spaces (just in case)
2293 // copy into szURLBase
2294 while ((*p
!= '\0') && (*p
!= '<') && !isspace(*p
)) {
2295 if (i
++ > 1000) break;
2303 // ----------------------------------------------
2307 static int FindDescInfo(
2310 const char *szParentName
,
2319 // find the device within pbuf
2331 // now skip after end of this tag, then skip until manufacturer tag
2332 iSearchLen
= sprintf(szSearch
, "<%s>", szName
);
2333 p
= strstr_n(pbuf
, szSearch
, iLen
);
2334 if (p
== NULL
) return -1;
2337 // skip white spaces (just in case)
2341 // copy into szValue
2342 while ((*p
!= '\0') && (*p
!= '<')) {
2343 if (i
++ > 1000) break;
2353 static int FindIGDInfo(char *pbuf
, int iLen
, const char *szName
, char *szValue
)
2355 return FindDescInfo(
2357 "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
2361 static int FindManufacturer(char *pbuf
, int iLen
, char *szManuf
)
2363 return FindIGDInfo(pbuf
, iLen
, "manufacturer", szManuf
);
2366 static int FindFriendlyName(char *pbuf
, int iLen
, char *szValue
)
2368 return FindIGDInfo(pbuf
, iLen
, "friendlyName", szValue
);
2371 static int FindModelName(char *pbuf
, int iLen
, char *szValue
)
2373 return FindIGDInfo(pbuf
, iLen
, "modelName", szValue
);
2376 static int FindModelDescription(char *pbuf
, int iLen
, char *szValue
)
2378 return FindIGDInfo(pbuf
, iLen
, "modelDescription", szValue
);
2381 static int FindWANIPInfo(char *pbuf
, int iLen
, const char *szName
, char *szValue
)
2383 return FindDescInfo(
2385 "urn:schemas-upnp-org:service:WANIPConnection:1",
2389 static int FindControlURL(char *pbuf
, int iLen
, char *szControlURL
)
2391 return FindWANIPInfo(pbuf
, iLen
, "controlURL", szControlURL
);
2394 static int FindEventURL(char *pbuf
, int iLen
, char *szEventURL
)
2396 return FindWANIPInfo(pbuf
, iLen
, "eventSubURL", szEventURL
);
2399 static int FindRouterInfo(char *inBuffer
, int iLen
)
2401 if (FindManufacturer(inBuffer
, iLen
, g_szManufacturer
) != 0)
2402 g_szManufacturer
[0] = '\0';
2404 if (FindFriendlyName(inBuffer
, iLen
, g_szFriendlyName
) != 0)
2405 g_szFriendlyName
[0] = '\0';
2407 if (FindModelName(inBuffer
, iLen
, g_szModelName
) != 0)
2408 g_szModelName
[0] = '\0';
2410 if (FindModelDescription(inBuffer
, iLen
, g_szModelDescription
) != 0)
2411 g_szModelDescription
[0] = '\0';
2413 //TracePrint(ELL_TRACE,
2414 // "UPnP Router Info:\n"
2415 // " - manufacturer [%s]\n"
2416 // " - friendly name [%s]\n"
2417 // " - model name [%s]\n"
2418 // " - model desc [%s]\n",
2419 // g_szManufacturer, g_szFriendlyName, g_szModelName, g_szModelDescription);
2424 static void ParseURL(
2425 const char *szBuf
, char *pszHostPort
,
2426 struct sockaddr_in
*psaddr
, char *pszPath
)
2431 unsigned short port
;
2436 if (0 == strncmp(p
, "http://", 7))
2452 // find the port separetor
2458 // HTTP's by default port 80, so don't have it in the "Host:" header
2459 if (80 == port
) *q
= '\0';
2462 if (pszHostPort
) strcpy(pszHostPort
, p
);
2464 if (NULL
!= q
) *q
= '\0';
2466 if (NULL
!= psaddr
) {
2467 psaddr
->sin_family
= AF_INET
;
2468 psaddr
->sin_addr
.s_addr
= inet_addr(p
);
2469 psaddr
->sin_port
= htons(port
);
2472 //TracePrint(ELL_TRACE, "ParseURL [%s] -> [%s][%s] %lu.%lu.%lu.%lu:%u\n",
2474 pszHostPort
?pszHostPort
:"",
2476 (psaddr
->sin_addr
.s_addr
>> 24) & 0xff,
2477 (psaddr
->sin_addr
.s_addr
>> 16) & 0xff,
2478 (psaddr
->sin_addr
.s_addr
>> 8) & 0xff,
2479 (psaddr
->sin_addr
.s_addr
>> 0) & 0xff,
2484 static void GetDeviceDescription(void)
2486 char *outBuffer
= NULL
;
2487 char *inBuffer
= NULL
;
2490 char szURLBase
[1024];
2491 char szControlURL
[1024];
2492 char szEventURL
[1024];
2494 if (!g_fUPnPEnabled
) {
2495 if (g_fLogging
& NALOG_ERROR
)
2496 fprintf(g_log
, "GetDeviceDescription: upnp not enabled\n");
2500 if ((outBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2501 if (g_fLogging
& NALOG_ERROR
)
2502 fprintf(g_log
, "can't malloc for outBuffer\n");
2505 if ((inBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2506 if (g_fLogging
& NALOG_ERROR
)
2507 fprintf(g_log
, "can't malloc for inBuffer\n");
2511 iBufLen
= sprintf(outBuffer
, szSSDPMsgDescribeDeviceFMT
, g_szNATDevDescURL
,
2512 g_szRouterHostPortDesc
);
2514 if (g_fLogging
& NALOG_INFO1
)
2515 fprintf(g_log
, "Describe Device: [%s]\n", outBuffer
);
2516 iLen
= SendTCPMsg_saddr_parse(outBuffer
, iBufLen
, inBuffer
, MAX_SOAPMSGSIZE
,
2517 &g_saddrRouterDesc
);
2519 g_fControlURLSet
= FALSE
;
2521 if (FindControlURL(inBuffer
, iLen
, szControlURL
) != 0) {
2522 if (g_fLogging
& NALOG_ERROR
)
2523 fprintf(g_log
, "GetDeviceDesc: can't find control URL\n");
2527 // start modifying global
2528 pthread_mutex_lock(&g_xUPnP
);
2531 // now see if there's the URLBase
2532 if (FindURLBase(inBuffer
, iLen
, szURLBase
) != 0) {
2533 // not there? try default numbers from device description
2534 memcpy(&g_saddrRouterBase
, &g_saddrRouterDesc
,
2535 sizeof(g_saddrRouterBase
));
2536 strcpy(g_szRouterHostPortBase
, g_szRouterHostPortDesc
);
2540 g_szRouterHostPortBase
, &g_saddrRouterBase
, NULL
);
2542 if ((strlen(g_szRouterHostPortBase
) == 0) ||
2543 (g_saddrRouterBase
.sin_addr
.s_addr
== INADDR_NONE
)) {
2544 memcpy(&g_saddrRouterBase
, &g_saddrRouterDesc
,
2545 sizeof(g_saddrRouterBase
));
2546 strcpy(g_szRouterHostPortBase
, g_szRouterHostPortDesc
);
2551 ParseURL(szControlURL
,
2552 g_szRouterHostPortSOAP
, &g_saddrRouterSOAP
, g_szControlURL
);
2553 if ((strlen(g_szRouterHostPortSOAP
) == 0) ||
2554 (g_saddrRouterSOAP
.sin_addr
.s_addr
== INADDR_NONE
)) {
2555 memcpy(&g_saddrRouterSOAP
, &g_saddrRouterBase
,
2556 sizeof(g_saddrRouterSOAP
));
2557 strcpy(g_szRouterHostPortSOAP
, g_szRouterHostPortBase
);
2561 ////TracePrint(ELL_TRACE, "UPnP Control URL set to[%s][%s]...\n",
2562 // g_szRouterHostPortSOAP, g_szControlURL);
2564 g_fControlURLSet
= TRUE
;
2565 gettimeofday(&g_tvLastUpdateTime
, NULL
);
2566 pthread_cond_broadcast(&g_condUPnPControlURL
);
2568 if (g_fLogging
& NALOG_INFO1
)
2569 fprintf(g_log
, "Got Device Description\n");
2572 FindRouterInfo(inBuffer
, iLen
);
2574 if (FindEventURL(inBuffer
, iLen
, szEventURL
) != 0) {
2575 szEventURL
[0] = '\0';
2578 ParseURL(szEventURL
,
2579 g_szRouterHostPortEvent
, &g_saddrRouterEvent
, g_szEventURL
);
2580 if ((strlen(g_szRouterHostPortEvent
) == 0) ||
2581 (g_saddrRouterEvent
.sin_addr
.s_addr
== INADDR_NONE
)) {
2582 memcpy(&g_saddrRouterEvent
, &g_saddrRouterBase
,
2583 sizeof(g_saddrRouterEvent
));
2584 strcpy(g_szRouterHostPortEvent
, g_szRouterHostPortBase
);
2591 if (outBuffer
!= NULL
) free(outBuffer
);
2592 if (inBuffer
!= NULL
) free(inBuffer
);
2594 pthread_mutex_unlock(&g_xUPnP
);
2598 static void GetIPByName(char *hostname
, unsigned long *ip_ret
)
2602 ip
= inet_addr(hostname
);
2603 if (ip
== INADDR_NONE
) {
2604 struct hostent
*pHEnt
;
2605 pHEnt
= gethostbyname(hostname
);
2606 if (pHEnt
== NULL
) {
2607 if (g_fLogging
& NALOG_ALERT
)
2608 fprintf(g_log
, "Can't translate [%s] to IP...\n", hostname
);
2609 g_dwLocalIP
= htonl(INADDR_ANY
);
2612 ip
= ntohl(*(unsigned long *)(pHEnt
->h_addr
));
2613 if (g_fLogging
& NALOG_INFO1
)
2614 fprintf(g_log
, "hostname [%s] to ip: %ld.%ld.%ld.%ld\n",
2624 static void SetLocalIP()
2626 PIPINFO pIPInfo
= NULL
;
2627 int count
= GetIPInfo(&pIPInfo
);
2628 if (NULL
!= pIPInfo
)
2630 // choose first non IPV6 address
2631 // iterate through array and set port information
2633 unsigned long dwFirst
= 0;
2634 for(i
= 0; i
< count
; i
++)
2636 if (!(pIPInfo
[i
].iFlags
& ISIPV6
) &&
2637 (strncmp(pIPInfo
[i
].szIfName
, "ppp", 3) != 0))
2639 unsigned long dwTemp
;
2641 memcpy(&dwTemp
, pIPInfo
[i
].abIP
, sizeof(unsigned long));
2643 if (0 != GetNATIPNetmask(dwTemp
)) {
2644 g_dwLocalIP
= dwTemp
;
2653 g_dwLocalIP
= dwFirst
;
2654 FreeIPInfo(pIPInfo
);
2659 static int FindTagContent(const char *text
, const char *tagname
, char *buf
)
2663 p
= strstr(text
, tagname
);
2665 if (g_fLogging
& NALOG_INFO0
)
2666 fprintf(g_log
, "FindTagContent: can't find %s\n", tagname
);
2667 return NA_E_PARSE_ERROR
;
2670 if (sscanf(p
, "%*[^>]> %[^ <] <", buf
) < 1) {
2671 if (g_fLogging
& NALOG_INFO0
)
2672 fprintf(g_log
, "FindTagContent: Can't parse tag %s\n", tagname
);
2673 return NA_E_PARSE_ERROR
;
2676 return NA_E_SUCCESS
;
2679 mStatus
LNT_UnmapPort(mDNSIPPort PubPort
, mDNSBool tcp
)
2683 //char szRemoteHost[1024];
2684 //unsigned long dwIP;
2685 Property propArgs
[3];
2687 unsigned short port
= PubPort
.NotAnInteger
;
2688 int protocol
= tcp
? IPPROTO_TCP
: IPPROTO_UDP
;
2689 sprintf(szEPort
, "%u", port
);
2691 bzero(propArgs
, sizeof(propArgs
));
2692 propArgs
[0].pszName
= "NewRemoteHost";
2693 propArgs
[0].pszValue
= "";
2694 propArgs
[0].pszType
= "string";
2695 propArgs
[1].pszName
= "NewExternalPort";
2696 propArgs
[1].pszValue
= szEPort
;
2697 propArgs
[1].pszType
= "ui2";
2698 propArgs
[2].pszName
= "NewProtocol";
2699 if (protocol
== IPPROTO_TCP
) {
2700 propArgs
[2].pszValue
= "TCP";
2702 else if (protocol
== IPPROTO_UDP
) {
2703 propArgs
[2].pszValue
= "UDP";
2708 propArgs
[2].pszType
= "string";
2710 resp
= SendSOAPMsgControlAction(
2711 "DeletePortMapping", 3, propArgs
, FALSE
);
2713 return mStatus_NATTraversal
;
2716 if (strcmp(resp
->pszStatus
, "200") != 0) {
2717 DeleteHTTPResponse(resp
);
2718 return mStatus_NATTraversal
;
2721 DeleteHTTPResponse(resp
);
2722 return mStatus_NoError
;
2726 static int GetMappingUnused(unsigned short eport
, int protocol
);
2728 extern mStatus
LNT_MapPort(mDNSIPPort priv
, mDNSIPPort pub
, mDNSBool tcp
)
2735 Property propArgs
[8];
2737 unsigned short iport
= priv
.NotAnInteger
;
2738 unsigned short eport
= pub
.NotAnInteger
;
2739 int protocol
= tcp
? IPPROTO_TCP
: IPPROTO_UDP
;
2742 if (NA_E_EXISTS
== GetMappingUnused(eport
, protocol
))
2743 return mStatus_AlreadyRegistered
;
2745 //DeletePortMapping(eport, protocol);
2747 sprintf(szEPort
, "%u", eport
);
2749 sprintf(szIPort
, "%u", iport
);
2752 sprintf(szLocalIP
, "%u.%u.%u.%u",
2753 (unsigned int)((dwIP
>> 24) & 0xff),
2754 (unsigned int)((dwIP
>> 16) & 0xff),
2755 (unsigned int)((dwIP
>> 8) & 0xff),
2756 (unsigned int)((dwIP
>> 0) & 0xff));
2758 bzero(propArgs
, sizeof(propArgs
));
2759 propArgs
[0].pszName
= "NewRemoteHost";
2760 propArgs
[0].pszValue
= "";
2761 propArgs
[0].pszType
= "string";
2762 propArgs
[1].pszName
= "NewExternalPort";
2763 propArgs
[1].pszValue
= szEPort
;
2764 propArgs
[1].pszType
= "ui2";
2765 propArgs
[2].pszName
= "NewProtocol";
2766 if (protocol
== IPPROTO_TCP
) {
2767 propArgs
[2].pszValue
= "TCP";
2769 else if (protocol
== IPPROTO_UDP
) {
2770 propArgs
[2].pszValue
= "UDP";
2773 return mStatus_BadParamErr
;
2775 propArgs
[2].pszType
= "string";
2776 propArgs
[3].pszName
= "NewInternalPort";
2777 propArgs
[3].pszValue
= szIPort
;
2778 propArgs
[3].pszType
= "ui2";
2779 propArgs
[4].pszName
= "NewInternalClient";
2780 propArgs
[4].pszValue
= szLocalIP
;
2781 propArgs
[4].pszType
= "string";
2782 propArgs
[5].pszName
= "NewEnabled";
2783 propArgs
[5].pszValue
= "1";
2784 propArgs
[5].pszType
= "boolean";
2785 propArgs
[6].pszName
= "NewPortMappingDescription";
2786 sprintf(descr
, "iC%u", eport
);
2787 //propArgs[6].pszValue = "V";
2788 propArgs
[6].pszValue
= descr
;
2789 propArgs
[6].pszType
= "string";
2790 propArgs
[7].pszName
= "NewLeaseDuration";
2791 propArgs
[7].pszValue
= "0";
2792 propArgs
[7].pszType
= "ui4";
2794 resp
= SendSOAPMsgControlAction(
2795 "AddPortMapping", 8, propArgs
, FALSE
);
2798 return mStatus_NATTraversal
;
2801 if (strcmp(resp
->pszStatus
, "200") != 0) {
2802 DeleteHTTPResponse(resp
);
2803 return mStatus_NATTraversal
;
2806 DeleteHTTPResponse(resp
);
2807 return mStatus_NoError
;
2810 static int GetMappingUnused(unsigned short eport
, int protocol
)
2814 Property propArgs
[3];
2816 unsigned long ip
= 0;
2818 sprintf( szPort
, "%u", eport
);
2820 bzero(&propArgs
, sizeof(propArgs
));
2821 propArgs
[0].pszName
= "NewRemoteHost";
2822 propArgs
[0].pszValue
= "";
2823 propArgs
[0].pszType
= "string";
2824 propArgs
[1].pszName
= "NewExternalPort";
2825 propArgs
[1].pszValue
= szPort
;
2826 propArgs
[1].pszType
= "ui2";
2827 propArgs
[2].pszName
= "NewProtocol";
2828 if (protocol
== IPPROTO_TCP
) {
2829 propArgs
[2].pszValue
= "TCP";
2831 else if (protocol
== IPPROTO_UDP
) {
2832 propArgs
[2].pszValue
= "UDP";
2835 return NA_E_INVALID_PARAMETER
;
2837 propArgs
[2].pszType
= "string";
2839 resp
= SendSOAPMsgControlAction(
2840 "GetSpecificPortMappingEntry", 3, propArgs
, FALSE
);
2842 if ((strcmp(resp
->pszStatus
, "200") == 0) &&
2843 (FindTagContent(resp
->pszBody
, "NewInternalClient", buf
) == 0))
2845 GetIPByName(buf
, &ip
);
2846 if (ip
== g_dwLocalIP
) {
2847 // (perhaps we let it go?)
2848 DeleteHTTPResponse(resp
);
2849 return NA_E_SUCCESS
;
2852 DeleteHTTPResponse(resp
);
2856 DeleteHTTPResponse(resp
);
2859 return NA_E_SUCCESS
;
2862 mStatus
LNT_GetPublicIP(mDNSOpaque32
*IpPtr
)
2866 static struct timeval tvLastGoodIP
= {0,0};
2867 static unsigned long dwLastGoodIP
;
2869 unsigned long *ip
= (unsigned long *)IpPtr
;
2870 if (ip
== NULL
) return mStatus_BadParamErr
;
2872 gettimeofday(&tv
, NULL
);
2873 GetTimeElapsed(&tvLastGoodIP
, &tv
, &tv
);
2876 return dwLastGoodIP
;
2879 resp
= SendSOAPMsgControlAction(
2880 "GetExternalIPAddress", 0, NULL
, FALSE
);
2883 return mStatus_NATTraversal
;
2885 if (FindTagContent(resp
->pszBody
, "NewExternalIPAddress", buf
) == 0) {
2886 if (g_fLogging
& NALOG_INFO1
)
2887 fprintf(g_log
, "Mapped remote host = %s\n", buf
);
2888 *ip
= inet_addr(buf
);
2889 DeleteHTTPResponse(resp
);
2891 gettimeofday(&tvLastGoodIP
, NULL
);
2894 return mStatus_NoError
;
2897 DeleteHTTPResponse(resp
);
2898 return mStatus_NATTraversal
;
2901 static void SendDiscoveryMsg()
2903 // do it twice to avoid lost packet
2904 //SendUDPMsg(szSSDPMsgDiscoverNAT);
2905 SendUDPMsg(szSSDPMsgDiscoverRoot
);
2906 SendUDPMsg(szSSDPMsgDiscoverIGD
);
2907 SendUDPMsg(szSSDPMsgDiscoverNAT
);
2910 // Set up threads for upnp responses, etc.
2911 int LegacyNATInit(void)
2913 //pthread_t UDPthread;
2914 pthread_attr_t attr
;
2916 //struct timeval tv;
2918 static int fFirstInitLocks
= TRUE
;
2928 if (fFirstInitLocks
)
2931 if (pthread_mutex_init(&g_xUPnP
, NULL
)) {
2932 if (g_fLogging
& NALOG_ERROR
)
2933 fprintf(log
, "UpnpInit - mutex init failed\n");
2934 return NA_E_INTERNAL_ERROR
;
2936 if (pthread_cond_init(&g_condUPnP
, NULL
)) {
2937 pthread_mutex_destroy(&g_xUPnP
);
2938 if (g_fLogging
& NALOG_ERROR
)
2939 fprintf(log
, "UpnpInit - cond init failed\n");
2940 return NA_E_INTERNAL_ERROR
;
2942 if (pthread_cond_init(&g_condUPnPControlURL
, NULL
)) {
2943 pthread_mutex_destroy(&g_xUPnP
);
2944 pthread_cond_destroy(&g_condUPnP
);
2945 if (g_fLogging
& NALOG_ERROR
)
2946 fprintf(log
, "UpnpInit - cond init failed\n");
2947 return NA_E_INTERNAL_ERROR
;
2949 if (pthread_mutex_init(&g_xUPnPMsg
, NULL
)) {
2950 pthread_mutex_destroy(&g_xUPnP
);
2951 pthread_cond_destroy(&g_condUPnP
);
2952 pthread_cond_destroy(&g_condUPnPControlURL
);
2953 if (g_fLogging
& NALOG_ERROR
)
2954 fprintf(log
, "UpnpInit - mutex init failed\n");
2955 return NA_E_INTERNAL_ERROR
;
2958 fFirstInitLocks
= FALSE
;
2963 // initialize UDP socket for SSDP
2964 g_sUDP
= SSDPListen();
2965 g_sUDPCancel
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
); // sock to signal canccelation to UDP thread
2966 if (g_sUDP
< 0 || g_sUDPCancel
< 0) {
2967 if (g_fLogging
& NALOG_ERROR
)
2968 fprintf(log
, "UpnpInit - Failed to init multicast socket.\n");
2969 return NA_E_INTERNAL_ERROR
;
2973 pthread_attr_init(&attr
);
2974 iRet
= pthread_create(&g_UDPthread
, &attr
, UDPProc
, log
);
2976 g_fFirstInit
= TRUE
; // so we'll redo this part next time
2979 if (g_fLogging
& NALOG_ERROR
)
2980 fprintf(log
, "UpnpInit - pthread create failed (%d)\n", iRet
);
2981 return NA_E_THREAD_ERROR
;
2984 // set this to FALSE only if first call succeeded
2985 g_fFirstInit
= FALSE
;
2987 //TracePrint(ELL_TRACE, "UPnP init passed\n");
2990 //tv.tv_usec = 20000; // wait 20ms for thread/udp/multicast init
2991 //select(0, 0, 0, 0, &tv);
2994 // send discovery message
2997 return NA_E_SUCCESS
;
3000 int LegacyNATDestroy()
3002 void *UDPThreadRetVal
;
3004 if (g_sTCPCancel
>= 0) close(g_sTCPCancel
);
3005 if (g_sUDPCancel
>= 0) close(g_sUDPCancel
);
3006 pthread_join(g_UDPthread
, &UDPThreadRetVal
);
3009 g_fFirstInit
= TRUE
;
3010 g_fUPnPEnabled
= FALSE
;
3011 g_fControlURLSet
= FALSE
;
3012 return NA_E_SUCCESS
;