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 2005/12/08 03:00:33 cheshire
28 <rdar://problem/4349971> Byte order bugs in Legacy NAT traversal code
30 Revision 1.13 2005/09/07 18:23:05 ksekar
31 <rdar://problem/4151514> Off-by-one overflow in LegacyNATTraversal
33 Revision 1.12 2005/07/22 21:36:16 ksekar
34 Fix GCC 4.0/Intel compiler warnings
36 Revision 1.11 2004/12/03 03:34:20 ksekar
37 <rdar://problem/3882674> LegacyNATTraversal.c leaks threads
39 Revision 1.10 2004/12/01 02:43:49 cheshire
40 Update copyright message
42 Revision 1.9 2004/10/27 02:25:05 cheshire
43 <rdar://problem/3816029> Random memory smashing bug
45 Revision 1.8 2004/10/27 02:17:21 cheshire
46 Turn off "safe_close: ERROR" error messages -- there are too many of them
48 Revision 1.7 2004/10/26 21:15:40 cheshire
49 <rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0
50 Additional fixes: Code should set fds to -1 after closing sockets.
52 Revision 1.6 2004/10/26 20:59:20 cheshire
53 <rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0
55 Revision 1.5 2004/10/26 01:01:35 cheshire
56 Use "#if 0" instead of commenting out code
58 Revision 1.4 2004/10/10 06:51:36 cheshire
59 Declared some strings "const" as appropriate
61 Revision 1.3 2004/09/21 23:40:12 ksekar
62 <rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure
64 Revision 1.2 2004/09/17 01:08:52 cheshire
65 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
66 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
67 declared in that file are ONLY appropriate to single-address-space embedded applications.
68 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
70 Revision 1.1 2004/08/18 17:35:41 ksekar
71 <rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways
76 #include "mDNSEmbeddedAPI.h"
77 #include "mDNSMacOSX.h"
89 #include <sys/types.h>
90 #include <sys/socket.h>
91 #include <netinet/in.h>
92 #include <netinet/tcp.h>
95 #include <sys/ioctl.h>
97 #include <netinet/in.h>
98 #include <arpa/inet.h>
99 #include <sys/sysctl.h>
100 #include <net/route.h>
103 #include <arpa/inet.h>
105 //#include "IPAddr.h"
110 //#include "netaddr.h"
112 // TODO: remove later and do variable length
113 #define MAX_SOAPMSGSIZE 65536
115 // This code accidentally closes fd 0 all over the place
116 // To stop that messing up the mDNSResponder core, we trap it and prevent it
117 static int safe_close(int fd
)
119 if (fd
< 3) { /* LogMsg("safe_close: ERROR sd %d < 3", fd); */ return(-1); }
123 #define close safe_close
125 // This code uses fprintf(stderr, ...) and similar to log error messages
126 // We redirect all of them to syslog using our LogMsg mechanism
127 #define fprintf(file, ...) LogMsg(__VA_ARGS__)
129 ////////////////////////////////////////////////////////////////////////
131 ////////////////////////////////////////////////////////////////////////
134 #define NA_E_SUCCESS (0)
135 #define NA_E_INTERNAL_ERROR (-1) /* somewhere something wrong */
136 #define NA_E_INVALID_PARAMETER (-2) /* bad params */
137 #define NA_E_OPERATION_FAILED (-3) /* can't fulfill request */
138 #define NA_E_TIMEOUT (-4) /* operation timed out */
139 #define NA_E_THREAD_ERROR (-5) /* some error related to threads */
140 #define NA_E_PARSE_ERROR (-6) /* a parsing error occured */
141 #define NA_E_NOT_READY (-7) /* this op can't proceed yet */
142 #define NA_E_NOT_FOUND (-8) /* resource/prereq not found */
143 #define NA_E_NOT_AVAILABLE (-9) /* service not available */
144 #define NA_E_EXISTS (-10) /* can't modify existing item */
145 #define NA_E_AGAIN (-11) /* something wrong - try again */
146 #define NA_E_NOT_SUPPORTED (-12) /* wait until next version */
147 #define NA_E_ABORT (-14) /* operation aborted */
148 #define NA_E_NET (-15) /* network layer problem */
150 // Logging flags - log types (increasing degree of detail)
151 #define NALOG_ERROR (1UL) /* error messages */
152 #define NALOG_ALERT (2UL) /* useful warning/alerts */
153 #define NALOG_INFO0 (4UL) /* info - potential problem */
154 #define NALOG_INFO1 (8UL) /* extra info */
155 #define NALOG_DUMP (16UL) /* data dumps */
157 #define NALOG_RSRV1 (32UL) /* reserved */
158 #define NALOG_RSRV2 (64UL) /* reserved */
159 #define NALOG_RSRV3 (128UL) /* reserved */
161 // Logging flags - component (not used for now)
162 #define NALOG_UPNP (256) /* UPnP */
164 // Default Logging levels
165 #define NALOG_LEVEL0 (0)
166 #define NALOG_LEVEL1 (NALOG_UPNP | NALOG_ERROR)
167 #define NALOG_LEVEL2 (NALOG_LEVEL1 | NALOG_ALERT)
168 #define NALOG_LEVEL3 (NALOG_LEVEL2 | NALOG_INFO0)
169 #define NALOG_LEVEL4 (NALOG_LEVEL3 | NALOG_INFO1)
170 #define NALOG_LEVEL5 (NALOG_LEVEL4 | NALOG_DUMP)
171 #define NALOG_DEFAULT_LEVEL (NALOG_LEVEL2)
173 // Default timeout values (in m-seconds (milli))
174 // 50 milliseconds for function timeout
175 #define NA_DEFAULT_FUNCTION_TIMEOUT (50)
177 ////////////////////////////////////////////////////////////////////////
179 ////////////////////////////////////////////////////////////////////////
180 #define SSDP_IP "239.255.255.250"
181 #define SSDP_PORT 1900
185 #define H_CRLF "\r\n"
186 // SOAP message's CRLF:
187 //#define S_CRLF "\r\n"
190 // standard 200 ok msg
191 #define HTTP200OK "HTTP/1.1 200 OK\r\n\r\n"
192 #define HTTP200OKLEN (sizeof(HTTP200OK) - 1)
194 // maximum time to wait for an event (in microseconds)
195 #define MAX_EXPECTEVENTTIME (10000)
197 ////////////////////////////////////////////////////////////////////////
199 ////////////////////////////////////////////////////////////////////////
200 typedef struct tagProperty
{
204 } Property
, *PProperty
;
206 typedef struct tagHTTPResponse
{
210 Property aHeaders
[30]; // assume at most this many headers
216 } HTTPResponse
, *PHTTPResponse
, **PPHTTPResponse
;
218 ////////////////////////////////////////////////////////////////////////
220 ////////////////////////////////////////////////////////////////////////
221 static const char szSSDPMsgDiscoverRoot
[] =
222 "M-SEARCH * HTTP/1.1\r\n"
223 "Host:239.255.255.250:1900\r\n"
224 "ST:upnp:rootdevice\r\n"
225 "Man:\"ssdp:discover\"\r\n"
229 static const char szSSDPMsgDiscoverIGD
[] =
230 "M-SEARCH * HTTP/1.1\r\n"
231 "Host:239.255.255.250:1900\r\n"
232 "ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
233 "Man:\"ssdp:discover\"\r\n"
237 static const char szSSDPMsgDiscoverNAT
[] =
238 "M-SEARCH * HTTP/1.1\r\n"
239 "Host:239.255.255.250:1900\r\n"
240 "ST:urn:schemas-upnp-org:service:WANIPConnection:1\r\n"
241 "Man:\"ssdp:discover\"\r\n"
245 //// Subscribe message
247 // 2$s: local's host/port ("host:port")
248 // 3$s: router's host/port ("host:port")
249 // 4$d: subscription timeout in seconds
250 static const char szEventMsgSubscribeFMT
[] =
251 "SUBSCRIBE %1$s HTTP/1.1\r\n"
253 "Callback: <http://%2$s/notify>\r\n"
254 "Timeout: Second-%4$d\r\n"
255 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
257 "Content-Length: 0\r\n"
258 "Pragma: no-cache\r\n"
261 //// Unsubscribe message
263 // 2$s: SID (some uuid passed back during subscribe)
264 // 3$s: router's host ("host")
266 static const char szEventMsgUnsubscribeFMT
[] =
267 "UNSUBSCRIBE %1$s HTTP/1.1\r\n"
269 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
271 "Content-Length: 0\r\n"
272 "Pragma: no-cache\r\n"
276 //// Generic SOAP Control:Action request messages
278 // 2$s: router's host/port ("host:port")
279 // 3$s: action (string)
280 // 4$d: content-length
281 static const char szSOAPMsgControlAHeaderFMT
[] =
282 //"M-POST %1$s HTTP/1.1\r\n"
283 "POST %1$s HTTP/1.1\r\n"
284 "Content-Type: text/xml; charset=\"utf-8\"\r\n"
285 //"TEST: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
286 //"Man: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
287 //"01-SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#%3$s\"\r\n"
288 "SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#%3$s\"\r\n"
289 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n"
291 "Content-Length: %4$d\r\n"
292 "Connection: close\r\n"
293 // "Connection: Keep-Alive\r\n"
294 "Pragma: no-cache\r\n"
297 // 1$: action (string)
299 static const char szSOAPMsgControlABodyFMT
[] =
300 "<?xml version=\"1.0\"?>" CRLF
301 "<SOAP-ENV:Envelope" S_CRLF
302 " xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"" S_CRLF
303 " SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" S_CRLF
304 "<SOAP-ENV:Body>" S_CRLF
306 " xmlns:m=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" S_CRLF
309 "</SOAP-ENV:Body>" S_CRLF
310 "</SOAP-ENV:Envelope>" S_CRLF
317 // 2$: argument value
318 static const char szSOAPMsgControlAArgumentFMT
[] =
319 "<%1$s>%2$s</%1$s>" S_CRLF
;
322 // 2$: argument value
324 static const char szSOAPMsgControlAArgumentFMT_t
[] =
326 " xmlns:dt=\"urn:schemas-microsoft-com:datatypes\""
327 " dt:dt=\"%3$s\">%2$s</%1$s>" S_CRLF
;
330 //// Generic SOAP Control:Query request messages
332 // 2$s: router's host/port ("host:port")
333 // 3$d: content-length
334 static const char szSOAPMsgControlQHeaderFMT
[] =
335 "M-POST %1$s HTTP/1.1\r\n"
336 //"POST %1$s HTTP/1.1\r\n"
338 "Content-Length: %3$d\r\n"
339 "Content-Type: text/xml; charset-\"utf-8\"\r\n"
340 //"Man: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
341 //"SOAPAction: \"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"\r\n"
342 "01-SOAPAction: \"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"\r\n"
346 static const char szSOAPMsgControlQBodyFMT
[] =
348 " xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"" S_CRLF
349 " s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" S_CRLF
351 "<u:QueryStateVariable xmlns:u=\"urn:schemas-upnp-org:control-1-0\"" S_CRLF
352 "<u:varName>%s</u:varName>" S_CRLF
353 "</u:QueryStateVariable>" S_CRLF
355 "</s:Envelope>" S_CRLF
358 // 1$: device description URL
360 static const char szSSDPMsgDescribeDeviceFMT
[] =
361 "GET %s HTTP/1.1\r\n"
362 "Accept: text/xml, application/xml\r\n"
363 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
365 "Connection: close\r\n"
366 // "Connection: Keep-Alive\r\n"
369 ////////////////////////////////////////////////////////////////////////
371 ////////////////////////////////////////////////////////////////////////
373 static int g_fFirstInit
= TRUE
;
374 static int g_fQuit
= FALSE
;
376 static int g_fLogging
;
378 // Globally-accessible UDP socket
379 static int g_sUDP
= -1;
380 static int g_sUDPCancel
= -1;
382 // Globally-accessible TCP socket
383 static int g_sTCP
= -1;
384 static int g_sTCPCancel
= -1;
387 static int g_fEventEnabled
= FALSE
;
388 static unsigned short g_wEventPort
;
389 static struct sockaddr_in g_saddrRouterEvent
;
390 static char g_szRouterHostPortEvent
[1024];
391 static char g_szEventURL
[1024];
394 static char g_szFriendlyName
[1024];
395 static char g_szManufacturer
[1024];
396 static char g_szModelName
[1024];
397 static char g_szModelDescription
[1024];
400 static struct sockaddr_in g_saddrRouterBase
;
401 static char g_szRouterHostPortBase
[1024];
404 static pthread_t g_UDPthread
= NULL
;
405 static pthread_t g_TCPthread
= NULL
;
408 static unsigned long g_dwLocalIP
= 0;
410 // Globally accessible info about the router/UPnP
411 static int g_fUPnPEnabled
= FALSE
;
412 static char g_szUSN
[1024];
414 static struct sockaddr_in g_saddrRouterDesc
;
415 static char g_szRouterHostPortDesc
[1024];
416 static char g_szNATDevDescURL
[1024];
418 static struct sockaddr_in g_saddrRouterSOAP
;
419 static char g_szRouterHostPortSOAP
[1024];
420 static char g_szControlURL
[1024];
421 static int g_fControlURLSet
= FALSE
;
423 // Lock/condvar for synchronous upnp calls
424 static pthread_mutex_t g_xUPnP
;
425 static pthread_mutex_t g_xUPnPMsg
;
426 static pthread_cond_t g_condUPnP
;
427 static pthread_cond_t g_condUPnPControlURL
;
428 static struct timeval g_tvUPnPInitTime
;
429 static struct timeval g_tvLastUpdateTime
;
431 // timeout values in seconds
432 static int g_iFunctionTimeout
= NA_DEFAULT_FUNCTION_TIMEOUT
;
434 static void GetDeviceDescription(void);
435 static void SetLocalIP(void);
437 ////////////////////////////////////////////////////////////////////////
439 ////////////////////////////////////////////////////////////////////////
444 #define IFNAMELEN 16 /* Interface Name Length */
445 #define IPLEN 16 /* 16 bytes(128 bits) for IPv6 */
447 typedef struct tagIPINFO
450 char szIfName
[IFNAMELEN
]; /* Interface name */
451 unsigned char abIP
[IPLEN
];
452 unsigned short wPort
;
453 } IPINFO
, *PIPINFO
, **PPIPINFO
;
455 typedef struct hostent HOSTENT
, *PHOSTENT
;
457 static unsigned long GetNATIPNetmask(unsigned long dwIP
)
459 static const union { uint8_t b
[4]; uint32_t l
; } mask_10
= { { 255, 0, 0, 0 } }; // Mask for 10/8
460 static const union { uint8_t b
[4]; uint32_t l
; } mask172
= { { 255, 240, 0, 0 } }; // Mask for 172.16/12
461 static const union { uint8_t b
[4]; uint32_t l
; } mask192
= { { 255, 255, 0, 0 } }; // Mask for 192.168/16
462 uint8_t *p
= (uint8_t *)&dwIP
;
463 if (p
[0] == 10 ) return mask_10
.l
;
464 if (p
[0] == 172 && (p
[1] & 0xF0) == 16) return mask172
.l
;
465 if (p
[0] == 192 && p
[1] == 168 ) return mask192
.l
;
466 return 0; /* No NAT IP */
469 static int GetIPInfo(PPIPINFO ppIPInfo
)
472 int iLastLen
, iLen
, iNum
= 0, iMax
= 0;
474 char *pcBuf
, *pcTemp
;
475 PIPINFO pIPInfo
= NULL
;
477 struct ifreq
*ifr
, ifrcopy
;
479 if (ppIPInfo
== NULL
) return 0;
481 fd
= socket(AF_INET
, SOCK_DGRAM
, 0);
484 iLen
= 100 * sizeof(struct ifreq
);
488 pcBuf
= (char *)malloc(iLen
);
492 if (ioctl(fd
, SIOCGIFCONF
, &ifc
) < 0)
494 if (errno
!= EINVAL
|| iLastLen
!= -1)
496 // DbgPrint(ELL_ERROR, "ioctl failed(%d)\n", errno);
504 if (ifc
.ifc_len
== iLastLen
) break;
505 iLastLen
= ifc
.ifc_len
;
508 iLen
+= 10 * sizeof(struct ifreq
);
512 for (pcTemp
= pcBuf
; pcTemp
< pcBuf
+ ifc
.ifc_len
; )
519 pIPInfoNew
= (PIPINFO
)realloc(pIPInfo
, sizeof(IPINFO
) * iMax
);
520 if (pIPInfoNew
== NULL
)
527 else pIPInfo
= pIPInfoNew
;
529 memset(pIPInfo
+ (iMax
- 10), 0, sizeof(IPINFO
) * 10);
532 ifr
= (struct ifreq
*)pcTemp
;
534 pcTemp
+= sizeof(ifr
->ifr_name
) + ifr
->ifr_addr
.sa_len
;
536 /* discard invalid address families & loopback */
537 if ((ifr
->ifr_addr
.sa_family
!= AF_INET
&&
538 ifr
->ifr_addr
.sa_family
!= AF_INET6
) ||
539 strncmp(ifr
->ifr_name
, "lo", 2) == 0) continue;
542 ioctl(fd
, SIOCGIFFLAGS
, &ifrcopy
);
543 if ((ifrcopy
.ifr_flags
& IFF_UP
) == 0) continue;
545 switch (ifr
->ifr_addr
.sa_family
)
548 memcpy(pIPInfo
[iNum
].szIfName
, ifr
->ifr_name
, IFNAMELEN
);
549 dwIP
= ((struct sockaddr_in
*)&ifr
->ifr_addr
)->sin_addr
.s_addr
;
550 memcpy(pIPInfo
[iNum
].abIP
, &dwIP
, sizeof(unsigned long));
551 if (ifrcopy
.ifr_flags
& IFF_POINTOPOINT
)
552 pIPInfo
[iNum
].iFlags
|= ISPPP
;
557 memcpy(pIPInfo
[iNum
].szIfName
, ifr
->ifr_name
, IFNAMELEN
);
558 memcpy(pIPInfo
[iNum
].abIP
,
559 ((struct sockaddr_in6
*)&(ifr
->ifr_addr
))-> sin6_addr
.s6_addr
,
561 pIPInfo
[iNum
].iFlags
|= ISIPV6
;
562 if (ifrcopy
.ifr_flags
& IFF_POINTOPOINT
)
563 pIPInfo
[iNum
].iFlags
|= ISPPP
;
580 static void FreeIPInfo(PIPINFO pIPInfo
)
582 if (pIPInfo
!= NULL
) free(pIPInfo
);
586 ////////////////////////////////////////////////////////////////////////
587 // Function Definitions
588 ////////////////////////////////////////////////////////////////////////
590 static void SendDiscoveryMsg();
593 // Creates a UDP multicast socket and listens to the SSDP IP/PORT
595 // -1 on error, or the socket descriptor if success
596 static int SSDPListen()
601 struct sockaddr_in saddr
;
604 // IPPROTO_IP == 0; IPPROTO_TCP == 6; IPPROTO_UDP == 17; etc.
605 sd
= socket(AF_INET
, SOCK_DGRAM
, 0);
607 if (g_fLogging
& NALOG_ERROR
)
608 fprintf(g_log
, "Can't create socket! SSDPListen exiting\n");
612 // sock options values
613 fLoop
= 0; // false - don't send copy to self
616 // bind to listen to ssdp multicast address
617 bzero(&saddr
, sizeof(saddr
));
618 saddr
.sin_len
= sizeof(saddr
);
619 saddr
.sin_family
= AF_INET
;
620 //saddr.sin_addr.s_addr = inet_addr(SSDP_IP);
621 //saddr.sin_port = htons(SSDP_PORT);
622 saddr
.sin_addr
.s_addr
= g_dwLocalIP
;
625 // and set the multicast add_member structure
626 // (TODO: need to find interfaces later - ioctl, with:
627 // SIOCFIFCONF to find if's, SIOCGIFADDR to get addr, and SIOCFIFFLAGS
628 // to check for IFF_MULTICAST flag for multicast support on an if)
629 bzero(&mreq
, sizeof(mreq
));
630 mreq
.imr_interface
.s_addr
= g_dwLocalIP
;
631 mreq
.imr_multiaddr
.s_addr
= inet_addr(SSDP_IP
);
634 bind(sd
, (struct sockaddr
*)&saddr
, sizeof(saddr
)) //||
635 //setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &fLoop, sizeof(fLoop)) ||
636 //setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &iTTL, sizeof(iTTL)) ||
637 //setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))
639 if (g_fLogging
& NALOG_ERROR
)
641 "bind/setsockopt for multicast failed... errno = %d\n", errno
);
649 static int EventListen()
651 struct sockaddr_in saddr
;
654 // try 5 ports before failing completely
655 for (g_wEventPort
= 5000; g_wEventPort
< 5005; g_wEventPort
++)
657 sd
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
659 if (g_fLogging
& NALOG_ERROR
)
660 fprintf(g_log
, "Can't create socket! EventListen exiting\n");
664 bzero(&saddr
, sizeof(saddr
));
665 saddr
.sin_len
= sizeof(saddr
);
666 saddr
.sin_family
= AF_INET
;
667 saddr
.sin_addr
.s_addr
= g_dwLocalIP
;
668 saddr
.sin_port
= htons(g_wEventPort
);
671 if (bind(sd
, (struct sockaddr
*)&saddr
, sizeof(saddr
)) == 0)
674 ////TracePrint(ELL_TRACE, "UPnP: EventListen @%u\n", g_wEventPort);
678 // unsuccessful - close sd and try again
679 if (g_fLogging
& NALOG_ERROR
)
681 "bind TCP port %u failed: errno = %d\n", g_wEventPort
, errno
);
688 static void *TCPProc(void *in
);
690 static int EventInit()
695 if (g_fEventEnabled
== FALSE
)
697 // initialize TCP socket for Eventing
698 g_sTCP
= EventListen();
700 if (g_fLogging
& NALOG_ERROR
)
701 fprintf(g_log
, "EventInit - Failed to init tcp socket.\n");
702 return NA_E_INTERNAL_ERROR
;
706 pthread_attr_init(&attr
);
707 iRet
= pthread_create(&g_TCPthread
, &attr
, TCPProc
, 0);
711 if (g_fLogging
& NALOG_ERROR
)
712 fprintf(g_log
, "EventInit: TCPProc create failed(%d)\n", iRet
);
713 return NA_E_THREAD_ERROR
;
717 g_fEventEnabled
= TRUE
;
722 static void DumpHex(char *buf
, int len
)
729 if (g_fLogging
& NALOG_DUMP
) {
730 if (buf
== NULL
) return;
731 if (len
<= 0) return;
733 for (i
= 0; i
< len
; i
= nexti
) {
734 fprintf(g_log
, "%04x: ", i
);
736 endj
= (nexti
> len
) ? len
: nexti
;
737 for (j
= i
; j
< endj
; j
++)
738 fprintf(g_log
, "%02x %c ", buf
[j
] & 0xff, buf
[j
]);
741 char pad
[3 * 16 + 1]; // don't need the last 3 bytes anyway
742 j
= (16 - (j
% 16)) * 3;
748 for (j
= i
; j
< endj
; j
++)
749 isprint(buf
[j
]) ? fputc(buf
[j
], g_log
) : fputc('.', g_log
);
756 // FindHTTPHeaderNewLine
757 // Returns a pointer to the beginning of a CRLF, that is not a
758 // part of LWS. (LWS is CRLF followed by a space or tab, and in
759 // HTTP, considered as equivalent to a single space) (LWS stands
760 // for "linear white space")
761 // Returns a pointer the beginning of CRLF, and sets the EOH flag to
762 // whether this is the last header in the HTTP header section.
763 // Also, if pbuf is NULL, or if there isn't any CRLF found in the
764 // string, or if the HTTP syntax is wrong, NULL is returned, and
765 // the EOH flag is not touched.
766 static char *FindHTTPHeaderNewLine(char *pbuf
, int iBufSize
, int *pfEOH
)
771 if (pbuf
== NULL
) return NULL
;
774 result
= memchr(pbuf
, '\r', iBufSize
);
775 if (result
== NULL
) {
776 if (g_fLogging
& NALOG_INFO0
) {
777 fprintf(g_log
, "FindHTTPHeaderNewLine: er @(%d/%d)\n", i
, iBufSize
);
784 // decrement iBufSize, and move pbuf forward
785 iBufSize
-= (result
- pbuf
);
788 ++pbuf
; // now pointing right after "\r"
790 if (*pbuf
== '\0') break;
791 if (*pbuf
!= '\n') continue;
793 ++pbuf
; // now pointing after "\r\n"
795 if (*pbuf
== '\0') break;
796 if ((*pbuf
== ' ') || (*pbuf
== '\t')) continue;
798 // at this point we know we're at the end of a header field,
799 // and there's more stuff coming...
801 // just need to check if this is the last header
802 if ((pbuf
[0] == '\r') && (pbuf
[1] == '\n'))
813 // NewHTTPResponse_sz
814 // Creates an HTTPResponse structure from a string (sz). Set
815 // fDestroyOriginal to TRUE if the buffer passed in can be overwritten.
816 // Otherwise, NewHTTPResponse_sz will duplicate the buffer.
817 // Returns the created HTTPResponse structure if successful, or if an
818 // error occured (out of memory, or bad http syntax), returns NULL.
819 // NOTE: ALWAYS call DeleteHTTPResponse after using the HTTPResponse structure.
820 // NOTE: The input is assumed to be correct. If there're HTTP syntax errors,
821 // and the pszHTTPResponse is not null-terminated, result may be undefined.
822 // (to be fixed next version)
823 static PHTTPResponse
NewHTTPResponse_sz(
824 char *pszHTTPResponse
,
826 int fDestroyOriginal
)
828 PHTTPResponse pResponse
;
834 if ((pResponse
= (PHTTPResponse
)malloc(sizeof(HTTPResponse
))) == NULL
) {
835 if (g_fLogging
& NALOG_INFO0
) {
836 fprintf(g_log
, "NewHTTPResponse_sz: er 1\n");
842 // make copy of buffer now
843 if (fDestroyOriginal
) {
844 pResponse
->buf
= NULL
;
845 pBuf
= pszHTTPResponse
;
848 int len
= strlen(pszHTTPResponse
);
849 if ((len
+1) > iBufferSize
) {
850 if (g_fLogging
& NALOG_INFO0
)
851 fprintf(g_log
, "Length: %d > %d\n", len
+1, iBufferSize
);
854 if ((pResponse
->buf
= (char *)malloc(iBufferSize
)) == NULL
) {
856 if (g_fLogging
& NALOG_INFO0
) {
857 fprintf(g_log
, "NewHTTPResponse_sz: er 2\n");
862 memcpy(pResponse
->buf
, pszHTTPResponse
, iBufferSize
);
863 pBuf
= pResponse
->buf
;
866 // get the first line
867 pszEOL
= FindHTTPHeaderNewLine(pBuf
, iBufferSize
, &fEOH
);
868 if (pszEOL
== NULL
) {
869 if (g_fLogging
& NALOG_INFO0
) {
870 fprintf(g_log
, "NewHTTPResponse_sz: er 3\n");
876 *pszEOL
= '\0'; // terminate the status line
877 pszEOL
+= 2; // point to the rest of the buffer
879 // set the status string first
880 pResponse
->pszStatus
= strchr(pBuf
, ' ');
881 if (pResponse
->pszStatus
== NULL
) {
882 if (g_fLogging
& NALOG_INFO0
) {
883 fprintf(g_log
, "NewHTTPResponse_sz: er 4\n");
886 goto cleanup
; // syntax error
889 pResponse
->pszStatus
++; // point to the actual status
891 pResponse
->pszReason
= strchr(pResponse
->pszStatus
, ' ');
892 if (pResponse
->pszReason
== NULL
) {
893 if (g_fLogging
& NALOG_INFO0
) {
894 fprintf(g_log
, "NewHTTPResponse_sz: er 5\n");
897 goto cleanup
; // syntax error
900 pResponse
->pszReason
[0] = '\0'; // terminate status string
901 pResponse
->pszReason
++; // point to the reason string
903 iNumHeaders
= 0; // initialize to 0 headers
905 // parse header fields line by line (while not end of headers)
907 PProperty pHeader
= &(pResponse
->aHeaders
[iNumHeaders
]);
908 // point header field name to the first char of the line
909 pHeader
->pszName
= pszEOL
;
911 // search for the end of line
912 pszEOL
= FindHTTPHeaderNewLine(pszEOL
,
913 iBufferSize
- (pszEOL
- pBuf
), // remainder size
915 if (pszEOL
== NULL
) {
916 if (g_fLogging
& NALOG_INFO0
) {
917 fprintf(g_log
, "NewHTTPResponse_sz: er reading header field %d @ %lu / %lu\n",
918 iNumHeaders
, pHeader
->pszName
- pBuf
, iBufferSize
);
919 DumpHex(pszHTTPResponse
, iBufferSize
);
922 goto cleanup
; // syntax error
925 *pszEOL
= '\0'; // terminate this string
926 pszEOL
+= 2; // point to beginning of next line
928 pHeader
->pszValue
= strchr(pHeader
->pszName
, ':');
929 if (pHeader
->pszValue
== NULL
) {
930 if (g_fLogging
& NALOG_INFO0
) {
931 fprintf(g_log
, "NewHTTPResponse_sz: er 6\n");
934 goto cleanup
; // syntax error (header field has no ":")
937 pHeader
->pszValue
[0] = '\0'; // terminate the header name string
938 pHeader
->pszValue
++; // point after the ":"
939 // get rid of leading spaces for the value part
941 (pHeader
->pszValue
[0] == ' ') ||
942 (pHeader
->pszValue
[0] == '\t') ||
943 (pHeader
->pszValue
[0] == '\r') ||
944 (pHeader
->pszValue
[0] == '\n')
946 pHeader
->pszValue
++; // skip the space
949 iNumHeaders
++; // added one more header
950 pHeader
++; // point to the next header in pResponse->aHeaders
953 pResponse
->iNumHeaders
= iNumHeaders
; // remember to set it in pResponse
955 pResponse
->pszBody
= pszEOL
+ 2; // point after the empty line
960 if (pResponse
->buf
!= NULL
) free(pResponse
->buf
);
965 // DeleteHTTPResponse
966 // Deallocates stuff in the HTTPResponse structure, effectively returning
967 // memory to the system and destroying the structure.
968 // NOTE: The pointer pResponse WILL BE FREED, and will be unusable after
969 // the call to DeleteHTTPResponse.
970 static void DeleteHTTPResponse(PHTTPResponse pResponse
)
974 if (pResponse
== NULL
) return;
976 // Current impl is just simple array - no need to free()
977 //for (i = 0; i < pResponse->iNumHeaders; i++) {
978 // free(pResponse->aHeaders[i]);
981 if (pResponse
->buf
!= NULL
)
982 free(pResponse
->buf
);
986 //typedef struct tagHTTPResponse {
990 // Property aHeaders[30]; // assume at most this many headers
996 //} HTTPResponse, *PHTTPResponse, **PPHTTPResponse;
998 static void PrintHTTPResponse(PHTTPResponse pResponse
)
1002 if (g_fLogging
& (NALOG_INFO1
)) {
1003 if (pResponse
== NULL
) return;
1004 fprintf(g_log
, " *** HTTP response begin *** \n");
1005 fprintf(g_log
, " * status = [%s], reason = [%s] *\n",
1006 pResponse
->pszStatus
, pResponse
->pszReason
);
1007 for (i
= 0; i
< pResponse
->iNumHeaders
; i
++) {
1008 fprintf(g_log
, " * Header \"%s\" = [%s]\n",
1009 pResponse
->aHeaders
[i
].pszName
,
1010 pResponse
->aHeaders
[i
].pszValue
);
1012 if (g_fLogging
& NALOG_DUMP
)
1013 fprintf(g_log
, " * body = [%s] *\n", pResponse
->pszBody
);
1014 fprintf(g_log
, " *** HTTP response end *** \n");
1018 static int DiscoverRouter(PHTTPResponse pResponse
)
1021 int fLocation
= FALSE
;
1023 int fIsNATDevice
= FALSE
;
1026 if (strcmp(pResponse
->pszStatus
, "200") != 0)
1030 if (pResponse
== NULL
) {
1031 if (g_fLogging
& NALOG_INFO0
)
1032 fprintf(g_log
, "DiscoverRouter: pResponse == NULL\n");
1036 // check to see if this is a relevant packet
1037 for (i
= 0; i
< pResponse
->iNumHeaders
; i
++) {
1038 PProperty pHeader
= &(pResponse
->aHeaders
[i
]);
1040 if ((strcasecmp(pHeader
->pszName
, "ST") == 0) ||
1041 (strcasecmp(pHeader
->pszName
, "NT") == 0)) {
1042 if ((strcmp(pHeader
->pszValue
,
1043 "urn:schemas-upnp-org:service:WANIPConnection:1") == 0) ||
1044 (strcmp(pHeader
->pszValue
,
1045 "urn:schemas-upnp-org:device:InternetGatewayDevice:1") == 0)) {
1046 fIsNATDevice
= TRUE
;
1051 // leave the message alone if we don't need it
1055 // Now that we know we're looking at the message about the NAT device:
1056 pthread_mutex_lock(&g_xUPnP
);
1058 // set upnp to be unconfigured for now
1059 g_fUPnPEnabled
= FALSE
;
1061 // loop through the headers
1062 for (i
= 0; i
< pResponse
->iNumHeaders
; i
++) {
1063 PProperty pHeader
= &(pResponse
->aHeaders
[i
]);
1065 if (strcasecmp(pHeader
->pszName
, "Location") == 0) {
1069 if (g_fLogging
& NALOG_INFO1
)
1070 fprintf(g_log
, "Checking Location...\n");
1071 p
= pHeader
->pszValue
;
1072 if (strncmp(p
, "http://", 7) != 0)
1073 continue; // hope for another Location header to correct it
1074 p
+= 7; // skip over "http://"
1077 // set the control URL first
1079 g_szNATDevDescURL
[0] = '/';
1080 g_szNATDevDescURL
[1] = '\0';
1083 strncpy(g_szNATDevDescURL
, q
, sizeof(g_szNATDevDescURL
) - 1);
1084 g_szNATDevDescURL
[sizeof(g_szNATDevDescURL
) - 1] = '\0';
1085 // terminate the host/port string
1089 if (g_fLogging
& NALOG_INFO1
)
1090 fprintf(g_log
, " Device Description URL set to[%s]...\n",
1093 // see if port is specified
1096 sprintf(g_szRouterHostPortDesc
, "%s", p
);
1098 g_saddrRouterDesc
.sin_addr
.s_addr
= inet_addr(p
);
1099 g_saddrRouterDesc
.sin_port
= htons(80);
1102 // don't include the ":80" - HTTP is by default port 80
1103 if (atoi(q
+1) == 80) *q
= '\0';
1105 strcpy(g_szRouterHostPortDesc
, p
);
1107 // terminate the host part and point to it
1111 g_saddrRouterDesc
.sin_addr
.s_addr
= inet_addr(p
);
1112 g_saddrRouterDesc
.sin_port
= htons(atoi(q
));
1115 g_saddrRouterDesc
.sin_family
= AF_INET
;
1117 if (g_fLogging
& NALOG_INFO1
)
1118 fprintf(g_log
, " Router Address set to[%s]...\n",
1119 g_szRouterHostPortDesc
);
1122 else if (strcasecmp(pHeader
->pszName
, "USN") == 0) {
1123 if (g_fLogging
& NALOG_INFO1
)
1124 fprintf(g_log
, "Checking USN...\n");
1125 strncpy(g_szUSN
, pHeader
->pszValue
, sizeof(g_szUSN
) - 1);
1126 g_szUSN
[sizeof(g_szUSN
) - 1] = '\0';
1130 ; // do nothing for other headers for now
1134 // now check flags and set enabled if all set
1135 if (fLocation
&& fUSN
) {
1136 if (g_fLogging
& NALOG_INFO1
) {
1138 "Description Host/port string: [%s]\n"
1139 "NATDevDescURL: [%s], USN: [%s]\n",
1140 g_szRouterHostPortDesc
,
1141 g_szNATDevDescURL
, g_szUSN
);
1142 if (g_fLogging
& NALOG_INFO1
)
1143 fprintf(g_log
, "Got router information\n");
1146 g_fUPnPEnabled
= TRUE
;
1147 pthread_cond_broadcast(&g_condUPnP
);
1150 // remember to unlock before return
1151 pthread_mutex_unlock(&g_xUPnP
);
1156 // granularity is specified as: granularity = 1/nth seconds
1157 #define UPNP_TIMEOUT_GRANULARITY (1000)
1158 #define U_TOGRAN UPNP_TIMEOUT_GRANULARITY
1161 static void TimevalSubtract(
1162 struct timeval
*result
,
1163 const struct timeval
*a
,
1164 const struct timeval
*b
)
1166 result
->tv_sec
= a
->tv_sec
- b
->tv_sec
;
1168 if (b
->tv_usec
> a
->tv_usec
) {
1170 result
->tv_usec
= 1000000 + a
->tv_usec
- b
->tv_usec
;
1173 result
->tv_usec
= a
->tv_usec
- b
->tv_usec
;
1176 // elapsed = end - start
1177 static void GetTimeElapsed(
1178 const struct timeval
*tv_start
,
1179 const struct timeval
*tv_end
,
1180 struct timeval
*tv_elapsed
)
1182 TimevalSubtract(tv_elapsed
, tv_end
, tv_start
);
1184 tv_elapsed
->tv_sec
= tv_end
->tv_sec
- tv_start
->tv_sec
;
1186 if (tv_start
->tv_usec
> tv_end
->tv_usec
) {
1187 tv_elapsed
->tv_sec
--;
1188 tv_elapsed
->tv_usec
= 1000000 + tv_end
->tv_usec
- tv_start
->tv_usec
;
1191 tv_elapsed
->tv_usec
= tv_end
->tv_usec
- tv_start
->tv_usec
;
1195 // returns +1, 0, or -1, if a>b, a==b, a<b, respectively
1196 static int CompareTime(
1197 const struct timeval
*a
,
1198 const struct timeval
*b
1201 if ((a
->tv_sec
== b
->tv_sec
) &&
1202 (a
->tv_usec
== b
->tv_usec
)) return 0;
1204 if (a
->tv_sec
> b
->tv_sec
) return 1;
1205 else if (a
->tv_sec
< b
->tv_sec
) return -1;
1207 // if seconds are equal...
1208 if (a
->tv_usec
> b
->tv_usec
) return 1;
1212 static int WaitControlURLSet(double timeout
)
1216 struct timeval tv_start
;
1218 long to_sec
= (int) (timeout
/ U_TOGRAN
);
1220 (int) (((timeout
/ U_TOGRAN
) - to_sec
) * 1000000.0);
1221 //long to_sec = (int) timeout;
1222 //long to_usec = (int) ((timeout - to_sec) * 1000000.0);
1223 struct timeval elapsed
;
1225 // get function start time
1226 gettimeofday(&tv_start
, NULL
);
1228 pthread_mutex_lock(&g_xUPnP
);
1231 // if last update is too long ago then wait for it
1232 GetTimeElapsed(&g_tvLastUpdateTime
, &tv_start
, &elapsed
);
1233 if ((elapsed
.tv_sec
+ (elapsed
.tv_usec
/ 1000000.0)) >
1234 (((double) g_iUPnPTimeout
) / U_TOGRAN
))
1235 g_fControlURLSet
= 0;
1238 while (!g_fControlURLSet
) {
1240 gettimeofday(&tv
, NULL
);
1243 for now ignore device timeout
1244 // see if we've past the device's timeout first
1245 GetTimeElapsed(&g_tvUPnPInitTime
, &tv
, &elapsed
);
1246 if ((elapsed
.tv_sec
> g_timeout_sec
) ||
1247 ( (elapsed
.tv_sec
== g_timeout_sec
) &&
1248 (elapsed
.tv_usec
> g_timeout_usec
)
1251 pthread_mutex_unlock(&g_xUPnP
);
1256 // calculate ts to sleep till
1257 ts
.tv_sec
= tv
.tv_sec
+ to_sec
;
1258 ts
.tv_nsec
= (tv
.tv_usec
+ to_usec
) * 1000;
1259 if (ts
.tv_nsec
> 1000000000) {
1260 ts
.tv_nsec
-= 1000000000;
1264 // now get how long we've been in this function already and deduct
1265 GetTimeElapsed(&tv_start
, &tv
, &elapsed
);
1266 ts
.tv_sec
-= elapsed
.tv_sec
;
1267 if (ts
.tv_nsec
< (elapsed
.tv_usec
* 1000)) {
1269 ts
.tv_nsec
= 1000000000 + ts
.tv_nsec
- (elapsed
.tv_usec
* 1000);
1272 ts
.tv_nsec
-= (elapsed
.tv_usec
* 1000);
1275 iRet
= pthread_cond_timedwait(&g_condUPnPControlURL
, &g_xUPnP
, &ts
);
1277 // if timeout then return false
1280 pthread_mutex_unlock(&g_xUPnP
);
1284 pthread_mutex_unlock(&g_xUPnP
);
1289 static int WaitUPnPFunction()
1291 struct timeval start
;
1292 // struct timeval end;
1294 // struct timeval elapsed;
1296 gettimeofday(&start
, NULL
);
1298 wait2
= (double)g_iFunctionTimeout
;
1300 WaitControlURLSet(wait2
);
1302 //gettimeofday(&end, NULL);
1303 //GetTimeElapsed(&start, &end, &elapsed);
1304 //fprintf(stderr, "== wait2: (%f) %d.%06d\n",
1305 // wait2/U_TOGRAN, elapsed.tv_sec, elapsed.tv_usec);
1307 return g_fControlURLSet
;
1310 static void SetLocalIP();
1312 static int SendTCPMsg_saddr_parse(
1313 char *msg
, int iLen
,
1314 char *result
, int resultSize
,
1315 struct sockaddr_in
*saHost
);
1317 static void *TCPProc(void *in
)
1320 unsigned char buf
[MAX_SOAPMSGSIZE
];
1325 //TracePrint(ELL_TRACE, "UPnP: Begin TCPProc\n");
1327 // do the subscription
1330 char response
[2000];
1333 sprintf(callback
, "%u.%u.%u.%u:%u",
1334 ((uint8_t*)&g_dwLocalIP
)[0], ((uint8_t*)&g_dwLocalIP
)[1],
1335 ((uint8_t*)&g_dwLocalIP
)[2], ((uint8_t*)&g_dwLocalIP
)[3], g_wEventPort
);
1337 n
= sprintf((char *)buf
,
1338 szEventMsgSubscribeFMT
,
1340 callback
, g_szRouterHostPortEvent
, 1800);
1342 memset(response
, 0, 2000);
1343 n
= SendTCPMsg_saddr_parse(
1346 &g_saddrRouterEvent
);
1350 resp
= NewHTTPResponse_sz((char *)response
, n
+1, TRUE
);
1353 ////TracePrint(ELL_TRACE, "UPnP Subscribe returns %s/%d\n", resp->pszStatus, n);
1357 ////TracePrint(ELL_TRACE, "UPnP Subscribe not enough response (%d) \n[%s]\n",
1360 DeleteHTTPResponse(resp
);
1364 ////TracePrint(ELL_TRACE, "UPnP Subscribe failed (%d)\n", n);
1369 //TracePrint(ELL_TRACE, "UPnP: TCPProc begin loop\n");
1376 struct sockaddr_in recvaddr
;
1377 socklen_t recvaddrlen
;
1379 struct timeval timeout
;
1384 // for after responding to long(?) TCP event
1388 if (g_sTCPCancel
!= -1) close(g_sTCPCancel
);
1389 sMax
= g_sTCPCancel
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
1390 if (sMax
< g_sTCP
) sMax
= g_sTCP
;
1393 FD_SET(g_sTCP
, &readfds
);
1394 FD_SET(g_sTCPCancel
, &readfds
);
1395 iRet
= select(sMax
+1, &readfds
, NULL
, NULL
, NULL
);
1399 //TracePrint(ELL_TRACE, "UPnP Event select failed (%d)\n", errno);
1403 recvaddrlen
= sizeof(recvaddr
);
1404 sEvent
= accept(g_sTCP
, (struct sockaddr
*)&recvaddr
, &recvaddrlen
);
1405 // not likely - (system's descriptor/file table full)
1406 if (sEvent
<= 0) continue;
1408 ////TracePrint(ELL_TRACE, "UPnP receiving event..\n");
1410 // read all we could from this event
1416 FD_SET(sEvent
, &readfds
);
1418 timeout
.tv_usec
= 400000; // long cause we're dealing with input
1419 iRet
= select(sEvent
+1, &readfds
, NULL
, NULL
, &timeout
);
1430 iRet
= recv(sEvent
, buf
+ iBufLen
, MAX_SOAPMSGSIZE
- iBufLen
, 0);
1433 // something is wrong
1446 iTemp
= send(sEvent
, HTTP200OK
, HTTP200OKLEN
, 0);
1447 shutdown(sEvent
, 1);
1452 // now send 200 OK and be done
1455 ////TracePrint(ELL_TRACE, "UPnP event (%d) received (%d)\n", g_fExpectEvent, iBufLen);
1457 // and parse the XML here.
1458 if (iBufLen
< MAX_SOAPMSGSIZE
)
1460 buf
[iBufLen
] = '\0';
1461 // for now do nothing
1465 buf
[MAX_SOAPMSGSIZE
- 1] = '\0';
1470 //TracePrint(ELL_TRACE, "UPnP: TCPProc end\n");
1473 g_fEventEnabled
= FALSE
;
1474 if (g_sTCPCancel
!= -1) close(g_sTCPCancel
);
1479 static void *UDPProc(void *in
)
1481 // char fLoop = 0; // false - don't send copy to self
1482 // int iTTL = SSDP_TTL;
1484 // struct ip_mreq mreq;
1485 // struct sockaddr_in saddr;
1486 unsigned char buf
[65536];
1487 // FILE *log = g_log;
1488 static time_t last_getdevicedesc_t
= 0;
1491 pthread_mutex_lock(&g_xUPnP
);
1492 gettimeofday(&g_tvUPnPInitTime
, NULL
);
1493 pthread_mutex_unlock(&g_xUPnP
);
1497 struct sockaddr_in recvaddr
;
1498 socklen_t recvaddrlen
;
1500 //struct timeval timeout;
1504 if (g_sUDPCancel
< g_sUDP
) sMax
= g_sUDP
;
1505 else sMax
= g_sUDPCancel
;
1508 FD_SET(g_sUDP
, &readfds
);
1509 FD_SET(g_sUDPCancel
, &readfds
);
1510 iRet
= select(sMax
+1, &readfds
, NULL
, NULL
, NULL
);
1516 close(g_sUDPCancel
);
1524 if (!FD_ISSET(g_sUDP
, &readfds
)) continue;
1525 recvaddrlen
= sizeof(recvaddr
);
1526 n
= recvfrom(g_sUDP
, buf
, sizeof(buf
)-1, 0,
1527 (struct sockaddr
*)&recvaddr
, &recvaddrlen
);
1529 if (g_fLogging
& NALOG_ERROR
)
1530 fprintf(g_log
, "recv failed (%d)\n", errno
);
1532 close(g_sUDPCancel
);
1538 if (strncmp((char *)buf
, "HTTP/1.1", 8) == 0) {
1539 PHTTPResponse pResponse
= NewHTTPResponse_sz((char *)buf
, n
+1, TRUE
);
1540 PrintHTTPResponse(pResponse
);
1541 if (DiscoverRouter(pResponse
) == 0)
1543 time_t now
= time(NULL
);
1544 if (!g_fControlURLSet
||
1545 ((now
- last_getdevicedesc_t
) > 5))
1547 GetDeviceDescription();
1549 last_getdevicedesc_t
= now
;
1552 DeleteHTTPResponse(pResponse
);
1554 else if (strncmp((char *)buf
, "NOTIFY * HTTP/1.1", 7) == 0) {
1555 // temporarily use this to fudge - will have the exact same
1556 // parsing, only status/reason set to "*" and "HTTP/1.1".
1557 // TODO: add support for HTTP requests
1558 PHTTPResponse pResponse
= NewHTTPResponse_sz((char *)buf
, n
+1, TRUE
);
1559 if (DiscoverRouter(pResponse
) == 0)
1561 time_t now
= time(NULL
);
1562 if (!g_fControlURLSet
||
1563 ((now
- last_getdevicedesc_t
) > 5))
1565 GetDeviceDescription();
1567 last_getdevicedesc_t
= now
;
1570 DeleteHTTPResponse(pResponse
);
1573 if (g_fLogging
& NALOG_DUMP
)
1574 fprintf(g_log
, "(%ld) Buffer: \n[%s]\n", time(NULL
), buf
);
1583 static void SendUDPMsg(const char *msg
) {
1584 struct sockaddr_in saSendTo
;
1588 bzero(&saSendTo
, sizeof(saSendTo
));
1589 saSendTo
.sin_family
= AF_INET
;
1590 saSendTo
.sin_addr
.s_addr
= inet_addr(SSDP_IP
);
1591 saSendTo
.sin_port
= htons(SSDP_PORT
);
1595 if (g_fLogging
& NALOG_DUMP
)
1596 fprintf(g_log
, "SendUDP: [%s]\n", msg
);
1598 iRet
= sendto(g_sUDP
, msg
, iLen
, 0,
1599 (struct sockaddr
*)&saSendTo
, sizeof(saSendTo
));
1603 if (g_fLogging
& NALOG_ALERT
)
1605 "SendUDPMsg: iRet(%d) != strlen(msg)(%d)! (errno %d)\n",
1609 // strstr, case insensitive, and is limited by len
1610 static char *strcasestr_n(const char *big
, const char *little
, int len
)
1617 if (little
== NULL
) return (char *)big
;
1618 if (big
== NULL
) return NULL
;
1620 //bigLen = strlen(big);
1622 littleLen
= strlen(little
);
1624 if (bigLen
< littleLen
) return NULL
;
1626 end
= bigLen
- littleLen
;
1627 for (i
= 0; i
<= end
; (i
++), (big
++)) {
1628 if (strncasecmp(big
, little
, littleLen
) == 0)
1635 // this is strnstr, only portable
1636 static char *strstr_n(const char *big
, const char *little
, int len
)
1641 (void)len
; // unused
1643 if ((big
== NULL
) || (little
== NULL
)) return NULL
;
1645 iBigLen
= strlen(big
);
1646 iLittleLen
= strlen(little
);
1648 // this part is basically strnstr, except this is portable
1650 if (iBigLen
< iLittleLen
)
1652 if (strncmp(big
, little
, iLittleLen
) == 0)
1659 // returns -1 for "not found"
1660 static int FindContentLength(char *pbuf
, int iLen
)
1662 // non reusable HTTP header parsing code:
1663 // ----------------------------------------------
1667 // find content length header
1668 p
= strcasestr_n(pbuf
, "\r\nContent-Length:", iLen
);
1669 if (p
== NULL
) return -1;
1671 p
+= sizeof("\r\nContent-Length:") - 1; // minus '\0'
1676 // ----------------------------------------------
1679 // returns -1 for "not found"
1680 static int FindBody(char *pbuf
, int iLen
)
1682 // non reusable HTTP header parsing code:
1683 // ----------------------------------------------
1687 // find the empty line
1688 p
= strstr_n(pbuf
, "\r\n\r\n", iLen
);
1689 if (p
== NULL
) return -1;
1691 p
+= sizeof("\r\n\r\n") - 1; // minus '\0'
1694 // ----------------------------------------------
1697 static int SendTCPMsg_saddr_2part(
1698 char *msg
, int iLen
,
1699 char *msg2
, int iLen2
,
1700 char *result
, int resultSize
,
1701 struct sockaddr_in
*saHost
)
1704 struct sockaddr_in saSendTo
;
1713 struct timeval tv_start
;
1714 struct timeval tv_end
;
1715 struct timeval tv_elapsed
;
1717 int iContentLength
= -1;
1718 int iBodyOffset
= -1;
1720 gettimeofday(&tv_start
, NULL
);
1722 if (g_fUPnPEnabled
!= TRUE
) {
1723 //TracePrint(ELL_TRACE, "UPnP not enabled\n");
1724 if (g_fLogging
& NALOG_ERROR
)
1725 fprintf(g_log
, "UPnP not enabled (no UPnP device found yet)\n");
1726 return NA_E_NOT_AVAILABLE
;
1729 s
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
1731 if (g_fLogging
& NALOG_ERROR
)
1732 fprintf(g_log
, "Can't get TCP socket (%d)\n", errno
);
1737 if (setsockopt(s
, IPPROTO_IP
, TCP_NODELAY
, &fND
, sizeof(fND
)) != 0) {
1738 if (g_fLogging
& NALOG_ERROR
)
1739 fprintf(g_log
, "SendTCPMsg/2part: Can't set TCP_NODELAY option!\n");
1740 iRetcode
= NA_E_NET
;
1745 fcntl_flags
= fcntl(s
, F_GETFL
, 0);
1746 fcntl_flags
|= O_NONBLOCK
;
1747 if (fcntl(s
, F_SETFL
, fcntl_flags
) != 0) {
1748 if (g_fLogging
& NALOG_ERROR
)
1749 fprintf(g_log
, "SendTCPMsg/2part: Can't set O_NONBLOCK option!\n");
1750 iRetcode
= NA_E_NET
;
1755 memcpy(&saSendTo
, &g_saddrRouterDesc
, sizeof(saSendTo
));
1757 memcpy(&saSendTo
, saHost
, sizeof(saSendTo
));
1759 iRet
= connect(s
, (struct sockaddr
*) &saSendTo
, sizeof(saSendTo
));
1760 if ((iRet
< 0) && (errno
!= EINPROGRESS
)) {
1761 //TracePrint(ELL_TRACE, "UPnP connect failed\n");
1762 if (g_fLogging
& NALOG_ERROR
)
1763 fprintf(g_log
, "SendTCPMsg/2part: connect failed (%d)\n", errno
);
1764 iRetcode
= NA_E_NET
;
1768 if (g_fLogging
& NALOG_INFO1
)
1770 "- Before Sending TCP Msg1: %d == %lu?\n", iLen
, strlen(msg
));
1771 if (g_fLogging
& NALOG_DUMP
)
1772 fprintf(g_log
, "Sending TCP msg part 1:\n[%s]\n", msg
);
1774 tv
.tv_sec
= g_iFunctionTimeout
/ UPNP_TIMEOUT_GRANULARITY
;
1775 tv
.tv_usec
= (g_iFunctionTimeout
% U_TOGRAN
) * 1000000 / U_TOGRAN
;
1777 FD_SET(s
, &writefds
);
1778 iRet
= select(s
+1, 0, &writefds
, 0, &tv
);
1780 if (g_fLogging
& NALOG_ERROR
)
1781 fprintf(g_log
, "SendTCPMsg/2part: select failed (%d)\n", errno
);
1782 iRetcode
= NA_E_NET
;
1786 if (g_fLogging
& NALOG_ERROR
)
1787 fprintf(g_log
, "SendTCPMsg/2part: select timed out\n");
1788 iRetcode
= NA_E_TIMEOUT
;
1789 gettimeofday(&tv_end
, NULL
);
1790 GetTimeElapsed(&tv_start
, &tv_end
, &tv_elapsed
);
1791 //TracePrint(ELL_TRACE, "UPnP 2part: timeout @1st after %lu.%06lu secs\n",
1792 // tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1796 iRet
= send(s
, msg
, iLen
, 0);
1799 if (g_fLogging
& NALOG_ALERT
)
1800 fprintf(g_log
, "SendTCPMsg/2part: iRet(%d) != strlen(msg)(%d)!\n",
1803 //TracePrint(ELL_TRACE, "UPnP 2part: 1st %d == %d (%d) (%d)?\n", iRet, iLen, strlen(msg), errno);
1805 tv
.tv_sec
= g_iFunctionTimeout
/ UPNP_TIMEOUT_GRANULARITY
;
1806 tv
.tv_usec
= (g_iFunctionTimeout
% U_TOGRAN
) * 1000000 / U_TOGRAN
;
1808 FD_SET(s
, &writefds
);
1809 // calculate how much time elapsed
1810 gettimeofday(&tv_end
, NULL
);
1811 GetTimeElapsed(&tv_start
, &tv_end
, &tv_elapsed
);
1812 if (CompareTime(&tv_elapsed
, &tv
) > 0) {
1814 return NA_E_TIMEOUT
;
1819 // subtract that from timeout accordingly
1820 tv
.tv_sec
-= tv_elapsed
.tv_sec
;
1821 if (tv
.tv_usec
< tv_elapsed
.tv_usec
) {
1823 tv
.tv_usec
= 1000000 + tv
.tv_usec
- tv_elapsed
.tv_usec
;
1826 tv
.tv_usec
= tv
.tv_usec
- tv_elapsed
.tv_usec
;
1828 iRet
= select(s
+1, 0, &writefds
, 0, &tv
);
1830 if (g_fLogging
& NALOG_ERROR
)
1831 fprintf(g_log
, "SendTCPMsg/2part: select2 failed (%d)\n", errno
);
1832 iRetcode
= NA_E_NET
;
1836 if (g_fLogging
& NALOG_ERROR
)
1837 fprintf(g_log
, "SendTCPMsg/2part: select2 timed out\n");
1838 iRetcode
= NA_E_TIMEOUT
;
1839 gettimeofday(&tv_end
, NULL
);
1840 GetTimeElapsed(&tv_start
, &tv_end
, &tv_elapsed
);
1841 //TracePrint(ELL_TRACE, "UPnP 2part: timeout @2nd after %lu.%06lu secs\n",
1842 // tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1846 iRet
= send(s
, msg2
, iLen2
, 0);
1847 if (g_fLogging
& NALOG_INFO1
)
1849 "SendTCPMsg/parse: Before Sending TCP Msg2: %d == %lu?\n",
1850 iLen2
, strlen(msg2
));
1851 if (g_fLogging
& NALOG_DUMP
)
1852 fprintf(g_log
, "Sending TCP msg part 2:\n[%s]\n", msg2
);
1854 //TracePrint(ELL_TRACE, "UPnP 2part: 2nd %d == %d (%d) (%d)?\n", iRet, iLen2, strlen(msg2), errno);
1858 if (g_fLogging
& NALOG_ALERT
)
1859 fprintf(g_log
, "SendTCPMsg/2part: iRet(%d) != strlen(msg2)(%d)!\n",
1862 if (result
== NULL
) { // if caller just want to send/display msgs
1863 if (g_fLogging
& NALOG_DUMP
)
1864 fprintf(g_log
, "TCP Buffer: [");
1867 if (g_fLogging
& NALOG_INFO1
)
1868 fprintf(g_log
, "start recv @%lu\n", time(NULL
));
1871 iContentLength
= -1;
1875 struct timeval timeout
;
1879 FD_SET(s
, &readfds
);
1881 // In testing, the Linksys Wireless-G Broadband Router "WRT54GS" takes
1882 // up to four seconds to respond, and even then only a partial response,
1883 // with the remainder coming in a second TCP segment half a second later.
1884 // Accordingly, we wait up to five seconds for the initial data, and then after that
1885 // wait one second after subsequent TCP segments, in care more data is still coming.
1886 timeout
.tv_sec
= iBufLen
? 1 : 5;
1887 timeout
.tv_usec
= 0;
1888 iRet
= select(s
+1, &readfds
, NULL
, NULL
, &timeout
);
1891 //TracePrint(ELL_TRACE, "UPnP 2part: select timeout? (%d, %d)\n",
1896 //gettimeofday(&tv_end, NULL);
1897 //GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed);
1898 //fprintf(stderr, "2 == loop: %d.%06d\n", tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1900 // if only sending messages
1901 if (result
== NULL
) {
1903 i
= recv(s
, t
, 1000-1, 0); // leave room for '\0' for dump
1905 if (g_fLogging
& NALOG_DUMP
) {
1907 fprintf(g_log
, "%s", t
);
1912 // EO result buf: discard extra bytes
1913 if (resultSize
<= iBufLen
) {
1915 i
= recv(s
, &t
, 1000, 0);
1917 // Note that there's no dump here - prevents DoS attack from
1918 // flooding the logs/diskspace
1922 i
= recv(s
, result
+ iBufLen
, resultSize
- iBufLen
, 0);
1924 //TracePrint(ELL_TRACE, "UPnP 2part: recv done %d (%d, %d)\n",
1925 // iBufLen, i, errno);
1931 // parse and see if we can find content-length to quit early
1932 iContentLength
= FindContentLength(result
, iBufLen
);
1934 // now if we're still in header, see if we can find body
1935 iBodyOffset
= FindBody(result
, iBufLen
);
1937 // now check if we can leave early. conditions are:
1938 // past headers, and we've already recv'ed content-length of body
1939 if ((iBodyOffset
>= 0) &&
1940 (iContentLength
>= 0) &&
1941 ((iBufLen
- iBodyOffset
) >= iContentLength
))
1943 //TracePrint(ELL_TRACE, "UPnP 2part: read all specified %d (%d, %d) (%d, %d)\n",
1944 // iBufLen, i, errno, iBodyOffset, iContentLength);
1949 //fprintf(stderr, "2 -- \n");
1951 if (g_fLogging
& NALOG_INFO1
)
1952 fprintf(g_log
, "SendTCPMsg_saddr_2part done recv %d @ %lu\n", iBufLen
, time(NULL
));
1954 if (result
== NULL
) { // if caller just want to send/display msgs
1955 if (g_fLogging
& NALOG_DUMP
)
1956 fprintf(g_log
, "]\n");
1967 static int SendTCPMsg_saddr_parse(
1968 char *msg
, int iLen
,
1969 char *result
, int resultSize
,
1970 struct sockaddr_in
*saHost
)
1973 struct sockaddr_in saSendTo
;
1980 struct timeval tv_start
;
1981 // struct timeval tv_end;
1982 // struct timeval tv_elapsed;
1984 // HTTP parsing vars
1992 select(0, NULL
, NULL
, NULL
, &tv
);
1994 pthread_mutex_lock(&g_xUPnPMsg
);
1996 gettimeofday(&tv_start
, NULL
);
1998 if (g_fUPnPEnabled
!= TRUE
) {
1999 //TracePrint(ELL_TRACE, "UPnP not enabled\n");
2000 if (g_fLogging
& NALOG_ERROR
)
2001 fprintf(g_log
, "UPnP not enabled (no UPnP device found yet)\n");
2002 pthread_mutex_unlock(&g_xUPnPMsg
);
2003 return NA_E_NOT_AVAILABLE
;
2006 s
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
2008 if (g_fLogging
& NALOG_ERROR
)
2009 fprintf(g_log
, "Can't get TCP socket (%d)\n", errno
);
2010 pthread_mutex_unlock(&g_xUPnPMsg
);
2015 fcntl_flags
= fcntl(s
, F_GETFL
, 0);
2016 fcntl_flags
|= O_NONBLOCK
;
2017 if (fcntl(s
, F_SETFL
, fcntl_flags
) != 0) {
2018 if (g_fLogging
& NALOG_ERROR
)
2019 fprintf(g_log
, "SendTCPMsg/parse: Can't set O_NONBLOCK option!\n");
2021 pthread_mutex_unlock(&g_xUPnPMsg
);
2026 memcpy(&saSendTo
, &g_saddrRouterDesc
, sizeof(saSendTo
));
2028 memcpy(&saSendTo
, saHost
, sizeof(saSendTo
));
2030 iRet
= connect(s
, (struct sockaddr
*) &saSendTo
, sizeof(saSendTo
));
2031 if ((iRet
< 0) && (errno
!= EINPROGRESS
)) {
2032 //TracePrint(ELL_TRACE, "UPnP connect failed\n");
2033 if (g_fLogging
& NALOG_ERROR
)
2034 fprintf(g_log
, "SendTCPMsg/parse: connect failed (%d)\n", errno
);
2036 pthread_mutex_unlock(&g_xUPnPMsg
);
2040 if (g_fLogging
& NALOG_INFO1
)
2041 fprintf(g_log
, "SendTCPMsg_saddr_parse: Before Sending TCP Msg: %d == %lu?\n",
2043 if (g_fLogging
& NALOG_DUMP
)
2044 fprintf(g_log
,"Sending TCP msg:\n[%s]\n", msg
);
2046 tv
.tv_sec
= g_iFunctionTimeout
/ UPNP_TIMEOUT_GRANULARITY
;
2047 tv
.tv_usec
= (g_iFunctionTimeout
% U_TOGRAN
) * 1000000 / U_TOGRAN
;
2049 FD_SET(s
, &writefds
);
2050 iRet
= select(s
+1, 0, &writefds
, 0, &tv
);
2052 if (g_fLogging
& NALOG_ERROR
)
2053 fprintf(g_log
, "SendTCPMsg/parse: select failed (%d)\n", errno
);
2055 pthread_mutex_unlock(&g_xUPnPMsg
);
2059 if (g_fLogging
& NALOG_ERROR
)
2060 fprintf(g_log
, "SendTCPMsg/parse: select timed out\n");
2062 pthread_mutex_unlock(&g_xUPnPMsg
);
2063 return NA_E_TIMEOUT
;
2066 iRet
= send(s
, msg
, iLen
, 0);
2070 if (g_fLogging
& NALOG_ALERT
)
2071 fprintf(g_log
, "SendTCPMsg: iRet (%d) != strlen(msg) (%d)!\n",
2074 if (result
== NULL
) { // if caller just want to send/display msgs
2075 if (g_fLogging
& NALOG_DUMP
)
2076 fprintf(g_log
, "TCP Buffer: [");
2079 if (g_fLogging
& NALOG_INFO1
)
2080 fprintf(g_log
, "start recv @%lu\n", time(NULL
));
2084 iContentLength
= -1;
2088 struct timeval timeout
;
2092 FD_SET(s
, &readfds
);
2094 // In testing, the Linksys Wireless-G Broadband Router "WRT54GS" takes
2095 // up to four seconds to respond, and even then only a partial response,
2096 // with the remainder coming in a second TCP segment half a second later.
2097 // Accordingly, we wait up to five seconds for the initial data, and then after that
2098 // wait one second after subsequent TCP segments, in care more data is still coming.
2099 timeout
.tv_sec
= iBufLen
? 1 : 5;
2100 timeout
.tv_usec
= 0;
2101 iRet
= select(s
+1, &readfds
, NULL
, NULL
, &timeout
);
2103 //fprintf(stderr, "**********: select failed (%d/%d)\n", iRet, errno);
2107 //gettimeofday(&tv_end, NULL);
2108 //GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed);
2109 //fprintf(stderr, "p == loop: %d.%06d\n", tv_elapsed.tv_sec, tv_elapsed.tv_usec);
2111 // if only sending messages
2112 if (result
== NULL
) {
2114 i
= recv(s
, t
, 1000-1, 0); // leave room for '\0' for dump
2116 if (g_fLogging
& NALOG_DUMP
) {
2118 fprintf(g_log
, "%s", t
);
2123 // EO result buf: discard extra bytes
2124 if (resultSize
<= iBufLen
) {
2126 i
= recv(s
, &t
, 1000, 0);
2127 if (g_fLogging
& NALOG_INFO1
)
2128 fprintf(g_log
, "SendTCPMsg_saddr_parse discarding %d bytes\n", i
);
2130 // Note that there's no dump here - prevents DoS attack from
2131 // flooding the logs/diskspace
2135 i
= recv(s
, result
+ iBufLen
, resultSize
- iBufLen
, 0);
2136 if (g_fLogging
& NALOG_INFO1
)
2137 fprintf(g_log
, "SendTCPMsg_saddr_parse read %d bytes (%d/%d)\n", i
, iBufLen
, resultSize
);
2143 if (EAGAIN
== errno
) continue;
2149 // parse and see if we can find content-length to quit early
2150 iContentLength
= FindContentLength(result
, iBufLen
);
2152 // now if we're still in header, see if we can find body
2153 iBodyOffset
= FindBody(result
, iBufLen
);
2157 //fprintf(stderr, "p -- \n");
2159 if (g_fLogging
& NALOG_INFO1
)
2160 fprintf(g_log
, "SendTCPMsg_saddr_parse done recv %d @ %lu\n", iBufLen
, time(NULL
));
2162 if (result
== NULL
) { // if caller just want to send/display msgs
2163 if (g_fLogging
& NALOG_DUMP
)
2164 fprintf(g_log
, "]\n");
2168 pthread_mutex_unlock(&g_xUPnPMsg
);
2174 // szSOAPMsgControlAHeaderFMT - 4 args (ctrl_url, host/port, action, length)
2175 // szSOAPMsgControlABodyFMT - 2 args (action, args string)
2176 // szSOAPMsgControlAArgumentFMT - 2 args (name/value)
2177 static PHTTPResponse
SendSOAPMsgControlAction(
2183 //char outBuffer[65536];
2184 //char outBufferBody[65536];
2185 //char outBufferArgs[65536];
2186 char *outBuffer
= NULL
;
2187 char *outBufferBody
= NULL
;
2188 char *outBufferArgs
= NULL
;
2189 char *inBuffer
= NULL
;
2197 PHTTPResponse pResponse
= NULL
;
2200 if (!WaitUPnPFunction())
2203 if ((outBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2204 if (g_fLogging
& NALOG_ERROR
)
2205 fprintf(g_log
, "can't malloc for outBuffer\n");
2208 if ((outBufferBody
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2209 if (g_fLogging
& NALOG_ERROR
)
2210 fprintf(g_log
, "can't malloc for outBufferBody\n");
2213 if ((outBufferArgs
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2214 if (g_fLogging
& NALOG_ERROR
)
2215 fprintf(g_log
, "can't malloc for outBufferArgs\n");
2218 if ((inBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2219 if (g_fLogging
& NALOG_ERROR
)
2220 fprintf(g_log
, "can't malloc for inBuffer\n");
2226 for (i
=0; i
<argc
; i
++) {
2228 if (args
[i
].pszType
== NULL
) {
2229 n
= sprintf(outBufferArgs
+ iArgsLen
,
2230 szSOAPMsgControlAArgumentFMT
,
2231 args
[i
].pszName
, args
[i
].pszValue
);
2234 n
= sprintf(outBufferArgs
+ iArgsLen
,
2235 szSOAPMsgControlAArgumentFMT_t
,
2236 args
[i
].pszName
, args
[i
].pszValue
, args
[i
].pszType
);
2240 outBufferArgs
[iArgsLen
] = '\0';
2242 iBodyLen
= sprintf(outBufferBody
, szSOAPMsgControlABodyFMT
,
2243 action
, outBufferArgs
);
2245 iHeaderLen
= sprintf(outBuffer
, szSOAPMsgControlAHeaderFMT
,
2246 g_szControlURL
, g_szRouterHostPortSOAP
, action
, iBodyLen
);
2249 DumpHex(outBuffer
, iHeaderLen
+1);
2250 DumpHex(outBufferBody
, iBodyLen
+1);
2251 iResultLen
= SendTCPMsg_saddr_2part(
2252 outBuffer
, iHeaderLen
,
2253 outBufferBody
, iBodyLen
,
2254 inBuffer
, MAX_SOAPMSGSIZE
,
2255 &g_saddrRouterSOAP
);
2258 strcpy(outBuffer
+ iHeaderLen
, outBufferBody
);
2259 iLen
= iHeaderLen
+ iBodyLen
;
2261 DumpHex(outBuffer
, iLen
+1);
2263 //strcat(outBuffer, CRLF "0" CRLF CRLF);
2266 iResultLen
= SendTCPMsg_saddr_parse(
2268 inBuffer
, MAX_SOAPMSGSIZE
,
2269 &g_saddrRouterSOAP
);
2272 if (g_fLogging
& NALOG_INFO1
)
2273 fprintf(g_log
, "SendSOAPMsgControlAction iResultLen %d\n", iResultLen
);
2274 if (iResultLen
> 0) {
2275 if (iResultLen
> MAX_SOAPMSGSIZE
) {
2276 if (g_fLogging
& NALOG_ALERT
)
2277 fprintf(g_log
, "result truncated..\n");
2278 iResultLen
= MAX_SOAPMSGSIZE
;
2280 pResponse
= NewHTTPResponse_sz(inBuffer
, iResultLen
, FALSE
);
2281 if (pResponse
!= NULL
) {
2282 PrintHTTPResponse(pResponse
);
2283 //DeleteHTTPResponse(pResponse);
2284 // - return response to caller
2288 if (g_fLogging
& NALOG_ERROR
)
2289 fprintf(g_log
, "No TCP Response\n");
2290 //TracePrint(ELL_TRACE, "UPnP SendSOAPMsg got no TCP response (%d)\n",
2295 if (outBuffer
!= NULL
) free(outBuffer
);
2296 if (outBufferBody
!= NULL
) free(outBufferBody
);
2297 if (outBufferArgs
!= NULL
) free(outBufferArgs
);
2298 if (inBuffer
!= NULL
) free(inBuffer
);
2303 static int FindURLBase(char *pbuf
, int iLen
, char *szURLBase
)
2305 // non reusable XML parsing code:
2306 // ----------------------------------------------
2310 // now skip after end of this tag, then skip until controlURL tag
2311 p
= strstr_n(pbuf
, "<URLBase>", iLen
);
2312 if (p
== NULL
) return -1;
2314 // skip to the actual stuff
2315 p
+= sizeof("<URLBase>") - 1; // minus '\0'
2317 // skip white spaces (just in case)
2321 // copy into szURLBase
2322 while ((*p
!= '\0') && (*p
!= '<') && !isspace(*p
)) {
2323 if (i
++ > 1000) break;
2331 // ----------------------------------------------
2335 static int FindDescInfo(
2338 const char *szParentName
,
2347 // find the device within pbuf
2359 // now skip after end of this tag, then skip until manufacturer tag
2360 iSearchLen
= sprintf(szSearch
, "<%s>", szName
);
2361 p
= strstr_n(pbuf
, szSearch
, iLen
);
2362 if (p
== NULL
) return -1;
2365 // skip white spaces (just in case)
2369 // copy into szValue
2370 while ((*p
!= '\0') && (*p
!= '<')) {
2371 if (i
++ > 1000) break;
2381 static int FindIGDInfo(char *pbuf
, int iLen
, const char *szName
, char *szValue
)
2383 return FindDescInfo(
2385 "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
2389 static int FindManufacturer(char *pbuf
, int iLen
, char *szManuf
)
2391 return FindIGDInfo(pbuf
, iLen
, "manufacturer", szManuf
);
2394 static int FindFriendlyName(char *pbuf
, int iLen
, char *szValue
)
2396 return FindIGDInfo(pbuf
, iLen
, "friendlyName", szValue
);
2399 static int FindModelName(char *pbuf
, int iLen
, char *szValue
)
2401 return FindIGDInfo(pbuf
, iLen
, "modelName", szValue
);
2404 static int FindModelDescription(char *pbuf
, int iLen
, char *szValue
)
2406 return FindIGDInfo(pbuf
, iLen
, "modelDescription", szValue
);
2409 static int FindWANIPInfo(char *pbuf
, int iLen
, const char *szName
, char *szValue
)
2411 return FindDescInfo(
2413 "urn:schemas-upnp-org:service:WANIPConnection:1",
2417 static int FindControlURL(char *pbuf
, int iLen
, char *szControlURL
)
2419 return FindWANIPInfo(pbuf
, iLen
, "controlURL", szControlURL
);
2422 static int FindEventURL(char *pbuf
, int iLen
, char *szEventURL
)
2424 return FindWANIPInfo(pbuf
, iLen
, "eventSubURL", szEventURL
);
2427 static int FindRouterInfo(char *inBuffer
, int iLen
)
2429 if (FindManufacturer(inBuffer
, iLen
, g_szManufacturer
) != 0)
2430 g_szManufacturer
[0] = '\0';
2432 if (FindFriendlyName(inBuffer
, iLen
, g_szFriendlyName
) != 0)
2433 g_szFriendlyName
[0] = '\0';
2435 if (FindModelName(inBuffer
, iLen
, g_szModelName
) != 0)
2436 g_szModelName
[0] = '\0';
2438 if (FindModelDescription(inBuffer
, iLen
, g_szModelDescription
) != 0)
2439 g_szModelDescription
[0] = '\0';
2441 //TracePrint(ELL_TRACE,
2442 // "UPnP Router Info:\n"
2443 // " - manufacturer [%s]\n"
2444 // " - friendly name [%s]\n"
2445 // " - model name [%s]\n"
2446 // " - model desc [%s]\n",
2447 // g_szManufacturer, g_szFriendlyName, g_szModelName, g_szModelDescription);
2452 static void ParseURL(
2453 const char *szBuf
, char *pszHostPort
,
2454 struct sockaddr_in
*psaddr
, char *pszPath
)
2459 unsigned short port
;
2464 if (0 == strncmp(p
, "http://", 7))
2480 // find the port separetor
2486 // HTTP's by default port 80, so don't have it in the "Host:" header
2487 if (80 == port
) *q
= '\0';
2490 if (pszHostPort
) strcpy(pszHostPort
, p
);
2492 if (NULL
!= q
) *q
= '\0';
2494 if (NULL
!= psaddr
) {
2495 psaddr
->sin_family
= AF_INET
;
2496 psaddr
->sin_addr
.s_addr
= inet_addr(p
);
2497 psaddr
->sin_port
= htons(port
);
2500 //TracePrint(ELL_TRACE, "ParseURL [%s] -> [%s][%s] %lu.%lu.%lu.%lu:%u\n",
2502 pszHostPort
?pszHostPort
:"",
2504 ((uint8_t*)&psaddr
->sin_addr
.s_addr
)[0], ((uint8_t*)&psaddr
->sin_addr
.s_addr
)[1],
2505 ((uint8_t*)&psaddr
->sin_addr
.s_addr
)[2], ((uint8_t*)&psaddr
->sin_addr
.s_addr
)[3],
2510 static void GetDeviceDescription(void)
2512 char *outBuffer
= NULL
;
2513 char *inBuffer
= NULL
;
2516 char szURLBase
[1024];
2517 char szControlURL
[1024];
2518 char szEventURL
[1024];
2520 if (!g_fUPnPEnabled
) {
2521 if (g_fLogging
& NALOG_ERROR
)
2522 fprintf(g_log
, "GetDeviceDescription: upnp not enabled\n");
2526 if ((outBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2527 if (g_fLogging
& NALOG_ERROR
)
2528 fprintf(g_log
, "can't malloc for outBuffer\n");
2531 if ((inBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2532 if (g_fLogging
& NALOG_ERROR
)
2533 fprintf(g_log
, "can't malloc for inBuffer\n");
2537 iBufLen
= sprintf(outBuffer
, szSSDPMsgDescribeDeviceFMT
, g_szNATDevDescURL
,
2538 g_szRouterHostPortDesc
);
2540 if (g_fLogging
& NALOG_INFO1
)
2541 fprintf(g_log
, "Describe Device: [%s]\n", outBuffer
);
2542 iLen
= SendTCPMsg_saddr_parse(outBuffer
, iBufLen
, inBuffer
, MAX_SOAPMSGSIZE
,
2543 &g_saddrRouterDesc
);
2545 g_fControlURLSet
= FALSE
;
2547 if (FindControlURL(inBuffer
, iLen
, szControlURL
) != 0) {
2548 if (g_fLogging
& NALOG_ERROR
)
2549 fprintf(g_log
, "GetDeviceDesc: can't find control URL\n");
2553 // start modifying global
2554 pthread_mutex_lock(&g_xUPnP
);
2557 // now see if there's the URLBase
2558 if (FindURLBase(inBuffer
, iLen
, szURLBase
) != 0) {
2559 // not there? try default numbers from device description
2560 memcpy(&g_saddrRouterBase
, &g_saddrRouterDesc
,
2561 sizeof(g_saddrRouterBase
));
2562 strcpy(g_szRouterHostPortBase
, g_szRouterHostPortDesc
);
2566 g_szRouterHostPortBase
, &g_saddrRouterBase
, NULL
);
2568 if ((strlen(g_szRouterHostPortBase
) == 0) ||
2569 (g_saddrRouterBase
.sin_addr
.s_addr
== INADDR_NONE
)) {
2570 memcpy(&g_saddrRouterBase
, &g_saddrRouterDesc
,
2571 sizeof(g_saddrRouterBase
));
2572 strcpy(g_szRouterHostPortBase
, g_szRouterHostPortDesc
);
2577 ParseURL(szControlURL
,
2578 g_szRouterHostPortSOAP
, &g_saddrRouterSOAP
, g_szControlURL
);
2579 if ((strlen(g_szRouterHostPortSOAP
) == 0) ||
2580 (g_saddrRouterSOAP
.sin_addr
.s_addr
== INADDR_NONE
)) {
2581 memcpy(&g_saddrRouterSOAP
, &g_saddrRouterBase
,
2582 sizeof(g_saddrRouterSOAP
));
2583 strcpy(g_szRouterHostPortSOAP
, g_szRouterHostPortBase
);
2587 ////TracePrint(ELL_TRACE, "UPnP Control URL set to[%s][%s]...\n",
2588 // g_szRouterHostPortSOAP, g_szControlURL);
2590 g_fControlURLSet
= TRUE
;
2591 gettimeofday(&g_tvLastUpdateTime
, NULL
);
2592 pthread_cond_broadcast(&g_condUPnPControlURL
);
2594 if (g_fLogging
& NALOG_INFO1
)
2595 fprintf(g_log
, "Got Device Description\n");
2598 FindRouterInfo(inBuffer
, iLen
);
2600 if (FindEventURL(inBuffer
, iLen
, szEventURL
) != 0) {
2601 szEventURL
[0] = '\0';
2604 ParseURL(szEventURL
,
2605 g_szRouterHostPortEvent
, &g_saddrRouterEvent
, g_szEventURL
);
2606 if ((strlen(g_szRouterHostPortEvent
) == 0) ||
2607 (g_saddrRouterEvent
.sin_addr
.s_addr
== INADDR_NONE
)) {
2608 memcpy(&g_saddrRouterEvent
, &g_saddrRouterBase
,
2609 sizeof(g_saddrRouterEvent
));
2610 strcpy(g_szRouterHostPortEvent
, g_szRouterHostPortBase
);
2617 if (outBuffer
!= NULL
) free(outBuffer
);
2618 if (inBuffer
!= NULL
) free(inBuffer
);
2620 pthread_mutex_unlock(&g_xUPnP
);
2624 static void GetIPByName(char *hostname
, unsigned long *ip_ret
)
2628 ip
= inet_addr(hostname
);
2629 if (ip
== INADDR_NONE
) {
2630 struct hostent
*pHEnt
;
2631 pHEnt
= gethostbyname(hostname
);
2632 if (pHEnt
== NULL
) {
2633 if (g_fLogging
& NALOG_ALERT
)
2634 fprintf(g_log
, "Can't translate [%s] to IP...\n", hostname
);
2635 g_dwLocalIP
= INADDR_ANY
;
2638 ip
= *(unsigned long *)(pHEnt
->h_addr
);
2639 if (g_fLogging
& NALOG_INFO1
)
2640 fprintf(g_log
, "hostname [%s] to ip: %u.%u.%u.%u\n", hostname
,
2641 ((uint8_t*)&ip
)[0], ((uint8_t*)&ip
)[1], ((uint8_t*)&ip
)[2], ((uint8_t*)&ip
)[3]);
2646 static void SetLocalIP()
2648 PIPINFO pIPInfo
= NULL
;
2649 int count
= GetIPInfo(&pIPInfo
);
2650 if (NULL
!= pIPInfo
)
2652 // choose first non IPV6 address
2653 // iterate through array and set port information
2655 unsigned long dwFirst
= 0;
2656 for(i
= 0; i
< count
; i
++)
2658 if (!(pIPInfo
[i
].iFlags
& ISIPV6
) &&
2659 (strncmp(pIPInfo
[i
].szIfName
, "ppp", 3) != 0))
2661 unsigned long dwTemp
;
2663 memcpy(&dwTemp
, pIPInfo
[i
].abIP
, sizeof(unsigned long));
2665 if (0 != GetNATIPNetmask(dwTemp
)) {
2666 g_dwLocalIP
= dwTemp
;
2675 g_dwLocalIP
= dwFirst
;
2676 FreeIPInfo(pIPInfo
);
2681 static int FindTagContent(const char *text
, const char *tagname
, char *buf
)
2685 p
= strstr(text
, tagname
);
2687 if (g_fLogging
& NALOG_INFO0
)
2688 fprintf(g_log
, "FindTagContent: can't find %s\n", tagname
);
2689 return NA_E_PARSE_ERROR
;
2692 if (sscanf(p
, "%*[^>]> %[^ <] <", buf
) < 1) {
2693 if (g_fLogging
& NALOG_INFO0
)
2694 fprintf(g_log
, "FindTagContent: Can't parse tag %s\n", tagname
);
2695 return NA_E_PARSE_ERROR
;
2698 return NA_E_SUCCESS
;
2701 mStatus
LNT_UnmapPort(mDNSIPPort PubPort
, mDNSBool tcp
)
2705 //char szRemoteHost[1024];
2706 //unsigned long dwIP;
2707 Property propArgs
[3];
2709 int protocol
= tcp
? IPPROTO_TCP
: IPPROTO_UDP
;
2710 sprintf(szEPort
, "%u", mDNSVal16(PubPort
));
2712 bzero(propArgs
, sizeof(propArgs
));
2713 propArgs
[0].pszName
= "NewRemoteHost";
2714 propArgs
[0].pszValue
= "";
2715 propArgs
[0].pszType
= "string";
2716 propArgs
[1].pszName
= "NewExternalPort";
2717 propArgs
[1].pszValue
= szEPort
;
2718 propArgs
[1].pszType
= "ui2";
2719 propArgs
[2].pszName
= "NewProtocol";
2720 if (protocol
== IPPROTO_TCP
) {
2721 propArgs
[2].pszValue
= "TCP";
2723 else if (protocol
== IPPROTO_UDP
) {
2724 propArgs
[2].pszValue
= "UDP";
2729 propArgs
[2].pszType
= "string";
2731 resp
= SendSOAPMsgControlAction(
2732 "DeletePortMapping", 3, propArgs
, FALSE
);
2734 return mStatus_NATTraversal
;
2737 if (strcmp(resp
->pszStatus
, "200") != 0) {
2738 DeleteHTTPResponse(resp
);
2739 return mStatus_NATTraversal
;
2742 DeleteHTTPResponse(resp
);
2743 return mStatus_NoError
;
2747 static int GetMappingUnused(unsigned short eport
, int protocol
);
2749 extern mStatus
LNT_MapPort(mDNSIPPort priv
, mDNSIPPort pub
, mDNSBool tcp
)
2756 Property propArgs
[8];
2758 int protocol
= tcp
? IPPROTO_TCP
: IPPROTO_UDP
;
2760 if (NA_E_EXISTS
== GetMappingUnused(mDNSVal16(pub
), protocol
))
2761 return mStatus_AlreadyRegistered
;
2763 //DeletePortMapping(eport, protocol);
2765 sprintf(szEPort
, "%u", mDNSVal16(pub
));
2766 sprintf(szIPort
, "%u", mDNSVal16(priv
));
2769 sprintf(szLocalIP
, "%u.%u.%u.%u",
2770 ((uint8_t*)&dwIP
)[0], ((uint8_t*)&dwIP
)[1], ((uint8_t*)&dwIP
)[2], ((uint8_t*)&dwIP
)[3]);
2772 bzero(propArgs
, sizeof(propArgs
));
2773 propArgs
[0].pszName
= "NewRemoteHost";
2774 propArgs
[0].pszValue
= "";
2775 propArgs
[0].pszType
= "string";
2776 propArgs
[1].pszName
= "NewExternalPort";
2777 propArgs
[1].pszValue
= szEPort
;
2778 propArgs
[1].pszType
= "ui2";
2779 propArgs
[2].pszName
= "NewProtocol";
2780 if (protocol
== IPPROTO_TCP
) {
2781 propArgs
[2].pszValue
= "TCP";
2783 else if (protocol
== IPPROTO_UDP
) {
2784 propArgs
[2].pszValue
= "UDP";
2787 return mStatus_BadParamErr
;
2789 propArgs
[2].pszType
= "string";
2790 propArgs
[3].pszName
= "NewInternalPort";
2791 propArgs
[3].pszValue
= szIPort
;
2792 propArgs
[3].pszType
= "ui2";
2793 propArgs
[4].pszName
= "NewInternalClient";
2794 propArgs
[4].pszValue
= szLocalIP
;
2795 propArgs
[4].pszType
= "string";
2796 propArgs
[5].pszName
= "NewEnabled";
2797 propArgs
[5].pszValue
= "1";
2798 propArgs
[5].pszType
= "boolean";
2799 propArgs
[6].pszName
= "NewPortMappingDescription";
2800 sprintf(descr
, "iC%u", mDNSVal16(pub
));
2801 //propArgs[6].pszValue = "V";
2802 propArgs
[6].pszValue
= descr
;
2803 propArgs
[6].pszType
= "string";
2804 propArgs
[7].pszName
= "NewLeaseDuration";
2805 propArgs
[7].pszValue
= "0";
2806 propArgs
[7].pszType
= "ui4";
2808 if (g_fLogging
& NALOG_INFO1
)
2809 fprintf(g_log
, "Sending AddPortMapping priv %u pub %u\n", mDNSVal16(priv
), mDNSVal16(pub
));
2811 resp
= SendSOAPMsgControlAction(
2812 "AddPortMapping", 8, propArgs
, FALSE
);
2814 if (g_fLogging
& NALOG_INFO1
)
2815 fprintf(g_log
, "AddPortMapping resp %p\n", resp
);
2818 return mStatus_NATTraversal
;
2821 if (strcmp(resp
->pszStatus
, "200") != 0) {
2822 DeleteHTTPResponse(resp
);
2823 return mStatus_NATTraversal
;
2826 DeleteHTTPResponse(resp
);
2827 return mStatus_NoError
;
2830 static int GetMappingUnused(unsigned short eport
, int protocol
)
2834 Property propArgs
[3];
2836 unsigned long ip
= 0;
2838 sprintf( szPort
, "%u", eport
);
2840 bzero(&propArgs
, sizeof(propArgs
));
2841 propArgs
[0].pszName
= "NewRemoteHost";
2842 propArgs
[0].pszValue
= "";
2843 propArgs
[0].pszType
= "string";
2844 propArgs
[1].pszName
= "NewExternalPort";
2845 propArgs
[1].pszValue
= szPort
;
2846 propArgs
[1].pszType
= "ui2";
2847 propArgs
[2].pszName
= "NewProtocol";
2848 if (protocol
== IPPROTO_TCP
) {
2849 propArgs
[2].pszValue
= "TCP";
2851 else if (protocol
== IPPROTO_UDP
) {
2852 propArgs
[2].pszValue
= "UDP";
2855 return NA_E_INVALID_PARAMETER
;
2857 propArgs
[2].pszType
= "string";
2859 resp
= SendSOAPMsgControlAction(
2860 "GetSpecificPortMappingEntry", 3, propArgs
, FALSE
);
2862 if ((strcmp(resp
->pszStatus
, "200") == 0) &&
2863 (FindTagContent(resp
->pszBody
, "NewInternalClient", buf
) == 0))
2865 GetIPByName(buf
, &ip
);
2866 if (ip
== g_dwLocalIP
) {
2867 // (perhaps we let it go?)
2868 DeleteHTTPResponse(resp
);
2869 return NA_E_SUCCESS
;
2872 DeleteHTTPResponse(resp
);
2876 DeleteHTTPResponse(resp
);
2879 return NA_E_SUCCESS
;
2882 mStatus
LNT_GetPublicIP(mDNSOpaque32
*IpPtr
)
2886 static struct timeval tvLastGoodIP
= {0,0};
2887 static unsigned long dwLastGoodIP
;
2889 unsigned long *ip
= (unsigned long *)IpPtr
;
2890 if (ip
== NULL
) return mStatus_BadParamErr
;
2892 gettimeofday(&tv
, NULL
);
2893 GetTimeElapsed(&tvLastGoodIP
, &tv
, &tv
);
2896 return dwLastGoodIP
;
2899 resp
= SendSOAPMsgControlAction(
2900 "GetExternalIPAddress", 0, NULL
, FALSE
);
2903 return mStatus_NATTraversal
;
2905 if (FindTagContent(resp
->pszBody
, "NewExternalIPAddress", buf
) == 0) {
2906 if (g_fLogging
& NALOG_INFO1
)
2907 fprintf(g_log
, "Mapped remote host = %s\n", buf
);
2908 *ip
= inet_addr(buf
);
2909 DeleteHTTPResponse(resp
);
2911 gettimeofday(&tvLastGoodIP
, NULL
);
2914 return mStatus_NoError
;
2917 DeleteHTTPResponse(resp
);
2918 return mStatus_NATTraversal
;
2921 static void SendDiscoveryMsg()
2923 // do it twice to avoid lost packet
2924 //SendUDPMsg(szSSDPMsgDiscoverNAT);
2925 SendUDPMsg(szSSDPMsgDiscoverRoot
);
2926 SendUDPMsg(szSSDPMsgDiscoverIGD
);
2927 SendUDPMsg(szSSDPMsgDiscoverNAT
);
2930 // Set up threads for upnp responses, etc.
2931 int LegacyNATInit(void)
2933 //pthread_t UDPthread;
2934 pthread_attr_t attr
;
2936 //struct timeval tv;
2937 LogOperation("LegacyNATInit");
2939 static int fFirstInitLocks
= TRUE
;
2943 //g_fLogging = ~0; // Turns ALL logging on
2950 if (fFirstInitLocks
)
2953 if (pthread_mutex_init(&g_xUPnP
, NULL
)) {
2954 if (g_fLogging
& NALOG_ERROR
)
2955 fprintf(log
, "UpnpInit - mutex init failed\n");
2956 return NA_E_INTERNAL_ERROR
;
2958 if (pthread_cond_init(&g_condUPnP
, NULL
)) {
2959 pthread_mutex_destroy(&g_xUPnP
);
2960 if (g_fLogging
& NALOG_ERROR
)
2961 fprintf(log
, "UpnpInit - cond init failed\n");
2962 return NA_E_INTERNAL_ERROR
;
2964 if (pthread_cond_init(&g_condUPnPControlURL
, NULL
)) {
2965 pthread_mutex_destroy(&g_xUPnP
);
2966 pthread_cond_destroy(&g_condUPnP
);
2967 if (g_fLogging
& NALOG_ERROR
)
2968 fprintf(log
, "UpnpInit - cond init failed\n");
2969 return NA_E_INTERNAL_ERROR
;
2971 if (pthread_mutex_init(&g_xUPnPMsg
, NULL
)) {
2972 pthread_mutex_destroy(&g_xUPnP
);
2973 pthread_cond_destroy(&g_condUPnP
);
2974 pthread_cond_destroy(&g_condUPnPControlURL
);
2975 if (g_fLogging
& NALOG_ERROR
)
2976 fprintf(log
, "UpnpInit - mutex init failed\n");
2977 return NA_E_INTERNAL_ERROR
;
2980 fFirstInitLocks
= FALSE
;
2985 // initialize UDP socket for SSDP
2986 g_sUDP
= SSDPListen();
2987 g_sUDPCancel
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
); // sock to signal canccelation to UDP thread
2988 if (g_sUDP
< 0 || g_sUDPCancel
< 0) {
2989 if (g_fLogging
& NALOG_ERROR
)
2990 fprintf(log
, "UpnpInit - Failed to init multicast socket.\n");
2991 return NA_E_INTERNAL_ERROR
;
2995 pthread_attr_init(&attr
);
2996 iRet
= pthread_create(&g_UDPthread
, &attr
, UDPProc
, log
);
2998 g_fFirstInit
= TRUE
; // so we'll redo this part next time
3001 if (g_fLogging
& NALOG_ERROR
)
3002 fprintf(log
, "UpnpInit - pthread create failed (%d)\n", iRet
);
3003 return NA_E_THREAD_ERROR
;
3006 // set this to FALSE only if first call succeeded
3007 g_fFirstInit
= FALSE
;
3009 //TracePrint(ELL_TRACE, "UPnP init passed\n");
3012 //tv.tv_usec = 20000; // wait 20ms for thread/udp/multicast init
3013 //select(0, 0, 0, 0, &tv);
3016 // send discovery message
3019 return NA_E_SUCCESS
;
3022 int LegacyNATDestroy()
3024 void *UDPThreadRetVal
;
3026 if (g_sTCPCancel
>= 0) close(g_sTCPCancel
);
3027 if (g_sUDPCancel
>= 0) close(g_sUDPCancel
);
3028 pthread_join(g_UDPthread
, &UDPThreadRetVal
);
3031 g_fFirstInit
= TRUE
;
3032 g_fUPnPEnabled
= FALSE
;
3033 g_fControlURLSet
= FALSE
;
3034 return NA_E_SUCCESS
;