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.14.2.1 2005/12/12 17:38:40 cheshire
28 Put buffer overflow bug 4151514 back in by order of Program CCC:
29 "Program CCC Denied. This change does not meet the criteria for Chardonnay."
31 Revision 1.14 2005/12/08 03:00:33 cheshire
32 <rdar://problem/4349971> Byte order bugs in Legacy NAT traversal code
34 Revision 1.13 2005/09/07 18:23:05 ksekar
35 <rdar://problem/4151514> Off-by-one overflow in LegacyNATTraversal
37 Revision 1.12 2005/07/22 21:36:16 ksekar
38 Fix GCC 4.0/Intel compiler warnings
40 Revision 1.11 2004/12/03 03:34:20 ksekar
41 <rdar://problem/3882674> LegacyNATTraversal.c leaks threads
43 Revision 1.10 2004/12/01 02:43:49 cheshire
44 Update copyright message
46 Revision 1.9 2004/10/27 02:25:05 cheshire
47 <rdar://problem/3816029> Random memory smashing bug
49 Revision 1.8 2004/10/27 02:17:21 cheshire
50 Turn off "safe_close: ERROR" error messages -- there are too many of them
52 Revision 1.7 2004/10/26 21:15:40 cheshire
53 <rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0
54 Additional fixes: Code should set fds to -1 after closing sockets.
56 Revision 1.6 2004/10/26 20:59:20 cheshire
57 <rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0
59 Revision 1.5 2004/10/26 01:01:35 cheshire
60 Use "#if 0" instead of commenting out code
62 Revision 1.4 2004/10/10 06:51:36 cheshire
63 Declared some strings "const" as appropriate
65 Revision 1.3 2004/09/21 23:40:12 ksekar
66 <rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure
68 Revision 1.2 2004/09/17 01:08:52 cheshire
69 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
70 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
71 declared in that file are ONLY appropriate to single-address-space embedded applications.
72 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
74 Revision 1.1 2004/08/18 17:35:41 ksekar
75 <rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways
80 #include "mDNSEmbeddedAPI.h"
81 #include "mDNSMacOSX.h"
93 #include <sys/types.h>
94 #include <sys/socket.h>
95 #include <netinet/in.h>
96 #include <netinet/tcp.h>
99 #include <sys/ioctl.h>
101 #include <netinet/in.h>
102 #include <arpa/inet.h>
103 #include <sys/sysctl.h>
104 #include <net/route.h>
107 #include <arpa/inet.h>
109 //#include "IPAddr.h"
114 //#include "netaddr.h"
116 // TODO: remove later and do variable length
117 #define MAX_SOAPMSGSIZE 65536
119 // This code accidentally closes fd 0 all over the place
120 // To stop that messing up the mDNSResponder core, we trap it and prevent it
121 static int safe_close(int fd
)
123 if (fd
< 3) { /* LogMsg("safe_close: ERROR sd %d < 3", fd); */ return(-1); }
127 #define close safe_close
129 // This code uses fprintf(stderr, ...) and similar to log error messages
130 // We redirect all of them to syslog using our LogMsg mechanism
131 #define fprintf(file, ...) LogMsg(__VA_ARGS__)
133 ////////////////////////////////////////////////////////////////////////
135 ////////////////////////////////////////////////////////////////////////
138 #define NA_E_SUCCESS (0)
139 #define NA_E_INTERNAL_ERROR (-1) /* somewhere something wrong */
140 #define NA_E_INVALID_PARAMETER (-2) /* bad params */
141 #define NA_E_OPERATION_FAILED (-3) /* can't fulfill request */
142 #define NA_E_TIMEOUT (-4) /* operation timed out */
143 #define NA_E_THREAD_ERROR (-5) /* some error related to threads */
144 #define NA_E_PARSE_ERROR (-6) /* a parsing error occured */
145 #define NA_E_NOT_READY (-7) /* this op can't proceed yet */
146 #define NA_E_NOT_FOUND (-8) /* resource/prereq not found */
147 #define NA_E_NOT_AVAILABLE (-9) /* service not available */
148 #define NA_E_EXISTS (-10) /* can't modify existing item */
149 #define NA_E_AGAIN (-11) /* something wrong - try again */
150 #define NA_E_NOT_SUPPORTED (-12) /* wait until next version */
151 #define NA_E_ABORT (-14) /* operation aborted */
152 #define NA_E_NET (-15) /* network layer problem */
154 // Logging flags - log types (increasing degree of detail)
155 #define NALOG_ERROR (1UL) /* error messages */
156 #define NALOG_ALERT (2UL) /* useful warning/alerts */
157 #define NALOG_INFO0 (4UL) /* info - potential problem */
158 #define NALOG_INFO1 (8UL) /* extra info */
159 #define NALOG_DUMP (16UL) /* data dumps */
161 #define NALOG_RSRV1 (32UL) /* reserved */
162 #define NALOG_RSRV2 (64UL) /* reserved */
163 #define NALOG_RSRV3 (128UL) /* reserved */
165 // Logging flags - component (not used for now)
166 #define NALOG_UPNP (256) /* UPnP */
168 // Default Logging levels
169 #define NALOG_LEVEL0 (0)
170 #define NALOG_LEVEL1 (NALOG_UPNP | NALOG_ERROR)
171 #define NALOG_LEVEL2 (NALOG_LEVEL1 | NALOG_ALERT)
172 #define NALOG_LEVEL3 (NALOG_LEVEL2 | NALOG_INFO0)
173 #define NALOG_LEVEL4 (NALOG_LEVEL3 | NALOG_INFO1)
174 #define NALOG_LEVEL5 (NALOG_LEVEL4 | NALOG_DUMP)
175 #define NALOG_DEFAULT_LEVEL (NALOG_LEVEL2)
177 // Default timeout values (in m-seconds (milli))
178 // 50 milliseconds for function timeout
179 #define NA_DEFAULT_FUNCTION_TIMEOUT (50)
181 ////////////////////////////////////////////////////////////////////////
183 ////////////////////////////////////////////////////////////////////////
184 #define SSDP_IP "239.255.255.250"
185 #define SSDP_PORT 1900
189 #define H_CRLF "\r\n"
190 // SOAP message's CRLF:
191 //#define S_CRLF "\r\n"
194 // standard 200 ok msg
195 #define HTTP200OK "HTTP/1.1 200 OK\r\n\r\n"
196 #define HTTP200OKLEN (sizeof(HTTP200OK) - 1)
198 // maximum time to wait for an event (in microseconds)
199 #define MAX_EXPECTEVENTTIME (10000)
201 ////////////////////////////////////////////////////////////////////////
203 ////////////////////////////////////////////////////////////////////////
204 typedef struct tagProperty
{
208 } Property
, *PProperty
;
210 typedef struct tagHTTPResponse
{
214 Property aHeaders
[30]; // assume at most this many headers
220 } HTTPResponse
, *PHTTPResponse
, **PPHTTPResponse
;
222 ////////////////////////////////////////////////////////////////////////
224 ////////////////////////////////////////////////////////////////////////
225 static const char szSSDPMsgDiscoverRoot
[] =
226 "M-SEARCH * HTTP/1.1\r\n"
227 "Host:239.255.255.250:1900\r\n"
228 "ST:upnp:rootdevice\r\n"
229 "Man:\"ssdp:discover\"\r\n"
233 static const char szSSDPMsgDiscoverIGD
[] =
234 "M-SEARCH * HTTP/1.1\r\n"
235 "Host:239.255.255.250:1900\r\n"
236 "ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
237 "Man:\"ssdp:discover\"\r\n"
241 static const char szSSDPMsgDiscoverNAT
[] =
242 "M-SEARCH * HTTP/1.1\r\n"
243 "Host:239.255.255.250:1900\r\n"
244 "ST:urn:schemas-upnp-org:service:WANIPConnection:1\r\n"
245 "Man:\"ssdp:discover\"\r\n"
249 //// Subscribe message
251 // 2$s: local's host/port ("host:port")
252 // 3$s: router's host/port ("host:port")
253 // 4$d: subscription timeout in seconds
254 static const char szEventMsgSubscribeFMT
[] =
255 "SUBSCRIBE %1$s HTTP/1.1\r\n"
257 "Callback: <http://%2$s/notify>\r\n"
258 "Timeout: Second-%4$d\r\n"
259 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
261 "Content-Length: 0\r\n"
262 "Pragma: no-cache\r\n"
265 //// Unsubscribe message
267 // 2$s: SID (some uuid passed back during subscribe)
268 // 3$s: router's host ("host")
270 static const char szEventMsgUnsubscribeFMT
[] =
271 "UNSUBSCRIBE %1$s HTTP/1.1\r\n"
273 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
275 "Content-Length: 0\r\n"
276 "Pragma: no-cache\r\n"
280 //// Generic SOAP Control:Action request messages
282 // 2$s: router's host/port ("host:port")
283 // 3$s: action (string)
284 // 4$d: content-length
285 static const char szSOAPMsgControlAHeaderFMT
[] =
286 //"M-POST %1$s HTTP/1.1\r\n"
287 "POST %1$s HTTP/1.1\r\n"
288 "Content-Type: text/xml; charset=\"utf-8\"\r\n"
289 //"TEST: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
290 //"Man: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
291 //"01-SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#%3$s\"\r\n"
292 "SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#%3$s\"\r\n"
293 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n"
295 "Content-Length: %4$d\r\n"
296 "Connection: close\r\n"
297 // "Connection: Keep-Alive\r\n"
298 "Pragma: no-cache\r\n"
301 // 1$: action (string)
303 static const char szSOAPMsgControlABodyFMT
[] =
304 "<?xml version=\"1.0\"?>" CRLF
305 "<SOAP-ENV:Envelope" S_CRLF
306 " xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"" S_CRLF
307 " SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" S_CRLF
308 "<SOAP-ENV:Body>" S_CRLF
310 " xmlns:m=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" S_CRLF
313 "</SOAP-ENV:Body>" S_CRLF
314 "</SOAP-ENV:Envelope>" S_CRLF
321 // 2$: argument value
322 static const char szSOAPMsgControlAArgumentFMT
[] =
323 "<%1$s>%2$s</%1$s>" S_CRLF
;
326 // 2$: argument value
328 static const char szSOAPMsgControlAArgumentFMT_t
[] =
330 " xmlns:dt=\"urn:schemas-microsoft-com:datatypes\""
331 " dt:dt=\"%3$s\">%2$s</%1$s>" S_CRLF
;
334 //// Generic SOAP Control:Query request messages
336 // 2$s: router's host/port ("host:port")
337 // 3$d: content-length
338 static const char szSOAPMsgControlQHeaderFMT
[] =
339 "M-POST %1$s HTTP/1.1\r\n"
340 //"POST %1$s HTTP/1.1\r\n"
342 "Content-Length: %3$d\r\n"
343 "Content-Type: text/xml; charset-\"utf-8\"\r\n"
344 //"Man: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
345 //"SOAPAction: \"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"\r\n"
346 "01-SOAPAction: \"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"\r\n"
350 static const char szSOAPMsgControlQBodyFMT
[] =
352 " xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"" S_CRLF
353 " s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" S_CRLF
355 "<u:QueryStateVariable xmlns:u=\"urn:schemas-upnp-org:control-1-0\"" S_CRLF
356 "<u:varName>%s</u:varName>" S_CRLF
357 "</u:QueryStateVariable>" S_CRLF
359 "</s:Envelope>" S_CRLF
362 // 1$: device description URL
364 static const char szSSDPMsgDescribeDeviceFMT
[] =
365 "GET %s HTTP/1.1\r\n"
366 "Accept: text/xml, application/xml\r\n"
367 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
369 "Connection: close\r\n"
370 // "Connection: Keep-Alive\r\n"
373 ////////////////////////////////////////////////////////////////////////
375 ////////////////////////////////////////////////////////////////////////
377 static int g_fFirstInit
= TRUE
;
378 static int g_fQuit
= FALSE
;
380 static int g_fLogging
;
382 // Globally-accessible UDP socket
383 static int g_sUDP
= -1;
384 static int g_sUDPCancel
= -1;
386 // Globally-accessible TCP socket
387 static int g_sTCP
= -1;
388 static int g_sTCPCancel
= -1;
391 static int g_fEventEnabled
= FALSE
;
392 static unsigned short g_wEventPort
;
393 static struct sockaddr_in g_saddrRouterEvent
;
394 static char g_szRouterHostPortEvent
[1024];
395 static char g_szEventURL
[1024];
398 static char g_szFriendlyName
[1024];
399 static char g_szManufacturer
[1024];
400 static char g_szModelName
[1024];
401 static char g_szModelDescription
[1024];
404 static struct sockaddr_in g_saddrRouterBase
;
405 static char g_szRouterHostPortBase
[1024];
408 static pthread_t g_UDPthread
= NULL
;
409 static pthread_t g_TCPthread
= NULL
;
412 static unsigned long g_dwLocalIP
= 0;
414 // Globally accessible info about the router/UPnP
415 static int g_fUPnPEnabled
= FALSE
;
416 static char g_szUSN
[1024];
418 static struct sockaddr_in g_saddrRouterDesc
;
419 static char g_szRouterHostPortDesc
[1024];
420 static char g_szNATDevDescURL
[1024];
422 static struct sockaddr_in g_saddrRouterSOAP
;
423 static char g_szRouterHostPortSOAP
[1024];
424 static char g_szControlURL
[1024];
425 static int g_fControlURLSet
= FALSE
;
427 // Lock/condvar for synchronous upnp calls
428 static pthread_mutex_t g_xUPnP
;
429 static pthread_mutex_t g_xUPnPMsg
;
430 static pthread_cond_t g_condUPnP
;
431 static pthread_cond_t g_condUPnPControlURL
;
432 static struct timeval g_tvUPnPInitTime
;
433 static struct timeval g_tvLastUpdateTime
;
435 // timeout values in seconds
436 static int g_iFunctionTimeout
= NA_DEFAULT_FUNCTION_TIMEOUT
;
438 static void GetDeviceDescription(void);
439 static void SetLocalIP(void);
441 ////////////////////////////////////////////////////////////////////////
443 ////////////////////////////////////////////////////////////////////////
448 #define IFNAMELEN 16 /* Interface Name Length */
449 #define IPLEN 16 /* 16 bytes(128 bits) for IPv6 */
451 typedef struct tagIPINFO
454 char szIfName
[IFNAMELEN
]; /* Interface name */
455 unsigned char abIP
[IPLEN
];
456 unsigned short wPort
;
457 } IPINFO
, *PIPINFO
, **PPIPINFO
;
459 typedef struct hostent HOSTENT
, *PHOSTENT
;
461 static unsigned long GetNATIPNetmask(unsigned long dwIP
)
463 static const union { uint8_t b
[4]; uint32_t l
; } mask_10
= { { 255, 0, 0, 0 } }; // Mask for 10/8
464 static const union { uint8_t b
[4]; uint32_t l
; } mask172
= { { 255, 240, 0, 0 } }; // Mask for 172.16/12
465 static const union { uint8_t b
[4]; uint32_t l
; } mask192
= { { 255, 255, 0, 0 } }; // Mask for 192.168/16
466 uint8_t *p
= (uint8_t *)&dwIP
;
467 if (p
[0] == 10 ) return mask_10
.l
;
468 if (p
[0] == 172 && (p
[1] & 0xF0) == 16) return mask172
.l
;
469 if (p
[0] == 192 && p
[1] == 168 ) return mask192
.l
;
470 return 0; /* No NAT IP */
473 static int GetIPInfo(PPIPINFO ppIPInfo
)
476 int iLastLen
, iLen
, iNum
= 0, iMax
= 0;
478 char *pcBuf
, *pcTemp
;
479 PIPINFO pIPInfo
= NULL
;
481 struct ifreq
*ifr
, ifrcopy
;
483 if (ppIPInfo
== NULL
) return 0;
485 fd
= socket(AF_INET
, SOCK_DGRAM
, 0);
488 iLen
= 100 * sizeof(struct ifreq
);
492 pcBuf
= (char *)malloc(iLen
);
496 if (ioctl(fd
, SIOCGIFCONF
, &ifc
) < 0)
498 if (errno
!= EINVAL
|| iLastLen
!= -1)
500 // DbgPrint(ELL_ERROR, "ioctl failed(%d)\n", errno);
508 if (ifc
.ifc_len
== iLastLen
) break;
509 iLastLen
= ifc
.ifc_len
;
512 iLen
+= 10 * sizeof(struct ifreq
);
516 for (pcTemp
= pcBuf
; pcTemp
< pcBuf
+ ifc
.ifc_len
; )
523 pIPInfoNew
= (PIPINFO
)realloc(pIPInfo
, sizeof(IPINFO
) * iMax
);
524 if (pIPInfoNew
== NULL
)
531 else pIPInfo
= pIPInfoNew
;
533 memset(pIPInfo
+ (iMax
- 10), 0, sizeof(IPINFO
) * 10);
536 ifr
= (struct ifreq
*)pcTemp
;
538 pcTemp
+= sizeof(ifr
->ifr_name
) + ifr
->ifr_addr
.sa_len
;
540 /* discard invalid address families & loopback */
541 if ((ifr
->ifr_addr
.sa_family
!= AF_INET
&&
542 ifr
->ifr_addr
.sa_family
!= AF_INET6
) ||
543 strncmp(ifr
->ifr_name
, "lo", 2) == 0) continue;
546 ioctl(fd
, SIOCGIFFLAGS
, &ifrcopy
);
547 if ((ifrcopy
.ifr_flags
& IFF_UP
) == 0) continue;
549 switch (ifr
->ifr_addr
.sa_family
)
552 memcpy(pIPInfo
[iNum
].szIfName
, ifr
->ifr_name
, IFNAMELEN
);
553 dwIP
= ((struct sockaddr_in
*)&ifr
->ifr_addr
)->sin_addr
.s_addr
;
554 memcpy(pIPInfo
[iNum
].abIP
, &dwIP
, sizeof(unsigned long));
555 if (ifrcopy
.ifr_flags
& IFF_POINTOPOINT
)
556 pIPInfo
[iNum
].iFlags
|= ISPPP
;
561 memcpy(pIPInfo
[iNum
].szIfName
, ifr
->ifr_name
, IFNAMELEN
);
562 memcpy(pIPInfo
[iNum
].abIP
,
563 ((struct sockaddr_in6
*)&(ifr
->ifr_addr
))-> sin6_addr
.s6_addr
,
565 pIPInfo
[iNum
].iFlags
|= ISIPV6
;
566 if (ifrcopy
.ifr_flags
& IFF_POINTOPOINT
)
567 pIPInfo
[iNum
].iFlags
|= ISPPP
;
584 static void FreeIPInfo(PIPINFO pIPInfo
)
586 if (pIPInfo
!= NULL
) free(pIPInfo
);
590 ////////////////////////////////////////////////////////////////////////
591 // Function Definitions
592 ////////////////////////////////////////////////////////////////////////
594 static void SendDiscoveryMsg();
597 // Creates a UDP multicast socket and listens to the SSDP IP/PORT
599 // -1 on error, or the socket descriptor if success
600 static int SSDPListen()
605 struct sockaddr_in saddr
;
608 // IPPROTO_IP == 0; IPPROTO_TCP == 6; IPPROTO_UDP == 17; etc.
609 sd
= socket(AF_INET
, SOCK_DGRAM
, 0);
611 if (g_fLogging
& NALOG_ERROR
)
612 fprintf(g_log
, "Can't create socket! SSDPListen exiting\n");
616 // sock options values
617 fLoop
= 0; // false - don't send copy to self
620 // bind to listen to ssdp multicast address
621 bzero(&saddr
, sizeof(saddr
));
622 saddr
.sin_len
= sizeof(saddr
);
623 saddr
.sin_family
= AF_INET
;
624 //saddr.sin_addr.s_addr = inet_addr(SSDP_IP);
625 //saddr.sin_port = htons(SSDP_PORT);
626 saddr
.sin_addr
.s_addr
= g_dwLocalIP
;
629 // and set the multicast add_member structure
630 // (TODO: need to find interfaces later - ioctl, with:
631 // SIOCFIFCONF to find if's, SIOCGIFADDR to get addr, and SIOCFIFFLAGS
632 // to check for IFF_MULTICAST flag for multicast support on an if)
633 bzero(&mreq
, sizeof(mreq
));
634 mreq
.imr_interface
.s_addr
= g_dwLocalIP
;
635 mreq
.imr_multiaddr
.s_addr
= inet_addr(SSDP_IP
);
638 bind(sd
, (struct sockaddr
*)&saddr
, sizeof(saddr
)) //||
639 //setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &fLoop, sizeof(fLoop)) ||
640 //setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &iTTL, sizeof(iTTL)) ||
641 //setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))
643 if (g_fLogging
& NALOG_ERROR
)
645 "bind/setsockopt for multicast failed... errno = %d\n", errno
);
653 static int EventListen()
655 struct sockaddr_in saddr
;
658 // try 5 ports before failing completely
659 for (g_wEventPort
= 5000; g_wEventPort
< 5005; g_wEventPort
++)
661 sd
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
663 if (g_fLogging
& NALOG_ERROR
)
664 fprintf(g_log
, "Can't create socket! EventListen exiting\n");
668 bzero(&saddr
, sizeof(saddr
));
669 saddr
.sin_len
= sizeof(saddr
);
670 saddr
.sin_family
= AF_INET
;
671 saddr
.sin_addr
.s_addr
= g_dwLocalIP
;
672 saddr
.sin_port
= htons(g_wEventPort
);
675 if (bind(sd
, (struct sockaddr
*)&saddr
, sizeof(saddr
)) == 0)
678 ////TracePrint(ELL_TRACE, "UPnP: EventListen @%u\n", g_wEventPort);
682 // unsuccessful - close sd and try again
683 if (g_fLogging
& NALOG_ERROR
)
685 "bind TCP port %u failed: errno = %d\n", g_wEventPort
, errno
);
692 static void *TCPProc(void *in
);
694 static int EventInit()
699 if (g_fEventEnabled
== FALSE
)
701 // initialize TCP socket for Eventing
702 g_sTCP
= EventListen();
704 if (g_fLogging
& NALOG_ERROR
)
705 fprintf(g_log
, "EventInit - Failed to init tcp socket.\n");
706 return NA_E_INTERNAL_ERROR
;
710 pthread_attr_init(&attr
);
711 iRet
= pthread_create(&g_TCPthread
, &attr
, TCPProc
, 0);
715 if (g_fLogging
& NALOG_ERROR
)
716 fprintf(g_log
, "EventInit: TCPProc create failed(%d)\n", iRet
);
717 return NA_E_THREAD_ERROR
;
721 g_fEventEnabled
= TRUE
;
726 static void DumpHex(char *buf
, int len
)
733 if (g_fLogging
& NALOG_DUMP
) {
734 if (buf
== NULL
) return;
735 if (len
<= 0) return;
737 for (i
= 0; i
< len
; i
= nexti
) {
738 fprintf(g_log
, "%04x: ", i
);
740 endj
= (nexti
> len
) ? len
: nexti
;
741 for (j
= i
; j
< endj
; j
++)
742 fprintf(g_log
, "%02x %c ", buf
[j
] & 0xff, buf
[j
]);
745 char pad
[3 * 16 + 1]; // don't need the last 3 bytes anyway
746 j
= (16 - (j
% 16)) * 3;
752 for (j
= i
; j
< endj
; j
++)
753 isprint(buf
[j
]) ? fputc(buf
[j
], g_log
) : fputc('.', g_log
);
760 // FindHTTPHeaderNewLine
761 // Returns a pointer to the beginning of a CRLF, that is not a
762 // part of LWS. (LWS is CRLF followed by a space or tab, and in
763 // HTTP, considered as equivalent to a single space) (LWS stands
764 // for "linear white space")
765 // Returns a pointer the beginning of CRLF, and sets the EOH flag to
766 // whether this is the last header in the HTTP header section.
767 // Also, if pbuf is NULL, or if there isn't any CRLF found in the
768 // string, or if the HTTP syntax is wrong, NULL is returned, and
769 // the EOH flag is not touched.
770 static char *FindHTTPHeaderNewLine(char *pbuf
, int iBufSize
, int *pfEOH
)
775 if (pbuf
== NULL
) return NULL
;
778 result
= memchr(pbuf
, '\r', iBufSize
);
779 if (result
== NULL
) {
780 if (g_fLogging
& NALOG_INFO0
) {
781 fprintf(g_log
, "FindHTTPHeaderNewLine: er @(%d/%d)\n", i
, iBufSize
);
788 // decrement iBufSize, and move pbuf forward
789 iBufSize
-= (result
- pbuf
);
792 ++pbuf
; // now pointing right after "\r"
794 if (*pbuf
== '\0') break;
795 if (*pbuf
!= '\n') continue;
797 ++pbuf
; // now pointing after "\r\n"
799 if (*pbuf
== '\0') break;
800 if ((*pbuf
== ' ') || (*pbuf
== '\t')) continue;
802 // at this point we know we're at the end of a header field,
803 // and there's more stuff coming...
805 // just need to check if this is the last header
806 if ((pbuf
[0] == '\r') && (pbuf
[1] == '\n'))
817 // NewHTTPResponse_sz
818 // Creates an HTTPResponse structure from a string (sz). Set
819 // fDestroyOriginal to TRUE if the buffer passed in can be overwritten.
820 // Otherwise, NewHTTPResponse_sz will duplicate the buffer.
821 // Returns the created HTTPResponse structure if successful, or if an
822 // error occured (out of memory, or bad http syntax), returns NULL.
823 // NOTE: ALWAYS call DeleteHTTPResponse after using the HTTPResponse structure.
824 // NOTE: The input is assumed to be correct. If there're HTTP syntax errors,
825 // and the pszHTTPResponse is not null-terminated, result may be undefined.
826 // (to be fixed next version)
827 static PHTTPResponse
NewHTTPResponse_sz(
828 char *pszHTTPResponse
,
830 int fDestroyOriginal
)
832 PHTTPResponse pResponse
;
838 if ((pResponse
= (PHTTPResponse
)malloc(sizeof(HTTPResponse
))) == NULL
) {
839 if (g_fLogging
& NALOG_INFO0
) {
840 fprintf(g_log
, "NewHTTPResponse_sz: er 1\n");
846 // make copy of buffer now
847 if (fDestroyOriginal
) {
848 pResponse
->buf
= NULL
;
849 pBuf
= pszHTTPResponse
;
852 int len
= strlen(pszHTTPResponse
);
853 if ((len
+1) > iBufferSize
) {
854 if (g_fLogging
& NALOG_INFO0
)
855 fprintf(g_log
, "Length: %d > %d\n", len
+1, iBufferSize
);
858 if ((pResponse
->buf
= (char *)malloc(iBufferSize
)) == NULL
) {
860 if (g_fLogging
& NALOG_INFO0
) {
861 fprintf(g_log
, "NewHTTPResponse_sz: er 2\n");
866 memcpy(pResponse
->buf
, pszHTTPResponse
, iBufferSize
);
867 pBuf
= pResponse
->buf
;
870 // get the first line
871 pszEOL
= FindHTTPHeaderNewLine(pBuf
, iBufferSize
, &fEOH
);
872 if (pszEOL
== NULL
) {
873 if (g_fLogging
& NALOG_INFO0
) {
874 fprintf(g_log
, "NewHTTPResponse_sz: er 3\n");
880 *pszEOL
= '\0'; // terminate the status line
881 pszEOL
+= 2; // point to the rest of the buffer
883 // set the status string first
884 pResponse
->pszStatus
= strchr(pBuf
, ' ');
885 if (pResponse
->pszStatus
== NULL
) {
886 if (g_fLogging
& NALOG_INFO0
) {
887 fprintf(g_log
, "NewHTTPResponse_sz: er 4\n");
890 goto cleanup
; // syntax error
893 pResponse
->pszStatus
++; // point to the actual status
895 pResponse
->pszReason
= strchr(pResponse
->pszStatus
, ' ');
896 if (pResponse
->pszReason
== NULL
) {
897 if (g_fLogging
& NALOG_INFO0
) {
898 fprintf(g_log
, "NewHTTPResponse_sz: er 5\n");
901 goto cleanup
; // syntax error
904 pResponse
->pszReason
[0] = '\0'; // terminate status string
905 pResponse
->pszReason
++; // point to the reason string
907 iNumHeaders
= 0; // initialize to 0 headers
909 // parse header fields line by line (while not end of headers)
911 PProperty pHeader
= &(pResponse
->aHeaders
[iNumHeaders
]);
912 // point header field name to the first char of the line
913 pHeader
->pszName
= pszEOL
;
915 // search for the end of line
916 pszEOL
= FindHTTPHeaderNewLine(pszEOL
,
917 iBufferSize
- (pszEOL
- pBuf
), // remainder size
919 if (pszEOL
== NULL
) {
920 if (g_fLogging
& NALOG_INFO0
) {
921 fprintf(g_log
, "NewHTTPResponse_sz: er reading header field %d @ %lu / %lu\n",
922 iNumHeaders
, pHeader
->pszName
- pBuf
, iBufferSize
);
923 DumpHex(pszHTTPResponse
, iBufferSize
);
926 goto cleanup
; // syntax error
929 *pszEOL
= '\0'; // terminate this string
930 pszEOL
+= 2; // point to beginning of next line
932 pHeader
->pszValue
= strchr(pHeader
->pszName
, ':');
933 if (pHeader
->pszValue
== NULL
) {
934 if (g_fLogging
& NALOG_INFO0
) {
935 fprintf(g_log
, "NewHTTPResponse_sz: er 6\n");
938 goto cleanup
; // syntax error (header field has no ":")
941 pHeader
->pszValue
[0] = '\0'; // terminate the header name string
942 pHeader
->pszValue
++; // point after the ":"
943 // get rid of leading spaces for the value part
945 (pHeader
->pszValue
[0] == ' ') ||
946 (pHeader
->pszValue
[0] == '\t') ||
947 (pHeader
->pszValue
[0] == '\r') ||
948 (pHeader
->pszValue
[0] == '\n')
950 pHeader
->pszValue
++; // skip the space
953 iNumHeaders
++; // added one more header
954 pHeader
++; // point to the next header in pResponse->aHeaders
957 pResponse
->iNumHeaders
= iNumHeaders
; // remember to set it in pResponse
959 pResponse
->pszBody
= pszEOL
+ 2; // point after the empty line
964 if (pResponse
->buf
!= NULL
) free(pResponse
->buf
);
969 // DeleteHTTPResponse
970 // Deallocates stuff in the HTTPResponse structure, effectively returning
971 // memory to the system and destroying the structure.
972 // NOTE: The pointer pResponse WILL BE FREED, and will be unusable after
973 // the call to DeleteHTTPResponse.
974 static void DeleteHTTPResponse(PHTTPResponse pResponse
)
978 if (pResponse
== NULL
) return;
980 // Current impl is just simple array - no need to free()
981 //for (i = 0; i < pResponse->iNumHeaders; i++) {
982 // free(pResponse->aHeaders[i]);
985 if (pResponse
->buf
!= NULL
)
986 free(pResponse
->buf
);
990 //typedef struct tagHTTPResponse {
994 // Property aHeaders[30]; // assume at most this many headers
1000 //} HTTPResponse, *PHTTPResponse, **PPHTTPResponse;
1002 static void PrintHTTPResponse(PHTTPResponse pResponse
)
1006 if (g_fLogging
& (NALOG_INFO1
)) {
1007 if (pResponse
== NULL
) return;
1008 fprintf(g_log
, " *** HTTP response begin *** \n");
1009 fprintf(g_log
, " * status = [%s], reason = [%s] *\n",
1010 pResponse
->pszStatus
, pResponse
->pszReason
);
1011 for (i
= 0; i
< pResponse
->iNumHeaders
; i
++) {
1012 fprintf(g_log
, " * Header \"%s\" = [%s]\n",
1013 pResponse
->aHeaders
[i
].pszName
,
1014 pResponse
->aHeaders
[i
].pszValue
);
1016 if (g_fLogging
& NALOG_DUMP
)
1017 fprintf(g_log
, " * body = [%s] *\n", pResponse
->pszBody
);
1018 fprintf(g_log
, " *** HTTP response end *** \n");
1022 static int DiscoverRouter(PHTTPResponse pResponse
)
1025 int fLocation
= FALSE
;
1027 int fIsNATDevice
= FALSE
;
1030 if (strcmp(pResponse
->pszStatus
, "200") != 0)
1034 if (pResponse
== NULL
) {
1035 if (g_fLogging
& NALOG_INFO0
)
1036 fprintf(g_log
, "DiscoverRouter: pResponse == NULL\n");
1040 // check to see if this is a relevant packet
1041 for (i
= 0; i
< pResponse
->iNumHeaders
; i
++) {
1042 PProperty pHeader
= &(pResponse
->aHeaders
[i
]);
1044 if ((strcasecmp(pHeader
->pszName
, "ST") == 0) ||
1045 (strcasecmp(pHeader
->pszName
, "NT") == 0)) {
1046 if ((strcmp(pHeader
->pszValue
,
1047 "urn:schemas-upnp-org:service:WANIPConnection:1") == 0) ||
1048 (strcmp(pHeader
->pszValue
,
1049 "urn:schemas-upnp-org:device:InternetGatewayDevice:1") == 0)) {
1050 fIsNATDevice
= TRUE
;
1055 // leave the message alone if we don't need it
1059 // Now that we know we're looking at the message about the NAT device:
1060 pthread_mutex_lock(&g_xUPnP
);
1062 // set upnp to be unconfigured for now
1063 g_fUPnPEnabled
= FALSE
;
1065 // loop through the headers
1066 for (i
= 0; i
< pResponse
->iNumHeaders
; i
++) {
1067 PProperty pHeader
= &(pResponse
->aHeaders
[i
]);
1069 if (strcasecmp(pHeader
->pszName
, "Location") == 0) {
1073 if (g_fLogging
& NALOG_INFO1
)
1074 fprintf(g_log
, "Checking Location...\n");
1075 p
= pHeader
->pszValue
;
1076 if (strncmp(p
, "http://", 7) != 0)
1077 continue; // hope for another Location header to correct it
1078 p
+= 7; // skip over "http://"
1081 // set the control URL first
1083 g_szNATDevDescURL
[0] = '/';
1084 g_szNATDevDescURL
[1] = '\0';
1087 strncpy(g_szNATDevDescURL
, q
, sizeof(g_szNATDevDescURL
) - 1);
1088 g_szNATDevDescURL
[sizeof(g_szNATDevDescURL
) - 1] = '\0';
1089 // terminate the host/port string
1093 if (g_fLogging
& NALOG_INFO1
)
1094 fprintf(g_log
, " Device Description URL set to[%s]...\n",
1097 // see if port is specified
1100 sprintf(g_szRouterHostPortDesc
, "%s", p
);
1102 g_saddrRouterDesc
.sin_addr
.s_addr
= inet_addr(p
);
1103 g_saddrRouterDesc
.sin_port
= htons(80);
1106 // don't include the ":80" - HTTP is by default port 80
1107 if (atoi(q
+1) == 80) *q
= '\0';
1109 strcpy(g_szRouterHostPortDesc
, p
);
1111 // terminate the host part and point to it
1115 g_saddrRouterDesc
.sin_addr
.s_addr
= inet_addr(p
);
1116 g_saddrRouterDesc
.sin_port
= htons(atoi(q
));
1119 g_saddrRouterDesc
.sin_family
= AF_INET
;
1121 if (g_fLogging
& NALOG_INFO1
)
1122 fprintf(g_log
, " Router Address set to[%s]...\n",
1123 g_szRouterHostPortDesc
);
1126 else if (strcasecmp(pHeader
->pszName
, "USN") == 0) {
1127 if (g_fLogging
& NALOG_INFO1
)
1128 fprintf(g_log
, "Checking USN...\n");
1129 strncpy(g_szUSN
, pHeader
->pszValue
, sizeof(g_szUSN
) - 1);
1130 g_szUSN
[sizeof(g_szUSN
) - 1] = '\0';
1134 ; // do nothing for other headers for now
1138 // now check flags and set enabled if all set
1139 if (fLocation
&& fUSN
) {
1140 if (g_fLogging
& NALOG_INFO1
) {
1142 "Description Host/port string: [%s]\n"
1143 "NATDevDescURL: [%s], USN: [%s]\n",
1144 g_szRouterHostPortDesc
,
1145 g_szNATDevDescURL
, g_szUSN
);
1146 if (g_fLogging
& NALOG_INFO1
)
1147 fprintf(g_log
, "Got router information\n");
1150 g_fUPnPEnabled
= TRUE
;
1151 pthread_cond_broadcast(&g_condUPnP
);
1154 // remember to unlock before return
1155 pthread_mutex_unlock(&g_xUPnP
);
1160 // granularity is specified as: granularity = 1/nth seconds
1161 #define UPNP_TIMEOUT_GRANULARITY (1000)
1162 #define U_TOGRAN UPNP_TIMEOUT_GRANULARITY
1165 static void TimevalSubtract(
1166 struct timeval
*result
,
1167 const struct timeval
*a
,
1168 const struct timeval
*b
)
1170 result
->tv_sec
= a
->tv_sec
- b
->tv_sec
;
1172 if (b
->tv_usec
> a
->tv_usec
) {
1174 result
->tv_usec
= 1000000 + a
->tv_usec
- b
->tv_usec
;
1177 result
->tv_usec
= a
->tv_usec
- b
->tv_usec
;
1180 // elapsed = end - start
1181 static void GetTimeElapsed(
1182 const struct timeval
*tv_start
,
1183 const struct timeval
*tv_end
,
1184 struct timeval
*tv_elapsed
)
1186 TimevalSubtract(tv_elapsed
, tv_end
, tv_start
);
1188 tv_elapsed
->tv_sec
= tv_end
->tv_sec
- tv_start
->tv_sec
;
1190 if (tv_start
->tv_usec
> tv_end
->tv_usec
) {
1191 tv_elapsed
->tv_sec
--;
1192 tv_elapsed
->tv_usec
= 1000000 + tv_end
->tv_usec
- tv_start
->tv_usec
;
1195 tv_elapsed
->tv_usec
= tv_end
->tv_usec
- tv_start
->tv_usec
;
1199 // returns +1, 0, or -1, if a>b, a==b, a<b, respectively
1200 static int CompareTime(
1201 const struct timeval
*a
,
1202 const struct timeval
*b
1205 if ((a
->tv_sec
== b
->tv_sec
) &&
1206 (a
->tv_usec
== b
->tv_usec
)) return 0;
1208 if (a
->tv_sec
> b
->tv_sec
) return 1;
1209 else if (a
->tv_sec
< b
->tv_sec
) return -1;
1211 // if seconds are equal...
1212 if (a
->tv_usec
> b
->tv_usec
) return 1;
1216 static int WaitControlURLSet(double timeout
)
1220 struct timeval tv_start
;
1222 long to_sec
= (int) (timeout
/ U_TOGRAN
);
1224 (int) (((timeout
/ U_TOGRAN
) - to_sec
) * 1000000.0);
1225 //long to_sec = (int) timeout;
1226 //long to_usec = (int) ((timeout - to_sec) * 1000000.0);
1227 struct timeval elapsed
;
1229 // get function start time
1230 gettimeofday(&tv_start
, NULL
);
1232 pthread_mutex_lock(&g_xUPnP
);
1235 // if last update is too long ago then wait for it
1236 GetTimeElapsed(&g_tvLastUpdateTime
, &tv_start
, &elapsed
);
1237 if ((elapsed
.tv_sec
+ (elapsed
.tv_usec
/ 1000000.0)) >
1238 (((double) g_iUPnPTimeout
) / U_TOGRAN
))
1239 g_fControlURLSet
= 0;
1242 while (!g_fControlURLSet
) {
1244 gettimeofday(&tv
, NULL
);
1247 for now ignore device timeout
1248 // see if we've past the device's timeout first
1249 GetTimeElapsed(&g_tvUPnPInitTime
, &tv
, &elapsed
);
1250 if ((elapsed
.tv_sec
> g_timeout_sec
) ||
1251 ( (elapsed
.tv_sec
== g_timeout_sec
) &&
1252 (elapsed
.tv_usec
> g_timeout_usec
)
1255 pthread_mutex_unlock(&g_xUPnP
);
1260 // calculate ts to sleep till
1261 ts
.tv_sec
= tv
.tv_sec
+ to_sec
;
1262 ts
.tv_nsec
= (tv
.tv_usec
+ to_usec
) * 1000;
1263 if (ts
.tv_nsec
> 1000000000) {
1264 ts
.tv_nsec
-= 1000000000;
1268 // now get how long we've been in this function already and deduct
1269 GetTimeElapsed(&tv_start
, &tv
, &elapsed
);
1270 ts
.tv_sec
-= elapsed
.tv_sec
;
1271 if (ts
.tv_nsec
< (elapsed
.tv_usec
* 1000)) {
1273 ts
.tv_nsec
= 1000000000 + ts
.tv_nsec
- (elapsed
.tv_usec
* 1000);
1276 ts
.tv_nsec
-= (elapsed
.tv_usec
* 1000);
1279 iRet
= pthread_cond_timedwait(&g_condUPnPControlURL
, &g_xUPnP
, &ts
);
1281 // if timeout then return false
1284 pthread_mutex_unlock(&g_xUPnP
);
1288 pthread_mutex_unlock(&g_xUPnP
);
1293 static int WaitUPnPFunction()
1295 struct timeval start
;
1296 // struct timeval end;
1298 // struct timeval elapsed;
1300 gettimeofday(&start
, NULL
);
1302 wait2
= (double)g_iFunctionTimeout
;
1304 WaitControlURLSet(wait2
);
1306 //gettimeofday(&end, NULL);
1307 //GetTimeElapsed(&start, &end, &elapsed);
1308 //fprintf(stderr, "== wait2: (%f) %d.%06d\n",
1309 // wait2/U_TOGRAN, elapsed.tv_sec, elapsed.tv_usec);
1311 return g_fControlURLSet
;
1314 static void SetLocalIP();
1316 static int SendTCPMsg_saddr_parse(
1317 char *msg
, int iLen
,
1318 char *result
, int resultSize
,
1319 struct sockaddr_in
*saHost
);
1321 static void *TCPProc(void *in
)
1324 unsigned char buf
[MAX_SOAPMSGSIZE
];
1329 //TracePrint(ELL_TRACE, "UPnP: Begin TCPProc\n");
1331 // do the subscription
1334 char response
[2000];
1337 sprintf(callback
, "%u.%u.%u.%u:%u",
1338 ((uint8_t*)&g_dwLocalIP
)[0], ((uint8_t*)&g_dwLocalIP
)[1],
1339 ((uint8_t*)&g_dwLocalIP
)[2], ((uint8_t*)&g_dwLocalIP
)[3], g_wEventPort
);
1341 n
= sprintf((char *)buf
,
1342 szEventMsgSubscribeFMT
,
1344 callback
, g_szRouterHostPortEvent
, 1800);
1346 memset(response
, 0, 2000);
1347 n
= SendTCPMsg_saddr_parse(
1350 &g_saddrRouterEvent
);
1354 resp
= NewHTTPResponse_sz((char *)response
, n
+1, TRUE
);
1357 ////TracePrint(ELL_TRACE, "UPnP Subscribe returns %s/%d\n", resp->pszStatus, n);
1361 ////TracePrint(ELL_TRACE, "UPnP Subscribe not enough response (%d) \n[%s]\n",
1364 DeleteHTTPResponse(resp
);
1368 ////TracePrint(ELL_TRACE, "UPnP Subscribe failed (%d)\n", n);
1373 //TracePrint(ELL_TRACE, "UPnP: TCPProc begin loop\n");
1380 struct sockaddr_in recvaddr
;
1381 socklen_t recvaddrlen
;
1383 struct timeval timeout
;
1388 // for after responding to long(?) TCP event
1392 if (g_sTCPCancel
!= -1) close(g_sTCPCancel
);
1393 sMax
= g_sTCPCancel
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
1394 if (sMax
< g_sTCP
) sMax
= g_sTCP
;
1397 FD_SET(g_sTCP
, &readfds
);
1398 FD_SET(g_sTCPCancel
, &readfds
);
1399 iRet
= select(sMax
+1, &readfds
, NULL
, NULL
, NULL
);
1403 //TracePrint(ELL_TRACE, "UPnP Event select failed (%d)\n", errno);
1407 recvaddrlen
= sizeof(recvaddr
);
1408 sEvent
= accept(g_sTCP
, (struct sockaddr
*)&recvaddr
, &recvaddrlen
);
1409 // not likely - (system's descriptor/file table full)
1410 if (sEvent
<= 0) continue;
1412 ////TracePrint(ELL_TRACE, "UPnP receiving event..\n");
1414 // read all we could from this event
1420 FD_SET(sEvent
, &readfds
);
1422 timeout
.tv_usec
= 400000; // long cause we're dealing with input
1423 iRet
= select(sEvent
+1, &readfds
, NULL
, NULL
, &timeout
);
1434 iRet
= recv(sEvent
, buf
+ iBufLen
, MAX_SOAPMSGSIZE
- iBufLen
, 0);
1437 // something is wrong
1450 iTemp
= send(sEvent
, HTTP200OK
, HTTP200OKLEN
, 0);
1451 shutdown(sEvent
, 1);
1456 // now send 200 OK and be done
1459 ////TracePrint(ELL_TRACE, "UPnP event (%d) received (%d)\n", g_fExpectEvent, iBufLen);
1461 // and parse the XML here.
1462 if (iBufLen
< MAX_SOAPMSGSIZE
)
1464 buf
[iBufLen
] = '\0';
1465 // for now do nothing
1469 buf
[MAX_SOAPMSGSIZE
- 1] = '\0';
1474 //TracePrint(ELL_TRACE, "UPnP: TCPProc end\n");
1477 g_fEventEnabled
= FALSE
;
1478 if (g_sTCPCancel
!= -1) close(g_sTCPCancel
);
1483 static void *UDPProc(void *in
)
1485 // char fLoop = 0; // false - don't send copy to self
1486 // int iTTL = SSDP_TTL;
1488 // struct ip_mreq mreq;
1489 // struct sockaddr_in saddr;
1490 unsigned char buf
[65536];
1491 // FILE *log = g_log;
1492 static time_t last_getdevicedesc_t
= 0;
1495 pthread_mutex_lock(&g_xUPnP
);
1496 gettimeofday(&g_tvUPnPInitTime
, NULL
);
1497 pthread_mutex_unlock(&g_xUPnP
);
1501 struct sockaddr_in recvaddr
;
1502 socklen_t recvaddrlen
;
1504 //struct timeval timeout;
1508 if (g_sUDPCancel
< g_sUDP
) sMax
= g_sUDP
;
1509 else sMax
= g_sUDPCancel
;
1512 FD_SET(g_sUDP
, &readfds
);
1513 FD_SET(g_sUDPCancel
, &readfds
);
1514 iRet
= select(sMax
+1, &readfds
, NULL
, NULL
, NULL
);
1520 close(g_sUDPCancel
);
1528 if (!FD_ISSET(g_sUDP
, &readfds
)) continue;
1529 recvaddrlen
= sizeof(recvaddr
);
1530 n
= recvfrom(g_sUDP
, buf
, sizeof(buf
), 0, // ### !!! Buffer overflow !!! Should be sizeof(buf)-1 to allow for "buf[n] = '\0';" below !!!
1531 (struct sockaddr
*)&recvaddr
, &recvaddrlen
);
1533 if (g_fLogging
& NALOG_ERROR
)
1534 fprintf(g_log
, "recv failed (%d)\n", errno
);
1536 close(g_sUDPCancel
);
1542 if (strncmp((char *)buf
, "HTTP/1.1", 8) == 0) {
1543 PHTTPResponse pResponse
= NewHTTPResponse_sz((char *)buf
, n
+1, TRUE
);
1544 PrintHTTPResponse(pResponse
);
1545 if (DiscoverRouter(pResponse
) == 0)
1547 time_t now
= time(NULL
);
1548 if (!g_fControlURLSet
||
1549 ((now
- last_getdevicedesc_t
) > 5))
1551 GetDeviceDescription();
1553 last_getdevicedesc_t
= now
;
1556 DeleteHTTPResponse(pResponse
);
1558 else if (strncmp((char *)buf
, "NOTIFY * HTTP/1.1", 7) == 0) {
1559 // temporarily use this to fudge - will have the exact same
1560 // parsing, only status/reason set to "*" and "HTTP/1.1".
1561 // TODO: add support for HTTP requests
1562 PHTTPResponse pResponse
= NewHTTPResponse_sz((char *)buf
, n
+1, TRUE
);
1563 if (DiscoverRouter(pResponse
) == 0)
1565 time_t now
= time(NULL
);
1566 if (!g_fControlURLSet
||
1567 ((now
- last_getdevicedesc_t
) > 5))
1569 GetDeviceDescription();
1571 last_getdevicedesc_t
= now
;
1574 DeleteHTTPResponse(pResponse
);
1577 if (g_fLogging
& NALOG_DUMP
)
1578 fprintf(g_log
, "(%ld) Buffer: \n[%s]\n", time(NULL
), buf
);
1587 static void SendUDPMsg(const char *msg
) {
1588 struct sockaddr_in saSendTo
;
1592 bzero(&saSendTo
, sizeof(saSendTo
));
1593 saSendTo
.sin_family
= AF_INET
;
1594 saSendTo
.sin_addr
.s_addr
= inet_addr(SSDP_IP
);
1595 saSendTo
.sin_port
= htons(SSDP_PORT
);
1599 if (g_fLogging
& NALOG_DUMP
)
1600 fprintf(g_log
, "SendUDP: [%s]\n", msg
);
1602 iRet
= sendto(g_sUDP
, msg
, iLen
, 0,
1603 (struct sockaddr
*)&saSendTo
, sizeof(saSendTo
));
1607 if (g_fLogging
& NALOG_ALERT
)
1609 "SendUDPMsg: iRet(%d) != strlen(msg)(%d)! (errno %d)\n",
1613 // strstr, case insensitive, and is limited by len
1614 static char *strcasestr_n(const char *big
, const char *little
, int len
)
1621 if (little
== NULL
) return (char *)big
;
1622 if (big
== NULL
) return NULL
;
1624 //bigLen = strlen(big);
1626 littleLen
= strlen(little
);
1628 if (bigLen
< littleLen
) return NULL
;
1630 end
= bigLen
- littleLen
;
1631 for (i
= 0; i
<= end
; (i
++), (big
++)) {
1632 if (strncasecmp(big
, little
, littleLen
) == 0)
1639 // this is strnstr, only portable
1640 static char *strstr_n(const char *big
, const char *little
, int len
)
1645 (void)len
; // unused
1647 if ((big
== NULL
) || (little
== NULL
)) return NULL
;
1649 iBigLen
= strlen(big
);
1650 iLittleLen
= strlen(little
);
1652 // this part is basically strnstr, except this is portable
1654 if (iBigLen
< iLittleLen
)
1656 if (strncmp(big
, little
, iLittleLen
) == 0)
1663 // returns -1 for "not found"
1664 static int FindContentLength(char *pbuf
, int iLen
)
1666 // non reusable HTTP header parsing code:
1667 // ----------------------------------------------
1671 // find content length header
1672 p
= strcasestr_n(pbuf
, "\r\nContent-Length:", iLen
);
1673 if (p
== NULL
) return -1;
1675 p
+= sizeof("\r\nContent-Length:") - 1; // minus '\0'
1680 // ----------------------------------------------
1683 // returns -1 for "not found"
1684 static int FindBody(char *pbuf
, int iLen
)
1686 // non reusable HTTP header parsing code:
1687 // ----------------------------------------------
1691 // find the empty line
1692 p
= strstr_n(pbuf
, "\r\n\r\n", iLen
);
1693 if (p
== NULL
) return -1;
1695 p
+= sizeof("\r\n\r\n") - 1; // minus '\0'
1698 // ----------------------------------------------
1701 static int SendTCPMsg_saddr_2part(
1702 char *msg
, int iLen
,
1703 char *msg2
, int iLen2
,
1704 char *result
, int resultSize
,
1705 struct sockaddr_in
*saHost
)
1708 struct sockaddr_in saSendTo
;
1717 struct timeval tv_start
;
1718 struct timeval tv_end
;
1719 struct timeval tv_elapsed
;
1721 int iContentLength
= -1;
1722 int iBodyOffset
= -1;
1724 gettimeofday(&tv_start
, NULL
);
1726 if (g_fUPnPEnabled
!= TRUE
) {
1727 //TracePrint(ELL_TRACE, "UPnP not enabled\n");
1728 if (g_fLogging
& NALOG_ERROR
)
1729 fprintf(g_log
, "UPnP not enabled (no UPnP device found yet)\n");
1730 return NA_E_NOT_AVAILABLE
;
1733 s
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
1735 if (g_fLogging
& NALOG_ERROR
)
1736 fprintf(g_log
, "Can't get TCP socket (%d)\n", errno
);
1741 if (setsockopt(s
, IPPROTO_IP
, TCP_NODELAY
, &fND
, sizeof(fND
)) != 0) {
1742 if (g_fLogging
& NALOG_ERROR
)
1743 fprintf(g_log
, "SendTCPMsg/2part: Can't set TCP_NODELAY option!\n");
1744 iRetcode
= NA_E_NET
;
1749 fcntl_flags
= fcntl(s
, F_GETFL
, 0);
1750 fcntl_flags
|= O_NONBLOCK
;
1751 if (fcntl(s
, F_SETFL
, fcntl_flags
) != 0) {
1752 if (g_fLogging
& NALOG_ERROR
)
1753 fprintf(g_log
, "SendTCPMsg/2part: Can't set O_NONBLOCK option!\n");
1754 iRetcode
= NA_E_NET
;
1759 memcpy(&saSendTo
, &g_saddrRouterDesc
, sizeof(saSendTo
));
1761 memcpy(&saSendTo
, saHost
, sizeof(saSendTo
));
1763 iRet
= connect(s
, (struct sockaddr
*) &saSendTo
, sizeof(saSendTo
));
1764 if ((iRet
< 0) && (errno
!= EINPROGRESS
)) {
1765 //TracePrint(ELL_TRACE, "UPnP connect failed\n");
1766 if (g_fLogging
& NALOG_ERROR
)
1767 fprintf(g_log
, "SendTCPMsg/2part: connect failed (%d)\n", errno
);
1768 iRetcode
= NA_E_NET
;
1772 if (g_fLogging
& NALOG_INFO1
)
1774 "- Before Sending TCP Msg1: %d == %lu?\n", iLen
, strlen(msg
));
1775 if (g_fLogging
& NALOG_DUMP
)
1776 fprintf(g_log
, "Sending TCP msg part 1:\n[%s]\n", msg
);
1778 tv
.tv_sec
= g_iFunctionTimeout
/ UPNP_TIMEOUT_GRANULARITY
;
1779 tv
.tv_usec
= (g_iFunctionTimeout
% U_TOGRAN
) * 1000000 / U_TOGRAN
;
1781 FD_SET(s
, &writefds
);
1782 iRet
= select(s
+1, 0, &writefds
, 0, &tv
);
1784 if (g_fLogging
& NALOG_ERROR
)
1785 fprintf(g_log
, "SendTCPMsg/2part: select failed (%d)\n", errno
);
1786 iRetcode
= NA_E_NET
;
1790 if (g_fLogging
& NALOG_ERROR
)
1791 fprintf(g_log
, "SendTCPMsg/2part: select timed out\n");
1792 iRetcode
= NA_E_TIMEOUT
;
1793 gettimeofday(&tv_end
, NULL
);
1794 GetTimeElapsed(&tv_start
, &tv_end
, &tv_elapsed
);
1795 //TracePrint(ELL_TRACE, "UPnP 2part: timeout @1st after %lu.%06lu secs\n",
1796 // tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1800 iRet
= send(s
, msg
, iLen
, 0);
1803 if (g_fLogging
& NALOG_ALERT
)
1804 fprintf(g_log
, "SendTCPMsg/2part: iRet(%d) != strlen(msg)(%d)!\n",
1807 //TracePrint(ELL_TRACE, "UPnP 2part: 1st %d == %d (%d) (%d)?\n", iRet, iLen, strlen(msg), errno);
1809 tv
.tv_sec
= g_iFunctionTimeout
/ UPNP_TIMEOUT_GRANULARITY
;
1810 tv
.tv_usec
= (g_iFunctionTimeout
% U_TOGRAN
) * 1000000 / U_TOGRAN
;
1812 FD_SET(s
, &writefds
);
1813 // calculate how much time elapsed
1814 gettimeofday(&tv_end
, NULL
);
1815 GetTimeElapsed(&tv_start
, &tv_end
, &tv_elapsed
);
1816 if (CompareTime(&tv_elapsed
, &tv
) > 0) {
1818 return NA_E_TIMEOUT
;
1823 // subtract that from timeout accordingly
1824 tv
.tv_sec
-= tv_elapsed
.tv_sec
;
1825 if (tv
.tv_usec
< tv_elapsed
.tv_usec
) {
1827 tv
.tv_usec
= 1000000 + tv
.tv_usec
- tv_elapsed
.tv_usec
;
1830 tv
.tv_usec
= tv
.tv_usec
- tv_elapsed
.tv_usec
;
1832 iRet
= select(s
+1, 0, &writefds
, 0, &tv
);
1834 if (g_fLogging
& NALOG_ERROR
)
1835 fprintf(g_log
, "SendTCPMsg/2part: select2 failed (%d)\n", errno
);
1836 iRetcode
= NA_E_NET
;
1840 if (g_fLogging
& NALOG_ERROR
)
1841 fprintf(g_log
, "SendTCPMsg/2part: select2 timed out\n");
1842 iRetcode
= NA_E_TIMEOUT
;
1843 gettimeofday(&tv_end
, NULL
);
1844 GetTimeElapsed(&tv_start
, &tv_end
, &tv_elapsed
);
1845 //TracePrint(ELL_TRACE, "UPnP 2part: timeout @2nd after %lu.%06lu secs\n",
1846 // tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1850 iRet
= send(s
, msg2
, iLen2
, 0);
1851 if (g_fLogging
& NALOG_INFO1
)
1853 "SendTCPMsg/parse: Before Sending TCP Msg2: %d == %lu?\n",
1854 iLen2
, strlen(msg2
));
1855 if (g_fLogging
& NALOG_DUMP
)
1856 fprintf(g_log
, "Sending TCP msg part 2:\n[%s]\n", msg2
);
1858 //TracePrint(ELL_TRACE, "UPnP 2part: 2nd %d == %d (%d) (%d)?\n", iRet, iLen2, strlen(msg2), errno);
1862 if (g_fLogging
& NALOG_ALERT
)
1863 fprintf(g_log
, "SendTCPMsg/2part: iRet(%d) != strlen(msg2)(%d)!\n",
1866 if (result
== NULL
) { // if caller just want to send/display msgs
1867 if (g_fLogging
& NALOG_DUMP
)
1868 fprintf(g_log
, "TCP Buffer: [");
1871 if (g_fLogging
& NALOG_INFO1
)
1872 fprintf(g_log
, "start recv @%lu\n", time(NULL
));
1875 iContentLength
= -1;
1879 struct timeval timeout
;
1883 FD_SET(s
, &readfds
);
1885 // In testing, the Linksys Wireless-G Broadband Router "WRT54GS" takes
1886 // up to four seconds to respond, and even then only a partial response,
1887 // with the remainder coming in a second TCP segment half a second later.
1888 // Accordingly, we wait up to five seconds for the initial data, and then after that
1889 // wait one second after subsequent TCP segments, in care more data is still coming.
1890 timeout
.tv_sec
= iBufLen
? 1 : 5;
1891 timeout
.tv_usec
= 0;
1892 iRet
= select(s
+1, &readfds
, NULL
, NULL
, &timeout
);
1895 //TracePrint(ELL_TRACE, "UPnP 2part: select timeout? (%d, %d)\n",
1900 //gettimeofday(&tv_end, NULL);
1901 //GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed);
1902 //fprintf(stderr, "2 == loop: %d.%06d\n", tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1904 // if only sending messages
1905 if (result
== NULL
) {
1907 i
= recv(s
, t
, 1000-1, 0); // leave room for '\0' for dump
1909 if (g_fLogging
& NALOG_DUMP
) {
1911 fprintf(g_log
, "%s", t
);
1916 // EO result buf: discard extra bytes
1917 if (resultSize
<= iBufLen
) {
1919 i
= recv(s
, &t
, 1000, 0);
1921 // Note that there's no dump here - prevents DoS attack from
1922 // flooding the logs/diskspace
1926 i
= recv(s
, result
+ iBufLen
, resultSize
- iBufLen
, 0);
1928 //TracePrint(ELL_TRACE, "UPnP 2part: recv done %d (%d, %d)\n",
1929 // iBufLen, i, errno);
1935 // parse and see if we can find content-length to quit early
1936 iContentLength
= FindContentLength(result
, iBufLen
);
1938 // now if we're still in header, see if we can find body
1939 iBodyOffset
= FindBody(result
, iBufLen
);
1941 // now check if we can leave early. conditions are:
1942 // past headers, and we've already recv'ed content-length of body
1943 if ((iBodyOffset
>= 0) &&
1944 (iContentLength
>= 0) &&
1945 ((iBufLen
- iBodyOffset
) >= iContentLength
))
1947 //TracePrint(ELL_TRACE, "UPnP 2part: read all specified %d (%d, %d) (%d, %d)\n",
1948 // iBufLen, i, errno, iBodyOffset, iContentLength);
1953 //fprintf(stderr, "2 -- \n");
1955 if (g_fLogging
& NALOG_INFO1
)
1956 fprintf(g_log
, "SendTCPMsg_saddr_2part done recv %d @ %lu\n", iBufLen
, time(NULL
));
1958 if (result
== NULL
) { // if caller just want to send/display msgs
1959 if (g_fLogging
& NALOG_DUMP
)
1960 fprintf(g_log
, "]\n");
1971 static int SendTCPMsg_saddr_parse(
1972 char *msg
, int iLen
,
1973 char *result
, int resultSize
,
1974 struct sockaddr_in
*saHost
)
1977 struct sockaddr_in saSendTo
;
1984 struct timeval tv_start
;
1985 // struct timeval tv_end;
1986 // struct timeval tv_elapsed;
1988 // HTTP parsing vars
1996 select(0, NULL
, NULL
, NULL
, &tv
);
1998 pthread_mutex_lock(&g_xUPnPMsg
);
2000 gettimeofday(&tv_start
, NULL
);
2002 if (g_fUPnPEnabled
!= TRUE
) {
2003 //TracePrint(ELL_TRACE, "UPnP not enabled\n");
2004 if (g_fLogging
& NALOG_ERROR
)
2005 fprintf(g_log
, "UPnP not enabled (no UPnP device found yet)\n");
2006 pthread_mutex_unlock(&g_xUPnPMsg
);
2007 return NA_E_NOT_AVAILABLE
;
2010 s
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
2012 if (g_fLogging
& NALOG_ERROR
)
2013 fprintf(g_log
, "Can't get TCP socket (%d)\n", errno
);
2014 pthread_mutex_unlock(&g_xUPnPMsg
);
2019 fcntl_flags
= fcntl(s
, F_GETFL
, 0);
2020 fcntl_flags
|= O_NONBLOCK
;
2021 if (fcntl(s
, F_SETFL
, fcntl_flags
) != 0) {
2022 if (g_fLogging
& NALOG_ERROR
)
2023 fprintf(g_log
, "SendTCPMsg/parse: Can't set O_NONBLOCK option!\n");
2025 pthread_mutex_unlock(&g_xUPnPMsg
);
2030 memcpy(&saSendTo
, &g_saddrRouterDesc
, sizeof(saSendTo
));
2032 memcpy(&saSendTo
, saHost
, sizeof(saSendTo
));
2034 iRet
= connect(s
, (struct sockaddr
*) &saSendTo
, sizeof(saSendTo
));
2035 if ((iRet
< 0) && (errno
!= EINPROGRESS
)) {
2036 //TracePrint(ELL_TRACE, "UPnP connect failed\n");
2037 if (g_fLogging
& NALOG_ERROR
)
2038 fprintf(g_log
, "SendTCPMsg/parse: connect failed (%d)\n", errno
);
2040 pthread_mutex_unlock(&g_xUPnPMsg
);
2044 if (g_fLogging
& NALOG_INFO1
)
2045 fprintf(g_log
, "SendTCPMsg_saddr_parse: Before Sending TCP Msg: %d == %lu?\n",
2047 if (g_fLogging
& NALOG_DUMP
)
2048 fprintf(g_log
,"Sending TCP msg:\n[%s]\n", msg
);
2050 tv
.tv_sec
= g_iFunctionTimeout
/ UPNP_TIMEOUT_GRANULARITY
;
2051 tv
.tv_usec
= (g_iFunctionTimeout
% U_TOGRAN
) * 1000000 / U_TOGRAN
;
2053 FD_SET(s
, &writefds
);
2054 iRet
= select(s
+1, 0, &writefds
, 0, &tv
);
2056 if (g_fLogging
& NALOG_ERROR
)
2057 fprintf(g_log
, "SendTCPMsg/parse: select failed (%d)\n", errno
);
2059 pthread_mutex_unlock(&g_xUPnPMsg
);
2063 if (g_fLogging
& NALOG_ERROR
)
2064 fprintf(g_log
, "SendTCPMsg/parse: select timed out\n");
2066 pthread_mutex_unlock(&g_xUPnPMsg
);
2067 return NA_E_TIMEOUT
;
2070 iRet
= send(s
, msg
, iLen
, 0);
2074 if (g_fLogging
& NALOG_ALERT
)
2075 fprintf(g_log
, "SendTCPMsg: iRet (%d) != strlen(msg) (%d)!\n",
2078 if (result
== NULL
) { // if caller just want to send/display msgs
2079 if (g_fLogging
& NALOG_DUMP
)
2080 fprintf(g_log
, "TCP Buffer: [");
2083 if (g_fLogging
& NALOG_INFO1
)
2084 fprintf(g_log
, "start recv @%lu\n", time(NULL
));
2088 iContentLength
= -1;
2092 struct timeval timeout
;
2096 FD_SET(s
, &readfds
);
2098 // In testing, the Linksys Wireless-G Broadband Router "WRT54GS" takes
2099 // up to four seconds to respond, and even then only a partial response,
2100 // with the remainder coming in a second TCP segment half a second later.
2101 // Accordingly, we wait up to five seconds for the initial data, and then after that
2102 // wait one second after subsequent TCP segments, in care more data is still coming.
2103 timeout
.tv_sec
= iBufLen
? 1 : 5;
2104 timeout
.tv_usec
= 0;
2105 iRet
= select(s
+1, &readfds
, NULL
, NULL
, &timeout
);
2107 //fprintf(stderr, "**********: select failed (%d/%d)\n", iRet, errno);
2111 //gettimeofday(&tv_end, NULL);
2112 //GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed);
2113 //fprintf(stderr, "p == loop: %d.%06d\n", tv_elapsed.tv_sec, tv_elapsed.tv_usec);
2115 // if only sending messages
2116 if (result
== NULL
) {
2118 i
= recv(s
, t
, 1000-1, 0); // leave room for '\0' for dump
2120 if (g_fLogging
& NALOG_DUMP
) {
2122 fprintf(g_log
, "%s", t
);
2127 // EO result buf: discard extra bytes
2128 if (resultSize
<= iBufLen
) {
2130 i
= recv(s
, &t
, 1000, 0);
2131 if (g_fLogging
& NALOG_INFO1
)
2132 fprintf(g_log
, "SendTCPMsg_saddr_parse discarding %d bytes\n", i
);
2134 // Note that there's no dump here - prevents DoS attack from
2135 // flooding the logs/diskspace
2139 i
= recv(s
, result
+ iBufLen
, resultSize
- iBufLen
, 0);
2140 if (g_fLogging
& NALOG_INFO1
)
2141 fprintf(g_log
, "SendTCPMsg_saddr_parse read %d bytes (%d/%d)\n", i
, iBufLen
, resultSize
);
2147 if (EAGAIN
== errno
) continue;
2153 // parse and see if we can find content-length to quit early
2154 iContentLength
= FindContentLength(result
, iBufLen
);
2156 // now if we're still in header, see if we can find body
2157 iBodyOffset
= FindBody(result
, iBufLen
);
2161 //fprintf(stderr, "p -- \n");
2163 if (g_fLogging
& NALOG_INFO1
)
2164 fprintf(g_log
, "SendTCPMsg_saddr_parse done recv %d @ %lu\n", iBufLen
, time(NULL
));
2166 if (result
== NULL
) { // if caller just want to send/display msgs
2167 if (g_fLogging
& NALOG_DUMP
)
2168 fprintf(g_log
, "]\n");
2172 pthread_mutex_unlock(&g_xUPnPMsg
);
2178 // szSOAPMsgControlAHeaderFMT - 4 args (ctrl_url, host/port, action, length)
2179 // szSOAPMsgControlABodyFMT - 2 args (action, args string)
2180 // szSOAPMsgControlAArgumentFMT - 2 args (name/value)
2181 static PHTTPResponse
SendSOAPMsgControlAction(
2187 //char outBuffer[65536];
2188 //char outBufferBody[65536];
2189 //char outBufferArgs[65536];
2190 char *outBuffer
= NULL
;
2191 char *outBufferBody
= NULL
;
2192 char *outBufferArgs
= NULL
;
2193 char *inBuffer
= NULL
;
2201 PHTTPResponse pResponse
= NULL
;
2204 if (!WaitUPnPFunction())
2207 if ((outBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2208 if (g_fLogging
& NALOG_ERROR
)
2209 fprintf(g_log
, "can't malloc for outBuffer\n");
2212 if ((outBufferBody
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2213 if (g_fLogging
& NALOG_ERROR
)
2214 fprintf(g_log
, "can't malloc for outBufferBody\n");
2217 if ((outBufferArgs
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2218 if (g_fLogging
& NALOG_ERROR
)
2219 fprintf(g_log
, "can't malloc for outBufferArgs\n");
2222 if ((inBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2223 if (g_fLogging
& NALOG_ERROR
)
2224 fprintf(g_log
, "can't malloc for inBuffer\n");
2230 for (i
=0; i
<argc
; i
++) {
2232 if (args
[i
].pszType
== NULL
) {
2233 n
= sprintf(outBufferArgs
+ iArgsLen
,
2234 szSOAPMsgControlAArgumentFMT
,
2235 args
[i
].pszName
, args
[i
].pszValue
);
2238 n
= sprintf(outBufferArgs
+ iArgsLen
,
2239 szSOAPMsgControlAArgumentFMT_t
,
2240 args
[i
].pszName
, args
[i
].pszValue
, args
[i
].pszType
);
2244 outBufferArgs
[iArgsLen
] = '\0';
2246 iBodyLen
= sprintf(outBufferBody
, szSOAPMsgControlABodyFMT
,
2247 action
, outBufferArgs
);
2249 iHeaderLen
= sprintf(outBuffer
, szSOAPMsgControlAHeaderFMT
,
2250 g_szControlURL
, g_szRouterHostPortSOAP
, action
, iBodyLen
);
2253 DumpHex(outBuffer
, iHeaderLen
+1);
2254 DumpHex(outBufferBody
, iBodyLen
+1);
2255 iResultLen
= SendTCPMsg_saddr_2part(
2256 outBuffer
, iHeaderLen
,
2257 outBufferBody
, iBodyLen
,
2258 inBuffer
, MAX_SOAPMSGSIZE
,
2259 &g_saddrRouterSOAP
);
2262 strcpy(outBuffer
+ iHeaderLen
, outBufferBody
);
2263 iLen
= iHeaderLen
+ iBodyLen
;
2265 DumpHex(outBuffer
, iLen
+1);
2267 //strcat(outBuffer, CRLF "0" CRLF CRLF);
2270 iResultLen
= SendTCPMsg_saddr_parse(
2272 inBuffer
, MAX_SOAPMSGSIZE
,
2273 &g_saddrRouterSOAP
);
2276 if (g_fLogging
& NALOG_INFO1
)
2277 fprintf(g_log
, "SendSOAPMsgControlAction iResultLen %d\n", iResultLen
);
2278 if (iResultLen
> 0) {
2279 if (iResultLen
> MAX_SOAPMSGSIZE
) {
2280 if (g_fLogging
& NALOG_ALERT
)
2281 fprintf(g_log
, "result truncated..\n");
2282 iResultLen
= MAX_SOAPMSGSIZE
;
2284 pResponse
= NewHTTPResponse_sz(inBuffer
, iResultLen
, FALSE
);
2285 if (pResponse
!= NULL
) {
2286 PrintHTTPResponse(pResponse
);
2287 //DeleteHTTPResponse(pResponse);
2288 // - return response to caller
2292 if (g_fLogging
& NALOG_ERROR
)
2293 fprintf(g_log
, "No TCP Response\n");
2294 //TracePrint(ELL_TRACE, "UPnP SendSOAPMsg got no TCP response (%d)\n",
2299 if (outBuffer
!= NULL
) free(outBuffer
);
2300 if (outBufferBody
!= NULL
) free(outBufferBody
);
2301 if (outBufferArgs
!= NULL
) free(outBufferArgs
);
2302 if (inBuffer
!= NULL
) free(inBuffer
);
2307 static int FindURLBase(char *pbuf
, int iLen
, char *szURLBase
)
2309 // non reusable XML parsing code:
2310 // ----------------------------------------------
2314 // now skip after end of this tag, then skip until controlURL tag
2315 p
= strstr_n(pbuf
, "<URLBase>", iLen
);
2316 if (p
== NULL
) return -1;
2318 // skip to the actual stuff
2319 p
+= sizeof("<URLBase>") - 1; // minus '\0'
2321 // skip white spaces (just in case)
2325 // copy into szURLBase
2326 while ((*p
!= '\0') && (*p
!= '<') && !isspace(*p
)) {
2327 if (i
++ > 1000) break;
2335 // ----------------------------------------------
2339 static int FindDescInfo(
2342 const char *szParentName
,
2351 // find the device within pbuf
2363 // now skip after end of this tag, then skip until manufacturer tag
2364 iSearchLen
= sprintf(szSearch
, "<%s>", szName
);
2365 p
= strstr_n(pbuf
, szSearch
, iLen
);
2366 if (p
== NULL
) return -1;
2369 // skip white spaces (just in case)
2373 // copy into szValue
2374 while ((*p
!= '\0') && (*p
!= '<')) {
2375 if (i
++ > 1000) break;
2385 static int FindIGDInfo(char *pbuf
, int iLen
, const char *szName
, char *szValue
)
2387 return FindDescInfo(
2389 "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
2393 static int FindManufacturer(char *pbuf
, int iLen
, char *szManuf
)
2395 return FindIGDInfo(pbuf
, iLen
, "manufacturer", szManuf
);
2398 static int FindFriendlyName(char *pbuf
, int iLen
, char *szValue
)
2400 return FindIGDInfo(pbuf
, iLen
, "friendlyName", szValue
);
2403 static int FindModelName(char *pbuf
, int iLen
, char *szValue
)
2405 return FindIGDInfo(pbuf
, iLen
, "modelName", szValue
);
2408 static int FindModelDescription(char *pbuf
, int iLen
, char *szValue
)
2410 return FindIGDInfo(pbuf
, iLen
, "modelDescription", szValue
);
2413 static int FindWANIPInfo(char *pbuf
, int iLen
, const char *szName
, char *szValue
)
2415 return FindDescInfo(
2417 "urn:schemas-upnp-org:service:WANIPConnection:1",
2421 static int FindControlURL(char *pbuf
, int iLen
, char *szControlURL
)
2423 return FindWANIPInfo(pbuf
, iLen
, "controlURL", szControlURL
);
2426 static int FindEventURL(char *pbuf
, int iLen
, char *szEventURL
)
2428 return FindWANIPInfo(pbuf
, iLen
, "eventSubURL", szEventURL
);
2431 static int FindRouterInfo(char *inBuffer
, int iLen
)
2433 if (FindManufacturer(inBuffer
, iLen
, g_szManufacturer
) != 0)
2434 g_szManufacturer
[0] = '\0';
2436 if (FindFriendlyName(inBuffer
, iLen
, g_szFriendlyName
) != 0)
2437 g_szFriendlyName
[0] = '\0';
2439 if (FindModelName(inBuffer
, iLen
, g_szModelName
) != 0)
2440 g_szModelName
[0] = '\0';
2442 if (FindModelDescription(inBuffer
, iLen
, g_szModelDescription
) != 0)
2443 g_szModelDescription
[0] = '\0';
2445 //TracePrint(ELL_TRACE,
2446 // "UPnP Router Info:\n"
2447 // " - manufacturer [%s]\n"
2448 // " - friendly name [%s]\n"
2449 // " - model name [%s]\n"
2450 // " - model desc [%s]\n",
2451 // g_szManufacturer, g_szFriendlyName, g_szModelName, g_szModelDescription);
2456 static void ParseURL(
2457 const char *szBuf
, char *pszHostPort
,
2458 struct sockaddr_in
*psaddr
, char *pszPath
)
2463 unsigned short port
;
2468 if (0 == strncmp(p
, "http://", 7))
2484 // find the port separetor
2490 // HTTP's by default port 80, so don't have it in the "Host:" header
2491 if (80 == port
) *q
= '\0';
2494 if (pszHostPort
) strcpy(pszHostPort
, p
);
2496 if (NULL
!= q
) *q
= '\0';
2498 if (NULL
!= psaddr
) {
2499 psaddr
->sin_family
= AF_INET
;
2500 psaddr
->sin_addr
.s_addr
= inet_addr(p
);
2501 psaddr
->sin_port
= htons(port
);
2504 //TracePrint(ELL_TRACE, "ParseURL [%s] -> [%s][%s] %lu.%lu.%lu.%lu:%u\n",
2506 pszHostPort
?pszHostPort
:"",
2508 ((uint8_t*)&psaddr
->sin_addr
.s_addr
)[0], ((uint8_t*)&psaddr
->sin_addr
.s_addr
)[1],
2509 ((uint8_t*)&psaddr
->sin_addr
.s_addr
)[2], ((uint8_t*)&psaddr
->sin_addr
.s_addr
)[3],
2514 static void GetDeviceDescription(void)
2516 char *outBuffer
= NULL
;
2517 char *inBuffer
= NULL
;
2520 char szURLBase
[1024];
2521 char szControlURL
[1024];
2522 char szEventURL
[1024];
2524 if (!g_fUPnPEnabled
) {
2525 if (g_fLogging
& NALOG_ERROR
)
2526 fprintf(g_log
, "GetDeviceDescription: upnp not enabled\n");
2530 if ((outBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2531 if (g_fLogging
& NALOG_ERROR
)
2532 fprintf(g_log
, "can't malloc for outBuffer\n");
2535 if ((inBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2536 if (g_fLogging
& NALOG_ERROR
)
2537 fprintf(g_log
, "can't malloc for inBuffer\n");
2541 iBufLen
= sprintf(outBuffer
, szSSDPMsgDescribeDeviceFMT
, g_szNATDevDescURL
,
2542 g_szRouterHostPortDesc
);
2544 if (g_fLogging
& NALOG_INFO1
)
2545 fprintf(g_log
, "Describe Device: [%s]\n", outBuffer
);
2546 iLen
= SendTCPMsg_saddr_parse(outBuffer
, iBufLen
, inBuffer
, MAX_SOAPMSGSIZE
,
2547 &g_saddrRouterDesc
);
2549 g_fControlURLSet
= FALSE
;
2551 if (FindControlURL(inBuffer
, iLen
, szControlURL
) != 0) {
2552 if (g_fLogging
& NALOG_ERROR
)
2553 fprintf(g_log
, "GetDeviceDesc: can't find control URL\n");
2557 // start modifying global
2558 pthread_mutex_lock(&g_xUPnP
);
2561 // now see if there's the URLBase
2562 if (FindURLBase(inBuffer
, iLen
, szURLBase
) != 0) {
2563 // not there? try default numbers from device description
2564 memcpy(&g_saddrRouterBase
, &g_saddrRouterDesc
,
2565 sizeof(g_saddrRouterBase
));
2566 strcpy(g_szRouterHostPortBase
, g_szRouterHostPortDesc
);
2570 g_szRouterHostPortBase
, &g_saddrRouterBase
, NULL
);
2572 if ((strlen(g_szRouterHostPortBase
) == 0) ||
2573 (g_saddrRouterBase
.sin_addr
.s_addr
== INADDR_NONE
)) {
2574 memcpy(&g_saddrRouterBase
, &g_saddrRouterDesc
,
2575 sizeof(g_saddrRouterBase
));
2576 strcpy(g_szRouterHostPortBase
, g_szRouterHostPortDesc
);
2581 ParseURL(szControlURL
,
2582 g_szRouterHostPortSOAP
, &g_saddrRouterSOAP
, g_szControlURL
);
2583 if ((strlen(g_szRouterHostPortSOAP
) == 0) ||
2584 (g_saddrRouterSOAP
.sin_addr
.s_addr
== INADDR_NONE
)) {
2585 memcpy(&g_saddrRouterSOAP
, &g_saddrRouterBase
,
2586 sizeof(g_saddrRouterSOAP
));
2587 strcpy(g_szRouterHostPortSOAP
, g_szRouterHostPortBase
);
2591 ////TracePrint(ELL_TRACE, "UPnP Control URL set to[%s][%s]...\n",
2592 // g_szRouterHostPortSOAP, g_szControlURL);
2594 g_fControlURLSet
= TRUE
;
2595 gettimeofday(&g_tvLastUpdateTime
, NULL
);
2596 pthread_cond_broadcast(&g_condUPnPControlURL
);
2598 if (g_fLogging
& NALOG_INFO1
)
2599 fprintf(g_log
, "Got Device Description\n");
2602 FindRouterInfo(inBuffer
, iLen
);
2604 if (FindEventURL(inBuffer
, iLen
, szEventURL
) != 0) {
2605 szEventURL
[0] = '\0';
2608 ParseURL(szEventURL
,
2609 g_szRouterHostPortEvent
, &g_saddrRouterEvent
, g_szEventURL
);
2610 if ((strlen(g_szRouterHostPortEvent
) == 0) ||
2611 (g_saddrRouterEvent
.sin_addr
.s_addr
== INADDR_NONE
)) {
2612 memcpy(&g_saddrRouterEvent
, &g_saddrRouterBase
,
2613 sizeof(g_saddrRouterEvent
));
2614 strcpy(g_szRouterHostPortEvent
, g_szRouterHostPortBase
);
2621 if (outBuffer
!= NULL
) free(outBuffer
);
2622 if (inBuffer
!= NULL
) free(inBuffer
);
2624 pthread_mutex_unlock(&g_xUPnP
);
2628 static void GetIPByName(char *hostname
, unsigned long *ip_ret
)
2632 ip
= inet_addr(hostname
);
2633 if (ip
== INADDR_NONE
) {
2634 struct hostent
*pHEnt
;
2635 pHEnt
= gethostbyname(hostname
);
2636 if (pHEnt
== NULL
) {
2637 if (g_fLogging
& NALOG_ALERT
)
2638 fprintf(g_log
, "Can't translate [%s] to IP...\n", hostname
);
2639 g_dwLocalIP
= INADDR_ANY
;
2642 ip
= *(unsigned long *)(pHEnt
->h_addr
);
2643 if (g_fLogging
& NALOG_INFO1
)
2644 fprintf(g_log
, "hostname [%s] to ip: %u.%u.%u.%u\n", hostname
,
2645 ((uint8_t*)&ip
)[0], ((uint8_t*)&ip
)[1], ((uint8_t*)&ip
)[2], ((uint8_t*)&ip
)[3]);
2650 static void SetLocalIP()
2652 PIPINFO pIPInfo
= NULL
;
2653 int count
= GetIPInfo(&pIPInfo
);
2654 if (NULL
!= pIPInfo
)
2656 // choose first non IPV6 address
2657 // iterate through array and set port information
2659 unsigned long dwFirst
= 0;
2660 for(i
= 0; i
< count
; i
++)
2662 if (!(pIPInfo
[i
].iFlags
& ISIPV6
) &&
2663 (strncmp(pIPInfo
[i
].szIfName
, "ppp", 3) != 0))
2665 unsigned long dwTemp
;
2667 memcpy(&dwTemp
, pIPInfo
[i
].abIP
, sizeof(unsigned long));
2669 if (0 != GetNATIPNetmask(dwTemp
)) {
2670 g_dwLocalIP
= dwTemp
;
2679 g_dwLocalIP
= dwFirst
;
2680 FreeIPInfo(pIPInfo
);
2685 static int FindTagContent(const char *text
, const char *tagname
, char *buf
)
2689 p
= strstr(text
, tagname
);
2691 if (g_fLogging
& NALOG_INFO0
)
2692 fprintf(g_log
, "FindTagContent: can't find %s\n", tagname
);
2693 return NA_E_PARSE_ERROR
;
2696 if (sscanf(p
, "%*[^>]> %[^ <] <", buf
) < 1) {
2697 if (g_fLogging
& NALOG_INFO0
)
2698 fprintf(g_log
, "FindTagContent: Can't parse tag %s\n", tagname
);
2699 return NA_E_PARSE_ERROR
;
2702 return NA_E_SUCCESS
;
2705 mStatus
LNT_UnmapPort(mDNSIPPort PubPort
, mDNSBool tcp
)
2709 //char szRemoteHost[1024];
2710 //unsigned long dwIP;
2711 Property propArgs
[3];
2713 int protocol
= tcp
? IPPROTO_TCP
: IPPROTO_UDP
;
2714 sprintf(szEPort
, "%u", mDNSVal16(PubPort
));
2716 bzero(propArgs
, sizeof(propArgs
));
2717 propArgs
[0].pszName
= "NewRemoteHost";
2718 propArgs
[0].pszValue
= "";
2719 propArgs
[0].pszType
= "string";
2720 propArgs
[1].pszName
= "NewExternalPort";
2721 propArgs
[1].pszValue
= szEPort
;
2722 propArgs
[1].pszType
= "ui2";
2723 propArgs
[2].pszName
= "NewProtocol";
2724 if (protocol
== IPPROTO_TCP
) {
2725 propArgs
[2].pszValue
= "TCP";
2727 else if (protocol
== IPPROTO_UDP
) {
2728 propArgs
[2].pszValue
= "UDP";
2733 propArgs
[2].pszType
= "string";
2735 resp
= SendSOAPMsgControlAction(
2736 "DeletePortMapping", 3, propArgs
, FALSE
);
2738 return mStatus_NATTraversal
;
2741 if (strcmp(resp
->pszStatus
, "200") != 0) {
2742 DeleteHTTPResponse(resp
);
2743 return mStatus_NATTraversal
;
2746 DeleteHTTPResponse(resp
);
2747 return mStatus_NoError
;
2751 static int GetMappingUnused(unsigned short eport
, int protocol
);
2753 extern mStatus
LNT_MapPort(mDNSIPPort priv
, mDNSIPPort pub
, mDNSBool tcp
)
2760 Property propArgs
[8];
2762 int protocol
= tcp
? IPPROTO_TCP
: IPPROTO_UDP
;
2764 if (NA_E_EXISTS
== GetMappingUnused(mDNSVal16(pub
), protocol
))
2765 return mStatus_AlreadyRegistered
;
2767 //DeletePortMapping(eport, protocol);
2769 sprintf(szEPort
, "%u", mDNSVal16(pub
));
2770 sprintf(szIPort
, "%u", mDNSVal16(priv
));
2773 sprintf(szLocalIP
, "%u.%u.%u.%u",
2774 ((uint8_t*)&dwIP
)[0], ((uint8_t*)&dwIP
)[1], ((uint8_t*)&dwIP
)[2], ((uint8_t*)&dwIP
)[3]);
2776 bzero(propArgs
, sizeof(propArgs
));
2777 propArgs
[0].pszName
= "NewRemoteHost";
2778 propArgs
[0].pszValue
= "";
2779 propArgs
[0].pszType
= "string";
2780 propArgs
[1].pszName
= "NewExternalPort";
2781 propArgs
[1].pszValue
= szEPort
;
2782 propArgs
[1].pszType
= "ui2";
2783 propArgs
[2].pszName
= "NewProtocol";
2784 if (protocol
== IPPROTO_TCP
) {
2785 propArgs
[2].pszValue
= "TCP";
2787 else if (protocol
== IPPROTO_UDP
) {
2788 propArgs
[2].pszValue
= "UDP";
2791 return mStatus_BadParamErr
;
2793 propArgs
[2].pszType
= "string";
2794 propArgs
[3].pszName
= "NewInternalPort";
2795 propArgs
[3].pszValue
= szIPort
;
2796 propArgs
[3].pszType
= "ui2";
2797 propArgs
[4].pszName
= "NewInternalClient";
2798 propArgs
[4].pszValue
= szLocalIP
;
2799 propArgs
[4].pszType
= "string";
2800 propArgs
[5].pszName
= "NewEnabled";
2801 propArgs
[5].pszValue
= "1";
2802 propArgs
[5].pszType
= "boolean";
2803 propArgs
[6].pszName
= "NewPortMappingDescription";
2804 sprintf(descr
, "iC%u", mDNSVal16(pub
));
2805 //propArgs[6].pszValue = "V";
2806 propArgs
[6].pszValue
= descr
;
2807 propArgs
[6].pszType
= "string";
2808 propArgs
[7].pszName
= "NewLeaseDuration";
2809 propArgs
[7].pszValue
= "0";
2810 propArgs
[7].pszType
= "ui4";
2812 if (g_fLogging
& NALOG_INFO1
)
2813 fprintf(g_log
, "Sending AddPortMapping priv %u pub %u\n", mDNSVal16(priv
), mDNSVal16(pub
));
2815 resp
= SendSOAPMsgControlAction(
2816 "AddPortMapping", 8, propArgs
, FALSE
);
2818 if (g_fLogging
& NALOG_INFO1
)
2819 fprintf(g_log
, "AddPortMapping resp %p\n", resp
);
2822 return mStatus_NATTraversal
;
2825 if (strcmp(resp
->pszStatus
, "200") != 0) {
2826 DeleteHTTPResponse(resp
);
2827 return mStatus_NATTraversal
;
2830 DeleteHTTPResponse(resp
);
2831 return mStatus_NoError
;
2834 static int GetMappingUnused(unsigned short eport
, int protocol
)
2838 Property propArgs
[3];
2840 unsigned long ip
= 0;
2842 sprintf( szPort
, "%u", eport
);
2844 bzero(&propArgs
, sizeof(propArgs
));
2845 propArgs
[0].pszName
= "NewRemoteHost";
2846 propArgs
[0].pszValue
= "";
2847 propArgs
[0].pszType
= "string";
2848 propArgs
[1].pszName
= "NewExternalPort";
2849 propArgs
[1].pszValue
= szPort
;
2850 propArgs
[1].pszType
= "ui2";
2851 propArgs
[2].pszName
= "NewProtocol";
2852 if (protocol
== IPPROTO_TCP
) {
2853 propArgs
[2].pszValue
= "TCP";
2855 else if (protocol
== IPPROTO_UDP
) {
2856 propArgs
[2].pszValue
= "UDP";
2859 return NA_E_INVALID_PARAMETER
;
2861 propArgs
[2].pszType
= "string";
2863 resp
= SendSOAPMsgControlAction(
2864 "GetSpecificPortMappingEntry", 3, propArgs
, FALSE
);
2866 if ((strcmp(resp
->pszStatus
, "200") == 0) &&
2867 (FindTagContent(resp
->pszBody
, "NewInternalClient", buf
) == 0))
2869 GetIPByName(buf
, &ip
);
2870 if (ip
== g_dwLocalIP
) {
2871 // (perhaps we let it go?)
2872 DeleteHTTPResponse(resp
);
2873 return NA_E_SUCCESS
;
2876 DeleteHTTPResponse(resp
);
2880 DeleteHTTPResponse(resp
);
2883 return NA_E_SUCCESS
;
2886 mStatus
LNT_GetPublicIP(mDNSOpaque32
*IpPtr
)
2890 static struct timeval tvLastGoodIP
= {0,0};
2891 static unsigned long dwLastGoodIP
;
2893 unsigned long *ip
= (unsigned long *)IpPtr
;
2894 if (ip
== NULL
) return mStatus_BadParamErr
;
2896 gettimeofday(&tv
, NULL
);
2897 GetTimeElapsed(&tvLastGoodIP
, &tv
, &tv
);
2900 return dwLastGoodIP
;
2903 resp
= SendSOAPMsgControlAction(
2904 "GetExternalIPAddress", 0, NULL
, FALSE
);
2907 return mStatus_NATTraversal
;
2909 if (FindTagContent(resp
->pszBody
, "NewExternalIPAddress", buf
) == 0) {
2910 if (g_fLogging
& NALOG_INFO1
)
2911 fprintf(g_log
, "Mapped remote host = %s\n", buf
);
2912 *ip
= inet_addr(buf
);
2913 DeleteHTTPResponse(resp
);
2915 gettimeofday(&tvLastGoodIP
, NULL
);
2918 return mStatus_NoError
;
2921 DeleteHTTPResponse(resp
);
2922 return mStatus_NATTraversal
;
2925 static void SendDiscoveryMsg()
2927 // do it twice to avoid lost packet
2928 //SendUDPMsg(szSSDPMsgDiscoverNAT);
2929 SendUDPMsg(szSSDPMsgDiscoverRoot
);
2930 SendUDPMsg(szSSDPMsgDiscoverIGD
);
2931 SendUDPMsg(szSSDPMsgDiscoverNAT
);
2934 // Set up threads for upnp responses, etc.
2935 int LegacyNATInit(void)
2937 //pthread_t UDPthread;
2938 pthread_attr_t attr
;
2940 //struct timeval tv;
2941 LogOperation("LegacyNATInit");
2943 static int fFirstInitLocks
= TRUE
;
2947 //g_fLogging = ~0; // Turns ALL logging on
2954 if (fFirstInitLocks
)
2957 if (pthread_mutex_init(&g_xUPnP
, NULL
)) {
2958 if (g_fLogging
& NALOG_ERROR
)
2959 fprintf(log
, "UpnpInit - mutex init failed\n");
2960 return NA_E_INTERNAL_ERROR
;
2962 if (pthread_cond_init(&g_condUPnP
, NULL
)) {
2963 pthread_mutex_destroy(&g_xUPnP
);
2964 if (g_fLogging
& NALOG_ERROR
)
2965 fprintf(log
, "UpnpInit - cond init failed\n");
2966 return NA_E_INTERNAL_ERROR
;
2968 if (pthread_cond_init(&g_condUPnPControlURL
, NULL
)) {
2969 pthread_mutex_destroy(&g_xUPnP
);
2970 pthread_cond_destroy(&g_condUPnP
);
2971 if (g_fLogging
& NALOG_ERROR
)
2972 fprintf(log
, "UpnpInit - cond init failed\n");
2973 return NA_E_INTERNAL_ERROR
;
2975 if (pthread_mutex_init(&g_xUPnPMsg
, NULL
)) {
2976 pthread_mutex_destroy(&g_xUPnP
);
2977 pthread_cond_destroy(&g_condUPnP
);
2978 pthread_cond_destroy(&g_condUPnPControlURL
);
2979 if (g_fLogging
& NALOG_ERROR
)
2980 fprintf(log
, "UpnpInit - mutex init failed\n");
2981 return NA_E_INTERNAL_ERROR
;
2984 fFirstInitLocks
= FALSE
;
2989 // initialize UDP socket for SSDP
2990 g_sUDP
= SSDPListen();
2991 g_sUDPCancel
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
); // sock to signal canccelation to UDP thread
2992 if (g_sUDP
< 0 || g_sUDPCancel
< 0) {
2993 if (g_fLogging
& NALOG_ERROR
)
2994 fprintf(log
, "UpnpInit - Failed to init multicast socket.\n");
2995 return NA_E_INTERNAL_ERROR
;
2999 pthread_attr_init(&attr
);
3000 iRet
= pthread_create(&g_UDPthread
, &attr
, UDPProc
, log
);
3002 g_fFirstInit
= TRUE
; // so we'll redo this part next time
3005 if (g_fLogging
& NALOG_ERROR
)
3006 fprintf(log
, "UpnpInit - pthread create failed (%d)\n", iRet
);
3007 return NA_E_THREAD_ERROR
;
3010 // set this to FALSE only if first call succeeded
3011 g_fFirstInit
= FALSE
;
3013 //TracePrint(ELL_TRACE, "UPnP init passed\n");
3016 //tv.tv_usec = 20000; // wait 20ms for thread/udp/multicast init
3017 //select(0, 0, 0, 0, &tv);
3020 // send discovery message
3023 return NA_E_SUCCESS
;
3026 int LegacyNATDestroy()
3028 void *UDPThreadRetVal
;
3030 if (g_sTCPCancel
>= 0) close(g_sTCPCancel
);
3031 if (g_sUDPCancel
>= 0) close(g_sUDPCancel
);
3032 pthread_join(g_UDPthread
, &UDPThreadRetVal
);
3035 g_fFirstInit
= TRUE
;
3036 g_fUPnPEnabled
= FALSE
;
3037 g_fControlURLSet
= FALSE
;
3038 return NA_E_SUCCESS
;