1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 Change History (most recent first):
19 $Log: LegacyNATTraversal.c,v $
20 Revision 1.12.2.1 2006/08/29 06:24:30 cheshire
21 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
23 Revision 1.12 2005/07/22 21:36:16 ksekar
24 Fix GCC 4.0/Intel compiler warnings
26 Revision 1.11 2004/12/03 03:34:20 ksekar
27 <rdar://problem/3882674> LegacyNATTraversal.c leaks threads
29 Revision 1.10 2004/12/01 02:43:49 cheshire
30 Update copyright message
32 Revision 1.9 2004/10/27 02:25:05 cheshire
33 <rdar://problem/3816029> Random memory smashing bug
35 Revision 1.8 2004/10/27 02:17:21 cheshire
36 Turn off "safe_close: ERROR" error messages -- there are too many of them
38 Revision 1.7 2004/10/26 21:15:40 cheshire
39 <rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0
40 Additional fixes: Code should set fds to -1 after closing sockets.
42 Revision 1.6 2004/10/26 20:59:20 cheshire
43 <rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0
45 Revision 1.5 2004/10/26 01:01:35 cheshire
46 Use "#if 0" instead of commenting out code
48 Revision 1.4 2004/10/10 06:51:36 cheshire
49 Declared some strings "const" as appropriate
51 Revision 1.3 2004/09/21 23:40:12 ksekar
52 <rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure
54 Revision 1.2 2004/09/17 01:08:52 cheshire
55 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
56 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
57 declared in that file are ONLY appropriate to single-address-space embedded applications.
58 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
60 Revision 1.1 2004/08/18 17:35:41 ksekar
61 <rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways
66 #include "mDNSEmbeddedAPI.h"
67 #include "mDNSMacOSX.h"
79 #include <sys/types.h>
80 #include <sys/socket.h>
81 #include <netinet/in.h>
82 #include <netinet/tcp.h>
85 #include <sys/ioctl.h>
87 #include <netinet/in.h>
88 #include <arpa/inet.h>
89 #include <sys/sysctl.h>
90 #include <net/route.h>
93 #include <arpa/inet.h>
100 //#include "netaddr.h"
102 // TODO: remove later and do variable length
103 #define MAX_SOAPMSGSIZE 65536
105 static int safe_close(int fd
)
107 if (fd
< 3) { /* LogMsg("safe_close: ERROR sd %d < 3", fd); */ return(-1); }
111 #define close safe_close
113 ////////////////////////////////////////////////////////////////////////
115 ////////////////////////////////////////////////////////////////////////
118 #define NA_E_SUCCESS (0)
119 #define NA_E_INTERNAL_ERROR (-1) /* somewhere something wrong */
120 #define NA_E_INVALID_PARAMETER (-2) /* bad params */
121 #define NA_E_OPERATION_FAILED (-3) /* can't fulfill request */
122 #define NA_E_TIMEOUT (-4) /* operation timed out */
123 #define NA_E_THREAD_ERROR (-5) /* some error related to threads */
124 #define NA_E_PARSE_ERROR (-6) /* a parsing error occured */
125 #define NA_E_NOT_READY (-7) /* this op can't proceed yet */
126 #define NA_E_NOT_FOUND (-8) /* resource/prereq not found */
127 #define NA_E_NOT_AVAILABLE (-9) /* service not available */
128 #define NA_E_EXISTS (-10) /* can't modify existing item */
129 #define NA_E_AGAIN (-11) /* something wrong - try again */
130 #define NA_E_NOT_SUPPORTED (-12) /* wait until next version */
131 #define NA_E_ABORT (-14) /* operation aborted */
132 #define NA_E_NET (-15) /* network layer problem */
134 // Logging flags - log types (increasing degree of detail)
135 #define NALOG_ERROR (1UL) /* error messages */
136 #define NALOG_ALERT (2UL) /* useful warning/alerts */
137 #define NALOG_INFO0 (4UL) /* info - potential problem */
138 #define NALOG_INFO1 (8UL) /* extra info */
139 #define NALOG_DUMP (16UL) /* data dumps */
141 #define NALOG_RSRV1 (32UL) /* reserved */
142 #define NALOG_RSRV2 (64UL) /* reserved */
143 #define NALOG_RSRV3 (128UL) /* reserved */
145 // Logging flags - component (not used for now)
146 #define NALOG_UPNP (256) /* UPnP */
148 // Default Logging levels
149 #define NALOG_LEVEL0 (0)
150 #define NALOG_LEVEL1 (NALOG_UPNP | NALOG_ERROR)
151 #define NALOG_LEVEL2 (NALOG_LEVEL1 | NALOG_ALERT)
152 #define NALOG_LEVEL3 (NALOG_LEVEL2 | NALOG_INFO0)
153 #define NALOG_LEVEL4 (NALOG_LEVEL3 | NALOG_INFO1)
154 #define NALOG_LEVEL5 (NALOG_LEVEL4 | NALOG_DUMP)
155 #define NALOG_DEFAULT_LEVEL (NALOG_LEVEL2)
157 // Default timeout values (in m-seconds (milli))
158 // 50 milliseconds for function timeout
159 #define NA_DEFAULT_FUNCTION_TIMEOUT (50)
161 ////////////////////////////////////////////////////////////////////////
163 ////////////////////////////////////////////////////////////////////////
164 #define SSDP_IP "239.255.255.250"
165 #define SSDP_PORT 1900
169 #define H_CRLF "\r\n"
170 // SOAP message's CRLF:
171 //#define S_CRLF "\r\n"
174 // standard 200 ok msg
175 #define HTTP200OK "HTTP/1.1 200 OK\r\n\r\n"
176 #define HTTP200OKLEN (sizeof(HTTP200OK) - 1)
178 // maximum time to wait for an event (in microseconds)
179 #define MAX_EXPECTEVENTTIME (10000)
181 ////////////////////////////////////////////////////////////////////////
183 ////////////////////////////////////////////////////////////////////////
184 typedef struct tagProperty
{
188 } Property
, *PProperty
;
190 typedef struct tagHTTPResponse
{
194 Property aHeaders
[30]; // assume at most this many headers
200 } HTTPResponse
, *PHTTPResponse
, **PPHTTPResponse
;
202 ////////////////////////////////////////////////////////////////////////
204 ////////////////////////////////////////////////////////////////////////
205 static const char szSSDPMsgDiscoverRoot
[] =
206 "M-SEARCH * HTTP/1.1\r\n"
207 "Host:239.255.255.250:1900\r\n"
208 "ST:upnp:rootdevice\r\n"
209 "Man:\"ssdp:discover\"\r\n"
213 static const char szSSDPMsgDiscoverIGD
[] =
214 "M-SEARCH * HTTP/1.1\r\n"
215 "Host:239.255.255.250:1900\r\n"
216 "ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
217 "Man:\"ssdp:discover\"\r\n"
221 static const char szSSDPMsgDiscoverNAT
[] =
222 "M-SEARCH * HTTP/1.1\r\n"
223 "Host:239.255.255.250:1900\r\n"
224 "ST:urn:schemas-upnp-org:service:WANIPConnection:1\r\n"
225 "Man:\"ssdp:discover\"\r\n"
229 //// Subscribe message
231 // 2$s: local's host/port ("host:port")
232 // 3$s: router's host/port ("host:port")
233 // 4$d: subscription timeout in seconds
234 static const char szEventMsgSubscribeFMT
[] =
235 "SUBSCRIBE %1$s HTTP/1.1\r\n"
237 "Callback: <http://%2$s/notify>\r\n"
238 "Timeout: Second-%4$d\r\n"
239 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
241 "Content-Length: 0\r\n"
242 "Pragma: no-cache\r\n"
245 //// Unsubscribe message
247 // 2$s: SID (some uuid passed back during subscribe)
248 // 3$s: router's host ("host")
250 static const char szEventMsgUnsubscribeFMT
[] =
251 "UNSUBSCRIBE %1$s HTTP/1.1\r\n"
253 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
255 "Content-Length: 0\r\n"
256 "Pragma: no-cache\r\n"
260 //// Generic SOAP Control:Action request messages
262 // 2$s: router's host/port ("host:port")
263 // 3$s: action (string)
264 // 4$d: content-length
265 static const char szSOAPMsgControlAHeaderFMT
[] =
266 //"M-POST %1$s HTTP/1.1\r\n"
267 "POST %1$s HTTP/1.1\r\n"
268 "Content-Type: text/xml; charset=\"utf-8\"\r\n"
269 //"TEST: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
270 //"Man: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
271 //"01-SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#%3$s\"\r\n"
272 "SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#%3$s\"\r\n"
273 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n"
275 "Content-Length: %4$d\r\n"
276 "Connection: close\r\n"
277 // "Connection: Keep-Alive\r\n"
278 "Pragma: no-cache\r\n"
281 // 1$: action (string)
283 static const char szSOAPMsgControlABodyFMT
[] =
284 "<?xml version=\"1.0\"?>" CRLF
285 "<SOAP-ENV:Envelope" S_CRLF
286 " xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"" S_CRLF
287 " SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" S_CRLF
288 "<SOAP-ENV:Body>" S_CRLF
290 " xmlns:m=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" S_CRLF
293 "</SOAP-ENV:Body>" S_CRLF
294 "</SOAP-ENV:Envelope>" S_CRLF
301 // 2$: argument value
302 static const char szSOAPMsgControlAArgumentFMT
[] =
303 "<%1$s>%2$s</%1$s>" S_CRLF
;
306 // 2$: argument value
308 static const char szSOAPMsgControlAArgumentFMT_t
[] =
310 " xmlns:dt=\"urn:schemas-microsoft-com:datatypes\""
311 " dt:dt=\"%3$s\">%2$s</%1$s>" S_CRLF
;
314 //// Generic SOAP Control:Query request messages
316 // 2$s: router's host/port ("host:port")
317 // 3$d: content-length
318 static const char szSOAPMsgControlQHeaderFMT
[] =
319 "M-POST %1$s HTTP/1.1\r\n"
320 //"POST %1$s HTTP/1.1\r\n"
322 "Content-Length: %3$d\r\n"
323 "Content-Type: text/xml; charset-\"utf-8\"\r\n"
324 //"Man: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
325 //"SOAPAction: \"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"\r\n"
326 "01-SOAPAction: \"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"\r\n"
330 static const char szSOAPMsgControlQBodyFMT
[] =
332 " xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"" S_CRLF
333 " s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" S_CRLF
335 "<u:QueryStateVariable xmlns:u=\"urn:schemas-upnp-org:control-1-0\"" S_CRLF
336 "<u:varName>%s</u:varName>" S_CRLF
337 "</u:QueryStateVariable>" S_CRLF
339 "</s:Envelope>" S_CRLF
342 // 1$: device description URL
344 static const char szSSDPMsgDescribeDeviceFMT
[] =
345 "GET %s HTTP/1.1\r\n"
346 "Accept: text/xml, application/xml\r\n"
347 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
349 "Connection: close\r\n"
350 // "Connection: Keep-Alive\r\n"
353 ////////////////////////////////////////////////////////////////////////
355 ////////////////////////////////////////////////////////////////////////
357 static int g_fFirstInit
= TRUE
;
358 static int g_fQuit
= FALSE
;
360 static int g_fLogging
;
362 // Globally-accessible UDP socket
363 static int g_sUDP
= -1;
364 static int g_sUDPCancel
= -1;
366 // Globally-accessible TCP socket
367 static int g_sTCP
= -1;
368 static int g_sTCPCancel
= -1;
371 static int g_fEventEnabled
= FALSE
;
372 static unsigned short g_wEventPort
;
373 static struct sockaddr_in g_saddrRouterEvent
;
374 static char g_szRouterHostPortEvent
[1024];
375 static char g_szEventURL
[1024];
378 static char g_szFriendlyName
[1024];
379 static char g_szManufacturer
[1024];
380 static char g_szModelName
[1024];
381 static char g_szModelDescription
[1024];
384 static struct sockaddr_in g_saddrRouterBase
;
385 static char g_szRouterHostPortBase
[1024];
388 static pthread_t g_UDPthread
= NULL
;
389 static pthread_t g_TCPthread
= NULL
;
392 static unsigned long g_dwLocalIP
= 0;
394 // Globally accessible info about the router/UPnP
395 static int g_fUPnPEnabled
= FALSE
;
396 static char g_szUSN
[1024];
398 static struct sockaddr_in g_saddrRouterDesc
;
399 static char g_szRouterHostPortDesc
[1024];
400 static char g_szNATDevDescURL
[1024];
402 static struct sockaddr_in g_saddrRouterSOAP
;
403 static char g_szRouterHostPortSOAP
[1024];
404 static char g_szControlURL
[1024];
405 static int g_fControlURLSet
= FALSE
;
407 // Lock/condvar for synchronous upnp calls
408 static pthread_mutex_t g_xUPnP
;
409 static pthread_mutex_t g_xUPnPMsg
;
410 static pthread_cond_t g_condUPnP
;
411 static pthread_cond_t g_condUPnPControlURL
;
412 static struct timeval g_tvUPnPInitTime
;
413 static struct timeval g_tvLastUpdateTime
;
415 // timeout values in seconds
416 static int g_iFunctionTimeout
= NA_DEFAULT_FUNCTION_TIMEOUT
;
418 static void GetDeviceDescription(void);
419 static void SetLocalIP(void);
421 ////////////////////////////////////////////////////////////////////////
423 ////////////////////////////////////////////////////////////////////////
428 #define IFNAMELEN 16 /* Interface Name Length */
429 #define IPLEN 16 /* 16 bytes(128 bits) for IPv6 */
431 typedef struct tagIPINFO
434 char szIfName
[IFNAMELEN
]; /* Interface name */
435 unsigned char abIP
[IPLEN
]; /* IP in host byte order */
436 unsigned short wPort
;
437 } IPINFO
, *PIPINFO
, **PPIPINFO
;
439 typedef struct hostent HOSTENT
, *PHOSTENT
;
441 static unsigned long GetNATIPNetmask(unsigned long dwIP
)
443 if ((dwIP
& 0xFF000000) == 0x0A000000) return 0xFF000000;
444 if ((dwIP
& 0xFFF00000) == 0xAC100000) return 0xFFF00000;
445 if ((dwIP
& 0xFFFF0000) == 0xC0a80000) return 0xFFFF0000;
447 return 0; /* No NAT IP */
450 static int GetIPInfo(PPIPINFO ppIPInfo
)
453 int iLastLen
, iLen
, iNum
= 0, iMax
= 0;
455 char *pcBuf
, *pcTemp
;
456 PIPINFO pIPInfo
= NULL
;
458 struct ifreq
*ifr
, ifrcopy
;
460 if (ppIPInfo
== NULL
) return 0;
462 fd
= socket(AF_INET
, SOCK_DGRAM
, 0);
465 iLen
= 100 * sizeof(struct ifreq
);
469 pcBuf
= (char *)malloc(iLen
);
473 if (ioctl(fd
, SIOCGIFCONF
, &ifc
) < 0)
475 if (errno
!= EINVAL
|| iLastLen
!= -1)
477 // DbgPrint(ELL_ERROR, "ioctl failed(%d)\n", errno);
485 if (ifc
.ifc_len
== iLastLen
) break;
486 iLastLen
= ifc
.ifc_len
;
489 iLen
+= 10 * sizeof(struct ifreq
);
493 for (pcTemp
= pcBuf
; pcTemp
< pcBuf
+ ifc
.ifc_len
; )
500 pIPInfoNew
= (PIPINFO
)realloc(pIPInfo
, sizeof(IPINFO
) * iMax
);
501 if (pIPInfoNew
== NULL
)
508 else pIPInfo
= pIPInfoNew
;
510 memset(pIPInfo
+ (iMax
- 10), 0, sizeof(IPINFO
) * 10);
513 ifr
= (struct ifreq
*)pcTemp
;
515 pcTemp
+= sizeof(ifr
->ifr_name
) + ifr
->ifr_addr
.sa_len
;
517 /* discard invalid address families & loopback */
518 if ((ifr
->ifr_addr
.sa_family
!= AF_INET
&&
519 ifr
->ifr_addr
.sa_family
!= AF_INET6
) ||
520 strncmp(ifr
->ifr_name
, "lo", 2) == 0) continue;
523 ioctl(fd
, SIOCGIFFLAGS
, &ifrcopy
);
524 if ((ifrcopy
.ifr_flags
& IFF_UP
) == 0) continue;
526 switch (ifr
->ifr_addr
.sa_family
)
529 memcpy(pIPInfo
[iNum
].szIfName
, ifr
->ifr_name
, IFNAMELEN
);
531 ntohl(((struct sockaddr_in
*)&ifr
->ifr_addr
)->sin_addr
.s_addr
);
532 memcpy(pIPInfo
[iNum
].abIP
, &dwIP
, sizeof(unsigned long));
533 if (ifrcopy
.ifr_flags
& IFF_POINTOPOINT
)
534 pIPInfo
[iNum
].iFlags
|= ISPPP
;
539 memcpy(pIPInfo
[iNum
].szIfName
, ifr
->ifr_name
, IFNAMELEN
);
540 memcpy(pIPInfo
[iNum
].abIP
,
541 ((struct sockaddr_in6
*)&(ifr
->ifr_addr
))-> sin6_addr
.s6_addr
,
543 pIPInfo
[iNum
].iFlags
|= ISIPV6
;
544 if (ifrcopy
.ifr_flags
& IFF_POINTOPOINT
)
545 pIPInfo
[iNum
].iFlags
|= ISPPP
;
562 static void FreeIPInfo(PIPINFO pIPInfo
)
564 if (pIPInfo
!= NULL
) free(pIPInfo
);
568 ////////////////////////////////////////////////////////////////////////
569 // Function Definitions
570 ////////////////////////////////////////////////////////////////////////
572 static void SendDiscoveryMsg();
575 // Creates a UDP multicast socket and listens to the SSDP IP/PORT
577 // -1 on error, or the socket descriptor if success
578 static int SSDPListen()
583 struct sockaddr_in saddr
;
586 // IPPROTO_IP == 0; IPPROTO_TCP == 6; IPPROTO_UDP == 17; etc.
587 sd
= socket(AF_INET
, SOCK_DGRAM
, 0);
589 if (g_fLogging
& NALOG_ERROR
)
590 fprintf(g_log
, "Can't create socket! SSDPListen exiting\n");
594 // sock options values
595 fLoop
= 0; // false - don't send copy to self
598 // bind to listen to ssdp multicast address
599 bzero(&saddr
, sizeof(saddr
));
600 saddr
.sin_len
= sizeof(saddr
);
601 saddr
.sin_family
= AF_INET
;
602 //saddr.sin_addr.s_addr = inet_addr(SSDP_IP);
603 //saddr.sin_port = htons(SSDP_PORT);
604 saddr
.sin_addr
.s_addr
= htonl(g_dwLocalIP
);
607 // and set the multicast add_member structure
608 // (TODO: need to find interfaces later - ioctl, with:
609 // SIOCFIFCONF to find if's, SIOCGIFADDR to get addr, and SIOCFIFFLAGS
610 // to check for IFF_MULTICAST flag for multicast support on an if)
611 bzero(&mreq
, sizeof(mreq
));
612 mreq
.imr_interface
.s_addr
= g_dwLocalIP
;
613 mreq
.imr_multiaddr
.s_addr
= inet_addr(SSDP_IP
);
616 bind(sd
, (struct sockaddr
*)&saddr
, sizeof(saddr
)) //||
617 //setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &fLoop, sizeof(fLoop)) ||
618 //setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &iTTL, sizeof(iTTL)) ||
619 //setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))
621 if (g_fLogging
& NALOG_ERROR
)
623 "bind/setsockopt for multicast failed... errno = %d\n", errno
);
631 static int EventListen()
633 struct sockaddr_in saddr
;
636 // try 5 ports before failing completely
637 for (g_wEventPort
= 5000; g_wEventPort
< 5005; g_wEventPort
++)
639 sd
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
641 if (g_fLogging
& NALOG_ERROR
)
642 fprintf(g_log
, "Can't create socket! EventListen exiting\n");
646 bzero(&saddr
, sizeof(saddr
));
647 saddr
.sin_len
= sizeof(saddr
);
648 saddr
.sin_family
= AF_INET
;
649 saddr
.sin_addr
.s_addr
= htonl(g_dwLocalIP
);
650 saddr
.sin_port
= htons(g_wEventPort
);
653 if (bind(sd
, (struct sockaddr
*)&saddr
, sizeof(saddr
)) == 0)
656 ////TracePrint(ELL_TRACE, "UPnP: EventListen @%u\n", g_wEventPort);
660 // unsuccessful - close sd and try again
661 if (g_fLogging
& NALOG_ERROR
)
663 "bind TCP port %u failed: errno = %d\n", g_wEventPort
, errno
);
670 static void *TCPProc(void *in
);
672 static int EventInit()
677 if (g_fEventEnabled
== FALSE
)
679 // initialize TCP socket for Eventing
680 g_sTCP
= EventListen();
682 if (g_fLogging
& NALOG_ERROR
)
683 fprintf(g_log
, "EventInit - Failed to init tcp socket.\n");
684 return NA_E_INTERNAL_ERROR
;
688 pthread_attr_init(&attr
);
689 iRet
= pthread_create(&g_TCPthread
, &attr
, TCPProc
, 0);
693 if (g_fLogging
& NALOG_ERROR
)
694 fprintf(g_log
, "EventInit: TCPProc create failed(%d)\n", iRet
);
695 return NA_E_THREAD_ERROR
;
699 g_fEventEnabled
= TRUE
;
704 static void DumpHex(char *buf
, int len
)
711 if (g_fLogging
& NALOG_DUMP
) {
712 if (buf
== NULL
) return;
713 if (len
<= 0) return;
715 for (i
= 0; i
< len
; i
= nexti
) {
716 fprintf(g_log
, "%04x: ", i
);
718 endj
= (nexti
> len
) ? len
: nexti
;
719 for (j
= i
; j
< endj
; j
++)
720 fprintf(g_log
, "%02x ", buf
[j
] & 0xff);
723 char pad
[3 * 16 + 1]; // don't need the last 3 bytes anyway
724 j
= (16 - (j
% 16)) * 3;
730 for (j
= i
; j
< endj
; j
++)
731 isprint(buf
[j
]) ? fputc(buf
[j
], g_log
) : fputc('.', g_log
);
738 // FindHTTPHeaderNewLine
739 // Returns a pointer to the beginning of a CRLF, that is not a
740 // part of LWS. (LWS is CRLF followed by a space or tab, and in
741 // HTTP, considered as equivalent to a single space) (LWS stands
742 // for "linear white space")
743 // Returns a pointer the beginning of CRLF, and sets the EOH flag to
744 // whether this is the last header in the HTTP header section.
745 // Also, if pbuf is NULL, or if there isn't any CRLF found in the
746 // string, or if the HTTP syntax is wrong, NULL is returned, and
747 // the EOH flag is not touched.
748 static char *FindHTTPHeaderNewLine(char *pbuf
, int iBufSize
, int *pfEOH
)
753 if (pbuf
== NULL
) return NULL
;
756 result
= memchr(pbuf
, '\r', iBufSize
);
757 if (result
== NULL
) {
758 if (g_fLogging
& NALOG_INFO0
) {
759 fprintf(g_log
, "FindHTTPHeaderNewLine: er @(%d)\n", i
);
766 // decrement iBufSize, and move pbuf forward
767 iBufSize
-= (result
- pbuf
);
770 ++pbuf
; // now pointing right after "\r"
772 if (*pbuf
== '\0') break;
773 if (*pbuf
!= '\n') continue;
775 ++pbuf
; // now pointing after "\r\n"
777 if (*pbuf
== '\0') break;
778 if ((*pbuf
== ' ') || (*pbuf
== '\t')) continue;
780 // at this point we know we're at the end of a header field,
781 // and there's more stuff coming...
783 // just need to check if this is the last header
784 if ((pbuf
[0] == '\r') && (pbuf
[1] == '\n'))
795 // NewHTTPResponse_sz
796 // Creates an HTTPResponse structure from a string (sz). Set
797 // fDestroyOriginal to TRUE if the buffer passed in can be overwritten.
798 // Otherwise, NewHTTPResponse_sz will duplicate the buffer.
799 // Returns the created HTTPResponse structure if successful, or if an
800 // error occured (out of memory, or bad http syntax), returns NULL.
801 // NOTE: ALWAYS call DeleteHTTPResponse after using the HTTPResponse structure.
802 // NOTE: The input is assumed to be correct. If there're HTTP syntax errors,
803 // and the pszHTTPResponse is not null-terminated, result may be undefined.
804 // (to be fixed next version)
805 static PHTTPResponse
NewHTTPResponse_sz(
806 char *pszHTTPResponse
,
808 int fDestroyOriginal
)
810 PHTTPResponse pResponse
;
816 if ((pResponse
= (PHTTPResponse
)malloc(sizeof(HTTPResponse
))) == NULL
) {
817 if (g_fLogging
& NALOG_INFO0
) {
818 fprintf(g_log
, "NewHTTPResponse_sz: er 1\n");
824 // make copy of buffer now
825 if (fDestroyOriginal
) {
826 pResponse
->buf
= NULL
;
827 pBuf
= pszHTTPResponse
;
830 int len
= strlen(pszHTTPResponse
);
831 if ((len
+1) > iBufferSize
) {
832 if (g_fLogging
& NALOG_INFO0
)
833 fprintf(g_log
, "Length: %d > %d\n", len
+1, iBufferSize
);
836 if ((pResponse
->buf
= (char *)malloc(iBufferSize
)) == NULL
) {
838 if (g_fLogging
& NALOG_INFO0
) {
839 fprintf(g_log
, "NewHTTPResponse_sz: er 2\n");
844 memcpy(pResponse
->buf
, pszHTTPResponse
, iBufferSize
);
845 pBuf
= pResponse
->buf
;
848 // get the first line
849 pszEOL
= FindHTTPHeaderNewLine(pBuf
, iBufferSize
, &fEOH
);
850 if (pszEOL
== NULL
) {
851 if (g_fLogging
& NALOG_INFO0
) {
852 fprintf(g_log
, "NewHTTPResponse_sz: er 3\n");
858 *pszEOL
= '\0'; // terminate the status line
859 pszEOL
+= 2; // point to the rest of the buffer
861 // set the status string first
862 pResponse
->pszStatus
= strchr(pBuf
, ' ');
863 if (pResponse
->pszStatus
== NULL
) {
864 if (g_fLogging
& NALOG_INFO0
) {
865 fprintf(g_log
, "NewHTTPResponse_sz: er 4\n");
868 goto cleanup
; // syntax error
871 pResponse
->pszStatus
++; // point to the actual status
873 pResponse
->pszReason
= strchr(pResponse
->pszStatus
, ' ');
874 if (pResponse
->pszReason
== NULL
) {
875 if (g_fLogging
& NALOG_INFO0
) {
876 fprintf(g_log
, "NewHTTPResponse_sz: er 5\n");
879 goto cleanup
; // syntax error
882 pResponse
->pszReason
[0] = '\0'; // terminate status string
883 pResponse
->pszReason
++; // point to the reason string
885 iNumHeaders
= 0; // initialize to 0 headers
887 // parse header fields line by line (while not end of headers)
889 PProperty pHeader
= &(pResponse
->aHeaders
[iNumHeaders
]);
890 // point header field name to the first char of the line
891 pHeader
->pszName
= pszEOL
;
893 // search for the end of line
894 pszEOL
= FindHTTPHeaderNewLine(pszEOL
,
895 iBufferSize
- (pszEOL
- pBuf
), // remainder size
897 if (pszEOL
== NULL
) goto cleanup
; // syntax error
899 *pszEOL
= '\0'; // terminate this string
900 pszEOL
+= 2; // point to beginning of next line
902 pHeader
->pszValue
= strchr(pHeader
->pszName
, ':');
903 if (pHeader
->pszValue
== NULL
) {
904 if (g_fLogging
& NALOG_INFO0
) {
905 fprintf(g_log
, "NewHTTPResponse_sz: er 6\n");
908 goto cleanup
; // syntax error (header field has no ":")
911 pHeader
->pszValue
[0] = '\0'; // terminate the header name string
912 pHeader
->pszValue
++; // point after the ":"
913 // get rid of leading spaces for the value part
915 (pHeader
->pszValue
[0] == ' ') ||
916 (pHeader
->pszValue
[0] == '\t') ||
917 (pHeader
->pszValue
[0] == '\r') ||
918 (pHeader
->pszValue
[0] == '\n')
920 pHeader
->pszValue
++; // skip the space
923 iNumHeaders
++; // added one more header
924 pHeader
++; // point to the next header in pResponse->aHeaders
927 pResponse
->iNumHeaders
= iNumHeaders
; // remember to set it in pResponse
929 pResponse
->pszBody
= pszEOL
+ 2; // point after the empty line
934 if (pResponse
->buf
!= NULL
) free(pResponse
->buf
);
939 // DeleteHTTPResponse
940 // Deallocates stuff in the HTTPResponse structure, effectively returning
941 // memory to the system and destroying the structure.
942 // NOTE: The pointer pResponse WILL BE FREED, and will be unusable after
943 // the call to DeleteHTTPResponse.
944 static void DeleteHTTPResponse(PHTTPResponse pResponse
)
948 if (pResponse
== NULL
) return;
950 // Current impl is just simple array - no need to free()
951 //for (i = 0; i < pResponse->iNumHeaders; i++) {
952 // free(pResponse->aHeaders[i]);
955 if (pResponse
->buf
!= NULL
)
956 free(pResponse
->buf
);
960 //typedef struct tagHTTPResponse {
964 // Property aHeaders[30]; // assume at most this many headers
970 //} HTTPResponse, *PHTTPResponse, **PPHTTPResponse;
972 static void PrintHTTPResponse(PHTTPResponse pResponse
)
976 if (g_fLogging
& (NALOG_INFO1
)) {
977 if (pResponse
== NULL
) return;
978 fprintf(g_log
, " *** HTTP response begin *** \n");
979 fprintf(g_log
, " * status = [%s], reason = [%s] *\n",
980 pResponse
->pszStatus
, pResponse
->pszReason
);
981 for (i
= 0; i
< pResponse
->iNumHeaders
; i
++) {
982 fprintf(g_log
, " * Header \"%s\" = [%s]\n",
983 pResponse
->aHeaders
[i
].pszName
,
984 pResponse
->aHeaders
[i
].pszValue
);
986 if (g_fLogging
& NALOG_DUMP
)
987 fprintf(g_log
, " * body = [%s] *\n", pResponse
->pszBody
);
988 fprintf(g_log
, " *** HTTP response end *** \n");
992 static int DiscoverRouter(PHTTPResponse pResponse
)
995 int fLocation
= FALSE
;
997 int fIsNATDevice
= FALSE
;
1000 if (strcmp(pResponse
->pszStatus
, "200") != 0)
1004 if (pResponse
== NULL
) {
1005 if (g_fLogging
& NALOG_INFO0
)
1006 fprintf(g_log
, "DiscoverRouter: pResponse == NULL\n");
1010 // check to see if this is a relevant packet
1011 for (i
= 0; i
< pResponse
->iNumHeaders
; i
++) {
1012 PProperty pHeader
= &(pResponse
->aHeaders
[i
]);
1014 if ((strcasecmp(pHeader
->pszName
, "ST") == 0) ||
1015 (strcasecmp(pHeader
->pszName
, "NT") == 0)) {
1016 if ((strcmp(pHeader
->pszValue
,
1017 "urn:schemas-upnp-org:service:WANIPConnection:1") == 0) ||
1018 (strcmp(pHeader
->pszValue
,
1019 "urn:schemas-upnp-org:device:InternetGatewayDevice:1") == 0)) {
1020 fIsNATDevice
= TRUE
;
1025 // leave the message alone if we don't need it
1029 // Now that we know we're looking at the message about the NAT device:
1030 pthread_mutex_lock(&g_xUPnP
);
1032 // set upnp to be unconfigured for now
1033 g_fUPnPEnabled
= FALSE
;
1035 // loop through the headers
1036 for (i
= 0; i
< pResponse
->iNumHeaders
; i
++) {
1037 PProperty pHeader
= &(pResponse
->aHeaders
[i
]);
1039 if (strcasecmp(pHeader
->pszName
, "Location") == 0) {
1043 if (g_fLogging
& NALOG_INFO1
)
1044 fprintf(g_log
, "Checking Location...\n");
1045 p
= pHeader
->pszValue
;
1046 if (strncmp(p
, "http://", 7) != 0)
1047 continue; // hope for another Location header to correct it
1048 p
+= 7; // skip over "http://"
1051 // set the control URL first
1053 g_szNATDevDescURL
[0] = '/';
1054 g_szNATDevDescURL
[1] = '\0';
1057 strncpy(g_szNATDevDescURL
, q
, sizeof(g_szNATDevDescURL
) - 1);
1058 g_szNATDevDescURL
[sizeof(g_szNATDevDescURL
) - 1] = '\0';
1059 // terminate the host/port string
1063 if (g_fLogging
& NALOG_INFO1
)
1064 fprintf(g_log
, " Device Description URL set to[%s]...\n",
1067 // see if port is specified
1070 sprintf(g_szRouterHostPortDesc
, "%s", p
);
1072 g_saddrRouterDesc
.sin_addr
.s_addr
= inet_addr(p
);
1073 g_saddrRouterDesc
.sin_port
= htons(80);
1076 // don't include the ":80" - HTTP is by default port 80
1077 if (atoi(q
+1) == 80) *q
= '\0';
1079 strcpy(g_szRouterHostPortDesc
, p
);
1081 // terminate the host part and point to it
1085 g_saddrRouterDesc
.sin_addr
.s_addr
= inet_addr(p
);
1086 g_saddrRouterDesc
.sin_port
= htons(atoi(q
));
1089 g_saddrRouterDesc
.sin_family
= AF_INET
;
1091 if (g_fLogging
& NALOG_INFO1
)
1092 fprintf(g_log
, " Router Address set to[%s]...\n",
1093 g_szRouterHostPortDesc
);
1096 else if (strcasecmp(pHeader
->pszName
, "USN") == 0) {
1097 if (g_fLogging
& NALOG_INFO1
)
1098 fprintf(g_log
, "Checking USN...\n");
1099 strncpy(g_szUSN
, pHeader
->pszValue
, sizeof(g_szUSN
) - 1);
1100 g_szUSN
[sizeof(g_szUSN
) - 1] = '\0';
1104 ; // do nothing for other headers for now
1108 // now check flags and set enabled if all set
1109 if (fLocation
&& fUSN
) {
1110 if (g_fLogging
& NALOG_INFO1
) {
1112 "Description Host/port string: [%s]\n"
1113 "NATDevDescURL: [%s], USN: [%s]\n",
1114 g_szRouterHostPortDesc
,
1115 g_szNATDevDescURL
, g_szUSN
);
1116 if (g_fLogging
& NALOG_INFO1
)
1117 fprintf(g_log
, "Got router information\n");
1120 g_fUPnPEnabled
= TRUE
;
1121 pthread_cond_broadcast(&g_condUPnP
);
1124 // remember to unlock before return
1125 pthread_mutex_unlock(&g_xUPnP
);
1130 // granularity is specified as: granularity = 1/nth seconds
1131 #define UPNP_TIMEOUT_GRANULARITY (1000)
1132 #define U_TOGRAN UPNP_TIMEOUT_GRANULARITY
1135 static void TimevalSubtract(
1136 struct timeval
*result
,
1137 const struct timeval
*a
,
1138 const struct timeval
*b
)
1140 result
->tv_sec
= a
->tv_sec
- b
->tv_sec
;
1142 if (b
->tv_usec
> a
->tv_usec
) {
1144 result
->tv_usec
= 1000000 + a
->tv_usec
- b
->tv_usec
;
1147 result
->tv_usec
= a
->tv_usec
- b
->tv_usec
;
1150 // elapsed = end - start
1151 static void GetTimeElapsed(
1152 const struct timeval
*tv_start
,
1153 const struct timeval
*tv_end
,
1154 struct timeval
*tv_elapsed
)
1156 TimevalSubtract(tv_elapsed
, tv_end
, tv_start
);
1158 tv_elapsed
->tv_sec
= tv_end
->tv_sec
- tv_start
->tv_sec
;
1160 if (tv_start
->tv_usec
> tv_end
->tv_usec
) {
1161 tv_elapsed
->tv_sec
--;
1162 tv_elapsed
->tv_usec
= 1000000 + tv_end
->tv_usec
- tv_start
->tv_usec
;
1165 tv_elapsed
->tv_usec
= tv_end
->tv_usec
- tv_start
->tv_usec
;
1169 // returns +1, 0, or -1, if a>b, a==b, a<b, respectively
1170 static int CompareTime(
1171 const struct timeval
*a
,
1172 const struct timeval
*b
1175 if ((a
->tv_sec
== b
->tv_sec
) &&
1176 (a
->tv_usec
== b
->tv_usec
)) return 0;
1178 if (a
->tv_sec
> b
->tv_sec
) return 1;
1179 else if (a
->tv_sec
< b
->tv_sec
) return -1;
1181 // if seconds are equal...
1182 if (a
->tv_usec
> b
->tv_usec
) return 1;
1186 static int WaitControlURLSet(double timeout
)
1190 struct timeval tv_start
;
1192 long to_sec
= (int) (timeout
/ U_TOGRAN
);
1194 (int) (((timeout
/ U_TOGRAN
) - to_sec
) * 1000000.0);
1195 //long to_sec = (int) timeout;
1196 //long to_usec = (int) ((timeout - to_sec) * 1000000.0);
1197 struct timeval elapsed
;
1199 // get function start time
1200 gettimeofday(&tv_start
, NULL
);
1202 pthread_mutex_lock(&g_xUPnP
);
1205 // if last update is too long ago then wait for it
1206 GetTimeElapsed(&g_tvLastUpdateTime
, &tv_start
, &elapsed
);
1207 if ((elapsed
.tv_sec
+ (elapsed
.tv_usec
/ 1000000.0)) >
1208 (((double) g_iUPnPTimeout
) / U_TOGRAN
))
1209 g_fControlURLSet
= 0;
1212 while (!g_fControlURLSet
) {
1214 gettimeofday(&tv
, NULL
);
1217 for now ignore device timeout
1218 // see if we've past the device's timeout first
1219 GetTimeElapsed(&g_tvUPnPInitTime
, &tv
, &elapsed
);
1220 if ((elapsed
.tv_sec
> g_timeout_sec
) ||
1221 ( (elapsed
.tv_sec
== g_timeout_sec
) &&
1222 (elapsed
.tv_usec
> g_timeout_usec
)
1225 pthread_mutex_unlock(&g_xUPnP
);
1230 // calculate ts to sleep till
1231 ts
.tv_sec
= tv
.tv_sec
+ to_sec
;
1232 ts
.tv_nsec
= (tv
.tv_usec
+ to_usec
) * 1000;
1233 if (ts
.tv_nsec
> 1000000000) {
1234 ts
.tv_nsec
-= 1000000000;
1238 // now get how long we've been in this function already and deduct
1239 GetTimeElapsed(&tv_start
, &tv
, &elapsed
);
1240 ts
.tv_sec
-= elapsed
.tv_sec
;
1241 if (ts
.tv_nsec
< (elapsed
.tv_usec
* 1000)) {
1243 ts
.tv_nsec
= 1000000000 + ts
.tv_nsec
- (elapsed
.tv_usec
* 1000);
1246 ts
.tv_nsec
-= (elapsed
.tv_usec
* 1000);
1249 iRet
= pthread_cond_timedwait(&g_condUPnPControlURL
, &g_xUPnP
, &ts
);
1251 // if timeout then return false
1254 pthread_mutex_unlock(&g_xUPnP
);
1258 pthread_mutex_unlock(&g_xUPnP
);
1263 static int WaitUPnPFunction()
1265 struct timeval start
;
1266 // struct timeval end;
1268 // struct timeval elapsed;
1270 gettimeofday(&start
, NULL
);
1272 wait2
= (double)g_iFunctionTimeout
;
1274 WaitControlURLSet(wait2
);
1276 //gettimeofday(&end, NULL);
1277 //GetTimeElapsed(&start, &end, &elapsed);
1278 //fprintf(stderr, "== wait2: (%f) %d.%06d\n",
1279 // wait2/U_TOGRAN, elapsed.tv_sec, elapsed.tv_usec);
1281 return g_fControlURLSet
;
1284 static void SetLocalIP();
1286 static int SendTCPMsg_saddr_parse(
1287 char *msg
, int iLen
,
1288 char *result
, int resultSize
,
1289 struct sockaddr_in
*saHost
);
1291 static void *TCPProc(void *in
)
1294 unsigned char buf
[MAX_SOAPMSGSIZE
];
1299 //TracePrint(ELL_TRACE, "UPnP: Begin TCPProc\n");
1301 // do the subscription
1304 char response
[2000];
1307 sprintf(callback
, "%lu.%lu.%lu.%lu:%u",
1308 (g_dwLocalIP
>> 24) & 0xFF,
1309 (g_dwLocalIP
>> 16) & 0xFF,
1310 (g_dwLocalIP
>> 8) & 0xFF,
1311 (g_dwLocalIP
>> 0) & 0xFF,
1314 n
= sprintf((char *)buf
,
1315 szEventMsgSubscribeFMT
,
1317 callback
, g_szRouterHostPortEvent
, 1800);
1319 memset(response
, 0, 2000);
1320 n
= SendTCPMsg_saddr_parse(
1323 &g_saddrRouterEvent
);
1327 resp
= NewHTTPResponse_sz((char *)buf
, n
, TRUE
);
1330 ////TracePrint(ELL_TRACE, "UPnP Subscribe returns %s/%d\n", resp->pszStatus, n);
1334 ////TracePrint(ELL_TRACE, "UPnP Subscribe not enough response (%d) \n[%s]\n",
1337 DeleteHTTPResponse(resp
);
1341 ////TracePrint(ELL_TRACE, "UPnP Subscribe failed (%d)\n", n);
1346 //TracePrint(ELL_TRACE, "UPnP: TCPProc begin loop\n");
1353 struct sockaddr_in recvaddr
;
1354 socklen_t recvaddrlen
;
1356 struct timeval timeout
;
1361 // for after responding to long(?) TCP event
1365 if (g_sTCPCancel
!= -1) close(g_sTCPCancel
);
1366 sMax
= g_sTCPCancel
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
1367 if (sMax
< g_sTCP
) sMax
= g_sTCP
;
1370 FD_SET(g_sTCP
, &readfds
);
1371 FD_SET(g_sTCPCancel
, &readfds
);
1372 iRet
= select(sMax
+1, &readfds
, NULL
, NULL
, NULL
);
1376 //TracePrint(ELL_TRACE, "UPnP Event select failed (%d)\n", errno);
1380 recvaddrlen
= sizeof(recvaddr
);
1381 sEvent
= accept(g_sTCP
, (struct sockaddr
*)&recvaddr
, &recvaddrlen
);
1382 // not likely - (system's descriptor/file table full)
1383 if (sEvent
<= 0) continue;
1385 ////TracePrint(ELL_TRACE, "UPnP receiving event..\n");
1387 // read all we could from this event
1393 FD_SET(sEvent
, &readfds
);
1395 timeout
.tv_usec
= 400000; // long cause we're dealing with input
1396 iRet
= select(sEvent
+1, &readfds
, NULL
, NULL
, &timeout
);
1407 iRet
= recv(sEvent
, buf
+ iBufLen
, MAX_SOAPMSGSIZE
- iBufLen
, 0);
1410 // something is wrong
1423 iTemp
= send(sEvent
, HTTP200OK
, HTTP200OKLEN
, 0);
1424 shutdown(sEvent
, 1);
1429 // now send 200 OK and be done
1432 ////TracePrint(ELL_TRACE, "UPnP event (%d) received (%d)\n", g_fExpectEvent, iBufLen);
1434 // and parse the XML here.
1435 if (iBufLen
< MAX_SOAPMSGSIZE
)
1437 buf
[iBufLen
] = '\0';
1438 // for now do nothing
1442 buf
[MAX_SOAPMSGSIZE
- 1] = '\0';
1447 //TracePrint(ELL_TRACE, "UPnP: TCPProc end\n");
1450 g_fEventEnabled
= FALSE
;
1451 if (g_sTCPCancel
!= -1) close(g_sTCPCancel
);
1456 static void *UDPProc(void *in
)
1458 // char fLoop = 0; // false - don't send copy to self
1459 // int iTTL = SSDP_TTL;
1461 // struct ip_mreq mreq;
1462 // struct sockaddr_in saddr;
1463 unsigned char buf
[65536];
1464 // FILE *log = g_log;
1465 static time_t last_getdevicedesc_t
= 0;
1468 pthread_mutex_lock(&g_xUPnP
);
1469 gettimeofday(&g_tvUPnPInitTime
, NULL
);
1470 pthread_mutex_unlock(&g_xUPnP
);
1474 struct sockaddr_in recvaddr
;
1475 socklen_t recvaddrlen
;
1477 //struct timeval timeout;
1481 if (g_sUDPCancel
< g_sUDP
) sMax
= g_sUDP
;
1482 else sMax
= g_sUDPCancel
;
1485 FD_SET(g_sUDP
, &readfds
);
1486 FD_SET(g_sUDPCancel
, &readfds
);
1487 iRet
= select(sMax
+1, &readfds
, NULL
, NULL
, NULL
);
1493 close(g_sUDPCancel
);
1501 if (!FD_ISSET(g_sUDP
, &readfds
)) continue;
1502 recvaddrlen
= sizeof(recvaddr
);
1503 n
= recvfrom(g_sUDP
, buf
, sizeof(buf
), 0,
1504 (struct sockaddr
*)&recvaddr
, &recvaddrlen
);
1506 if (g_fLogging
& NALOG_ERROR
)
1507 fprintf(g_log
, "recv failed (%d)\n", errno
);
1509 close(g_sUDPCancel
);
1515 if (strncmp((char *)buf
, "HTTP/1.1", 8) == 0) {
1516 PHTTPResponse pResponse
= NewHTTPResponse_sz((char *)buf
, n
, TRUE
);
1517 PrintHTTPResponse(pResponse
);
1518 if (DiscoverRouter(pResponse
) == 0)
1520 time_t now
= time(NULL
);
1521 if (!g_fControlURLSet
||
1522 ((now
- last_getdevicedesc_t
) > 5))
1524 GetDeviceDescription();
1526 last_getdevicedesc_t
= now
;
1529 DeleteHTTPResponse(pResponse
);
1531 else if (strncmp((char *)buf
, "NOTIFY * HTTP/1.1", 7) == 0) {
1532 // temporarily use this to fudge - will have the exact same
1533 // parsing, only status/reason set to "*" and "HTTP/1.1".
1534 // TODO: add support for HTTP requests
1535 PHTTPResponse pResponse
= NewHTTPResponse_sz((char *)buf
, n
, TRUE
);
1536 if (DiscoverRouter(pResponse
) == 0)
1538 time_t now
= time(NULL
);
1539 if (!g_fControlURLSet
||
1540 ((now
- last_getdevicedesc_t
) > 5))
1542 GetDeviceDescription();
1544 last_getdevicedesc_t
= now
;
1547 DeleteHTTPResponse(pResponse
);
1550 if (g_fLogging
& NALOG_DUMP
)
1551 fprintf(g_log
, "(%ld) Buffer: \n[%s]\n", time(NULL
), buf
);
1560 static void SendUDPMsg(const char *msg
) {
1561 struct sockaddr_in saSendTo
;
1565 bzero(&saSendTo
, sizeof(saSendTo
));
1566 saSendTo
.sin_family
= AF_INET
;
1567 saSendTo
.sin_addr
.s_addr
= inet_addr(SSDP_IP
);
1568 saSendTo
.sin_port
= htons(SSDP_PORT
);
1572 if (g_fLogging
& NALOG_DUMP
)
1573 fprintf(g_log
, "SendUDP: [%s]\n", msg
);
1575 iRet
= sendto(g_sUDP
, msg
, iLen
, 0,
1576 (struct sockaddr
*)&saSendTo
, sizeof(saSendTo
));
1580 if (g_fLogging
& NALOG_ALERT
)
1582 "SendUDPMsg: iRet(%d) != strlen(msg)(%d)! (errno %d)\n",
1586 // strstr, case insensitive, and is limited by len
1587 static char *strcasestr_n(const char *big
, const char *little
, int len
)
1594 if (little
== NULL
) return (char *)big
;
1595 if (big
== NULL
) return NULL
;
1597 //bigLen = strlen(big);
1599 littleLen
= strlen(little
);
1601 if (bigLen
< littleLen
) return NULL
;
1603 end
= bigLen
- littleLen
;
1604 for (i
= 0; i
<= end
; (i
++), (big
++)) {
1605 if (strncasecmp(big
, little
, littleLen
) == 0)
1612 // this is strnstr, only portable
1613 static char *strstr_n(const char *big
, const char *little
, int len
)
1618 (void)len
; // unused
1620 if ((big
== NULL
) || (little
== NULL
)) return NULL
;
1622 iBigLen
= strlen(big
);
1623 iLittleLen
= strlen(little
);
1625 // this part is basically strnstr, except this is portable
1627 if (iBigLen
< iLittleLen
)
1629 if (strncmp(big
, little
, iLittleLen
) == 0)
1636 // returns -1 for "not found"
1637 static int FindContentLength(char *pbuf
, int iLen
)
1639 // non reusable HTTP header parsing code:
1640 // ----------------------------------------------
1644 // find content length header
1645 p
= strcasestr_n(pbuf
, "\r\nContent-Length:", iLen
);
1646 if (p
== NULL
) return -1;
1648 p
+= sizeof("\r\nContent-Length:") - 1; // minus '\0'
1653 // ----------------------------------------------
1656 // returns -1 for "not found"
1657 static int FindBody(char *pbuf
, int iLen
)
1659 // non reusable HTTP header parsing code:
1660 // ----------------------------------------------
1664 // find the empty line
1665 p
= strstr_n(pbuf
, "\r\n\r\n", iLen
);
1666 if (p
== NULL
) return -1;
1668 p
+= sizeof("\r\n\r\n") - 1; // minus '\0'
1671 // ----------------------------------------------
1674 static int SendTCPMsg_saddr_2part(
1675 char *msg
, int iLen
,
1676 char *msg2
, int iLen2
,
1677 char *result
, int resultSize
,
1678 struct sockaddr_in
*saHost
)
1681 struct sockaddr_in saSendTo
;
1690 struct timeval tv_start
;
1691 struct timeval tv_end
;
1692 struct timeval tv_elapsed
;
1694 int iContentLength
= -1;
1695 int iBodyOffset
= -1;
1697 gettimeofday(&tv_start
, NULL
);
1699 if (g_fUPnPEnabled
!= TRUE
) {
1700 //TracePrint(ELL_TRACE, "UPnP not enabled\n");
1701 if (g_fLogging
& NALOG_ERROR
)
1702 fprintf(g_log
, "UPnP not enabled (no UPnP device found yet)\n");
1703 return NA_E_NOT_AVAILABLE
;
1706 s
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
1708 if (g_fLogging
& NALOG_ERROR
)
1709 fprintf(g_log
, "Can't get TCP socket (%d)\n", errno
);
1714 if (setsockopt(s
, IPPROTO_IP
, TCP_NODELAY
, &fND
, sizeof(fND
)) != 0) {
1715 if (g_fLogging
& NALOG_ERROR
)
1716 fprintf(g_log
, "SendTCPMsg/2part: Can't set TCP_NODELAY option!\n");
1717 iRetcode
= NA_E_NET
;
1722 fcntl_flags
= fcntl(s
, F_GETFL
, 0);
1723 fcntl_flags
|= O_NONBLOCK
;
1724 if (fcntl(s
, F_SETFL
, fcntl_flags
) != 0) {
1725 if (g_fLogging
& NALOG_ERROR
)
1726 fprintf(g_log
, "SendTCPMsg/2part: Can't set O_NONBLOCK option!\n");
1727 iRetcode
= NA_E_NET
;
1732 memcpy(&saSendTo
, &g_saddrRouterDesc
, sizeof(saSendTo
));
1734 memcpy(&saSendTo
, saHost
, sizeof(saSendTo
));
1736 iRet
= connect(s
, (struct sockaddr
*) &saSendTo
, sizeof(saSendTo
));
1737 if ((iRet
< 0) && (errno
!= EINPROGRESS
)) {
1738 //TracePrint(ELL_TRACE, "UPnP connect failed\n");
1739 if (g_fLogging
& NALOG_ERROR
)
1740 fprintf(g_log
, "SendTCPMsg/2part: connect failed (%d)\n", errno
);
1741 iRetcode
= NA_E_NET
;
1745 if (g_fLogging
& NALOG_INFO1
)
1747 "- Before Sending TCP Msg1: %d == %lu?\n", iLen
, strlen(msg
));
1748 if (g_fLogging
& NALOG_DUMP
)
1749 fprintf(g_log
, "Sending TCP msg part 1:\n[%s]\n", msg
);
1751 tv
.tv_sec
= g_iFunctionTimeout
/ UPNP_TIMEOUT_GRANULARITY
;
1752 tv
.tv_usec
= (g_iFunctionTimeout
% U_TOGRAN
) * 1000000 / U_TOGRAN
;
1754 FD_SET(s
, &writefds
);
1755 iRet
= select(s
+1, 0, &writefds
, 0, &tv
);
1757 if (g_fLogging
& NALOG_ERROR
)
1758 fprintf(g_log
, "SendTCPMsg/2part: select failed (%d)\n", errno
);
1759 iRetcode
= NA_E_NET
;
1763 if (g_fLogging
& NALOG_ERROR
)
1764 fprintf(g_log
, "SendTCPMsg/2part: select timed out\n");
1765 iRetcode
= NA_E_TIMEOUT
;
1766 gettimeofday(&tv_end
, NULL
);
1767 GetTimeElapsed(&tv_start
, &tv_end
, &tv_elapsed
);
1768 //TracePrint(ELL_TRACE, "UPnP 2part: timeout @1st after %lu.%06lu secs\n",
1769 // tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1773 iRet
= send(s
, msg
, iLen
, 0);
1776 if (g_fLogging
& NALOG_ALERT
)
1777 fprintf(g_log
, "SendTCPMsg/2part: iRet(%d) != strlen(msg)(%d)!\n",
1780 //TracePrint(ELL_TRACE, "UPnP 2part: 1st %d == %d (%d) (%d)?\n", iRet, iLen, strlen(msg), errno);
1782 tv
.tv_sec
= g_iFunctionTimeout
/ UPNP_TIMEOUT_GRANULARITY
;
1783 tv
.tv_usec
= (g_iFunctionTimeout
% U_TOGRAN
) * 1000000 / U_TOGRAN
;
1785 FD_SET(s
, &writefds
);
1786 // calculate how much time elapsed
1787 gettimeofday(&tv_end
, NULL
);
1788 GetTimeElapsed(&tv_start
, &tv_end
, &tv_elapsed
);
1789 if (CompareTime(&tv_elapsed
, &tv
) > 0) {
1791 return NA_E_TIMEOUT
;
1796 // subtract that from timeout accordingly
1797 tv
.tv_sec
-= tv_elapsed
.tv_sec
;
1798 if (tv
.tv_usec
< tv_elapsed
.tv_usec
) {
1800 tv
.tv_usec
= 1000000 + tv
.tv_usec
- tv_elapsed
.tv_usec
;
1803 tv
.tv_usec
= tv
.tv_usec
- tv_elapsed
.tv_usec
;
1805 iRet
= select(s
+1, 0, &writefds
, 0, &tv
);
1807 if (g_fLogging
& NALOG_ERROR
)
1808 fprintf(g_log
, "SendTCPMsg/2part: select2 failed (%d)\n", errno
);
1809 iRetcode
= NA_E_NET
;
1813 if (g_fLogging
& NALOG_ERROR
)
1814 fprintf(g_log
, "SendTCPMsg/2part: select2 timed out\n");
1815 iRetcode
= NA_E_TIMEOUT
;
1816 gettimeofday(&tv_end
, NULL
);
1817 GetTimeElapsed(&tv_start
, &tv_end
, &tv_elapsed
);
1818 //TracePrint(ELL_TRACE, "UPnP 2part: timeout @2nd after %lu.%06lu secs\n",
1819 // tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1823 iRet
= send(s
, msg2
, iLen2
, 0);
1824 if (g_fLogging
& NALOG_INFO1
)
1826 "SendTCPMsg/parse: Before Sending TCP Msg2: %d == %lu?\n",
1827 iLen2
, strlen(msg2
));
1828 if (g_fLogging
& NALOG_DUMP
)
1829 fprintf(g_log
, "Sending TCP msg part 2:\n[%s]\n", msg2
);
1831 //TracePrint(ELL_TRACE, "UPnP 2part: 2nd %d == %d (%d) (%d)?\n", iRet, iLen2, strlen(msg2), errno);
1835 if (g_fLogging
& NALOG_ALERT
)
1836 fprintf(g_log
, "SendTCPMsg/2part: iRet(%d) != strlen(msg2)(%d)!\n",
1839 if (result
== NULL
) { // if caller just want to send/display msgs
1840 if (g_fLogging
& NALOG_DUMP
)
1841 fprintf(g_log
, "TCP Buffer: [");
1844 if (g_fLogging
& NALOG_INFO1
)
1845 fprintf(g_log
, "start recv @%lu\n", time(NULL
));
1848 iContentLength
= -1;
1852 struct timeval timeout
;
1856 FD_SET(s
, &readfds
);
1857 //timeout.tv_sec = g_iFunctionTimeout / U_TOGRAN;
1858 //timeout.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN;
1859 // just do flat 2 sec now, since connection already established
1861 timeout
.tv_usec
= 0;
1864 iRet
= select(s
+1, &readfds
, NULL
, NULL
, &timeout
);
1867 //TracePrint(ELL_TRACE, "UPnP 2part: select timeout? (%d, %d)\n",
1872 //gettimeofday(&tv_end, NULL);
1873 //GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed);
1874 //fprintf(stderr, "2 == loop: %d.%06d\n", tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1876 // if only sending messages
1877 if (result
== NULL
) {
1879 i
= recv(s
, t
, 1000-1, 0); // leave room for '\0' for dump
1881 if (g_fLogging
& NALOG_DUMP
) {
1883 fprintf(g_log
, "%s", t
);
1888 // EO result buf: discard extra bytes
1889 if (resultSize
<= iBufLen
) {
1891 i
= recv(s
, &t
, 1000, 0);
1893 // Note that there's no dump here - prevents DoS attack from
1894 // flooding the logs/diskspace
1898 i
= recv(s
, result
+ iBufLen
, resultSize
- iBufLen
, 0);
1900 //TracePrint(ELL_TRACE, "UPnP 2part: recv done %d (%d, %d)\n",
1901 // iBufLen, i, errno);
1907 // parse and see if we can find content-length to quit early
1908 iContentLength
= FindContentLength(result
, iBufLen
);
1910 // now if we're still in header, see if we can find body
1911 iBodyOffset
= FindBody(result
, iBufLen
);
1913 // now check if we can leave early. conditions are:
1914 // past headers, and we've already recv'ed content-length of body
1915 if ((iBodyOffset
>= 0) &&
1916 (iContentLength
>= 0) &&
1917 ((iBufLen
- iBodyOffset
) >= iContentLength
))
1919 //TracePrint(ELL_TRACE, "UPnP 2part: read all specified %d (%d, %d) (%d, %d)\n",
1920 // iBufLen, i, errno, iBodyOffset, iContentLength);
1925 //fprintf(stderr, "2 -- \n");
1927 if (g_fLogging
& NALOG_INFO1
)
1928 fprintf(g_log
, "done recv @%lu\n", time(NULL
));
1930 if (result
== NULL
) { // if caller just want to send/display msgs
1931 if (g_fLogging
& NALOG_DUMP
)
1932 fprintf(g_log
, "]\n");
1943 static int SendTCPMsg_saddr_parse(
1944 char *msg
, int iLen
,
1945 char *result
, int resultSize
,
1946 struct sockaddr_in
*saHost
)
1949 struct sockaddr_in saSendTo
;
1956 struct timeval tv_start
;
1957 // struct timeval tv_end;
1958 // struct timeval tv_elapsed;
1960 // HTTP parsing vars
1968 select(0, NULL
, NULL
, NULL
, &tv
);
1970 pthread_mutex_lock(&g_xUPnPMsg
);
1972 gettimeofday(&tv_start
, NULL
);
1974 if (g_fUPnPEnabled
!= TRUE
) {
1975 //TracePrint(ELL_TRACE, "UPnP not enabled\n");
1976 if (g_fLogging
& NALOG_ERROR
)
1977 fprintf(g_log
, "UPnP not enabled (no UPnP device found yet)\n");
1978 pthread_mutex_unlock(&g_xUPnPMsg
);
1979 return NA_E_NOT_AVAILABLE
;
1982 s
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
1984 if (g_fLogging
& NALOG_ERROR
)
1985 fprintf(g_log
, "Can't get TCP socket (%d)\n", errno
);
1986 pthread_mutex_unlock(&g_xUPnPMsg
);
1991 fcntl_flags
= fcntl(s
, F_GETFL
, 0);
1992 fcntl_flags
|= O_NONBLOCK
;
1993 if (fcntl(s
, F_SETFL
, fcntl_flags
) != 0) {
1994 if (g_fLogging
& NALOG_ERROR
)
1995 fprintf(g_log
, "SendTCPMsg/parse: Can't set O_NONBLOCK option!\n");
1997 pthread_mutex_unlock(&g_xUPnPMsg
);
2002 memcpy(&saSendTo
, &g_saddrRouterDesc
, sizeof(saSendTo
));
2004 memcpy(&saSendTo
, saHost
, sizeof(saSendTo
));
2006 iRet
= connect(s
, (struct sockaddr
*) &saSendTo
, sizeof(saSendTo
));
2007 if ((iRet
< 0) && (errno
!= EINPROGRESS
)) {
2008 //TracePrint(ELL_TRACE, "UPnP connect failed\n");
2009 if (g_fLogging
& NALOG_ERROR
)
2010 fprintf(g_log
, "SendTCPMsg/parse: connect failed (%d)\n", errno
);
2012 pthread_mutex_unlock(&g_xUPnPMsg
);
2016 if (g_fLogging
& NALOG_INFO1
)
2017 fprintf(g_log
, "SendTCPMsg/parse: Before Sending TCP Msg: %d == %lu?\n",
2019 if (g_fLogging
& NALOG_DUMP
)
2020 fprintf(g_log
,"Sending TCP msg:\n[%s]\n", msg
);
2022 tv
.tv_sec
= g_iFunctionTimeout
/ UPNP_TIMEOUT_GRANULARITY
;
2023 tv
.tv_usec
= (g_iFunctionTimeout
% U_TOGRAN
) * 1000000 / U_TOGRAN
;
2025 FD_SET(s
, &writefds
);
2026 iRet
= select(s
+1, 0, &writefds
, 0, &tv
);
2028 if (g_fLogging
& NALOG_ERROR
)
2029 fprintf(g_log
, "SendTCPMsg/parse: select failed (%d)\n", errno
);
2031 pthread_mutex_unlock(&g_xUPnPMsg
);
2035 if (g_fLogging
& NALOG_ERROR
)
2036 fprintf(g_log
, "SendTCPMsg/parse: select timed out\n");
2038 pthread_mutex_unlock(&g_xUPnPMsg
);
2039 return NA_E_TIMEOUT
;
2042 iRet
= send(s
, msg
, iLen
, 0);
2046 if (g_fLogging
& NALOG_ALERT
)
2047 fprintf(g_log
, "SendTCPMsg: iRet (%d) != strlen(msg) (%d)!\n",
2050 if (result
== NULL
) { // if caller just want to send/display msgs
2051 if (g_fLogging
& NALOG_DUMP
)
2052 fprintf(g_log
, "TCP Buffer: [");
2055 if (g_fLogging
& NALOG_INFO1
)
2056 fprintf(g_log
, "start recv @%lu\n", time(NULL
));
2060 iContentLength
= -1;
2064 struct timeval timeout
;
2068 FD_SET(s
, &readfds
);
2069 //timeout.tv_sec = g_iFunctionTimeout / U_TOGRAN;
2070 //timeout.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN;
2071 // just do flat 2 sec now, since connection already established
2073 timeout
.tv_usec
= 0;
2075 iRet
= select(s
+1, &readfds
, NULL
, NULL
, &timeout
);
2077 //fprintf(stderr, "**********: select failed (%d/%d)\n", iRet, errno);
2081 //gettimeofday(&tv_end, NULL);
2082 //GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed);
2083 //fprintf(stderr, "p == loop: %d.%06d\n", tv_elapsed.tv_sec, tv_elapsed.tv_usec);
2085 // if only sending messages
2086 if (result
== NULL
) {
2088 i
= recv(s
, t
, 1000-1, 0); // leave room for '\0' for dump
2090 if (g_fLogging
& NALOG_DUMP
) {
2092 fprintf(g_log
, "%s", t
);
2097 // EO result buf: discard extra bytes
2098 if (resultSize
<= iBufLen
) {
2100 i
= recv(s
, &t
, 1000, 0);
2102 // Note that there's no dump here - prevents DoS attack from
2103 // flooding the logs/diskspace
2107 i
= recv(s
, result
+ iBufLen
, resultSize
- iBufLen
, 0);
2113 if (EAGAIN
== errno
) continue;
2119 // parse and see if we can find content-length to quit early
2120 iContentLength
= FindContentLength(result
, iBufLen
);
2122 // now if we're still in header, see if we can find body
2123 iBodyOffset
= FindBody(result
, iBufLen
);
2127 //fprintf(stderr, "p -- \n");
2129 if (g_fLogging
& NALOG_INFO1
)
2130 fprintf(g_log
, "done recv @%lu\n", time(NULL
));
2132 if (result
== NULL
) { // if caller just want to send/display msgs
2133 if (g_fLogging
& NALOG_DUMP
)
2134 fprintf(g_log
, "]\n");
2138 pthread_mutex_unlock(&g_xUPnPMsg
);
2144 // szSOAPMsgControlAHeaderFMT - 4 args (ctrl_url, host/port, action, length)
2145 // szSOAPMsgControlABodyFMT - 2 args (action, args string)
2146 // szSOAPMsgControlAArgumentFMT - 2 args (name/value)
2147 static PHTTPResponse
SendSOAPMsgControlAction(
2153 //char outBuffer[65536];
2154 //char outBufferBody[65536];
2155 //char outBufferArgs[65536];
2156 char *outBuffer
= NULL
;
2157 char *outBufferBody
= NULL
;
2158 char *outBufferArgs
= NULL
;
2159 char *inBuffer
= NULL
;
2167 PHTTPResponse pResponse
= NULL
;
2170 if (!WaitUPnPFunction())
2173 if ((outBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2174 if (g_fLogging
& NALOG_ERROR
)
2175 fprintf(g_log
, "can't malloc for outBuffer\n");
2178 if ((outBufferBody
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2179 if (g_fLogging
& NALOG_ERROR
)
2180 fprintf(g_log
, "can't malloc for outBufferBody\n");
2183 if ((outBufferArgs
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2184 if (g_fLogging
& NALOG_ERROR
)
2185 fprintf(g_log
, "can't malloc for outBufferArgs\n");
2188 if ((inBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2189 if (g_fLogging
& NALOG_ERROR
)
2190 fprintf(g_log
, "can't malloc for inBuffer\n");
2196 for (i
=0; i
<argc
; i
++) {
2198 if (args
[i
].pszType
== NULL
) {
2199 n
= sprintf(outBufferArgs
+ iArgsLen
,
2200 szSOAPMsgControlAArgumentFMT
,
2201 args
[i
].pszName
, args
[i
].pszValue
);
2204 n
= sprintf(outBufferArgs
+ iArgsLen
,
2205 szSOAPMsgControlAArgumentFMT_t
,
2206 args
[i
].pszName
, args
[i
].pszValue
, args
[i
].pszType
);
2210 outBufferArgs
[iArgsLen
] = '\0';
2212 iBodyLen
= sprintf(outBufferBody
, szSOAPMsgControlABodyFMT
,
2213 action
, outBufferArgs
);
2215 iHeaderLen
= sprintf(outBuffer
, szSOAPMsgControlAHeaderFMT
,
2216 g_szControlURL
, g_szRouterHostPortSOAP
, action
, iBodyLen
);
2219 DumpHex(outBuffer
, iHeaderLen
+1);
2220 DumpHex(outBufferBody
, iBodyLen
+1);
2221 iResultLen
= SendTCPMsg_saddr_2part(
2222 outBuffer
, iHeaderLen
,
2223 outBufferBody
, iBodyLen
,
2224 inBuffer
, MAX_SOAPMSGSIZE
,
2225 &g_saddrRouterSOAP
);
2228 strcpy(outBuffer
+ iHeaderLen
, outBufferBody
);
2229 iLen
= iHeaderLen
+ iBodyLen
;
2231 DumpHex(outBuffer
, iLen
+1);
2233 //strcat(outBuffer, CRLF "0" CRLF CRLF);
2236 iResultLen
= SendTCPMsg_saddr_parse(
2238 inBuffer
, MAX_SOAPMSGSIZE
,
2239 &g_saddrRouterSOAP
);
2242 if (iResultLen
> 0) {
2243 if (iResultLen
> MAX_SOAPMSGSIZE
) {
2244 if (g_fLogging
& NALOG_ALERT
)
2245 fprintf(g_log
, "result truncated..\n");
2246 iResultLen
= MAX_SOAPMSGSIZE
;
2248 pResponse
= NewHTTPResponse_sz(inBuffer
, iResultLen
, FALSE
);
2249 if (pResponse
!= NULL
) {
2250 PrintHTTPResponse(pResponse
);
2251 //DeleteHTTPResponse(pResponse);
2252 // - return response to caller
2256 if (g_fLogging
& NALOG_ERROR
)
2257 fprintf(g_log
, "No TCP Response\n");
2258 //TracePrint(ELL_TRACE, "UPnP SendSOAPMsg got no TCP response (%d)\n",
2263 if (outBuffer
!= NULL
) free(outBuffer
);
2264 if (outBufferBody
!= NULL
) free(outBufferBody
);
2265 if (outBufferArgs
!= NULL
) free(outBufferArgs
);
2266 if (inBuffer
!= NULL
) free(inBuffer
);
2271 static int FindURLBase(char *pbuf
, int iLen
, char *szURLBase
)
2273 // non reusable XML parsing code:
2274 // ----------------------------------------------
2278 // now skip after end of this tag, then skip until controlURL tag
2279 p
= strstr_n(pbuf
, "<URLBase>", iLen
);
2280 if (p
== NULL
) return -1;
2282 // skip to the actual stuff
2283 p
+= sizeof("<URLBase>") - 1; // minus '\0'
2285 // skip white spaces (just in case)
2289 // copy into szURLBase
2290 while ((*p
!= '\0') && (*p
!= '<') && !isspace(*p
)) {
2291 if (i
++ > 1000) break;
2299 // ----------------------------------------------
2303 static int FindDescInfo(
2306 const char *szParentName
,
2315 // find the device within pbuf
2327 // now skip after end of this tag, then skip until manufacturer tag
2328 iSearchLen
= sprintf(szSearch
, "<%s>", szName
);
2329 p
= strstr_n(pbuf
, szSearch
, iLen
);
2330 if (p
== NULL
) return -1;
2333 // skip white spaces (just in case)
2337 // copy into szValue
2338 while ((*p
!= '\0') && (*p
!= '<')) {
2339 if (i
++ > 1000) break;
2349 static int FindIGDInfo(char *pbuf
, int iLen
, const char *szName
, char *szValue
)
2351 return FindDescInfo(
2353 "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
2357 static int FindManufacturer(char *pbuf
, int iLen
, char *szManuf
)
2359 return FindIGDInfo(pbuf
, iLen
, "manufacturer", szManuf
);
2362 static int FindFriendlyName(char *pbuf
, int iLen
, char *szValue
)
2364 return FindIGDInfo(pbuf
, iLen
, "friendlyName", szValue
);
2367 static int FindModelName(char *pbuf
, int iLen
, char *szValue
)
2369 return FindIGDInfo(pbuf
, iLen
, "modelName", szValue
);
2372 static int FindModelDescription(char *pbuf
, int iLen
, char *szValue
)
2374 return FindIGDInfo(pbuf
, iLen
, "modelDescription", szValue
);
2377 static int FindWANIPInfo(char *pbuf
, int iLen
, const char *szName
, char *szValue
)
2379 return FindDescInfo(
2381 "urn:schemas-upnp-org:service:WANIPConnection:1",
2385 static int FindControlURL(char *pbuf
, int iLen
, char *szControlURL
)
2387 return FindWANIPInfo(pbuf
, iLen
, "controlURL", szControlURL
);
2390 static int FindEventURL(char *pbuf
, int iLen
, char *szEventURL
)
2392 return FindWANIPInfo(pbuf
, iLen
, "eventSubURL", szEventURL
);
2395 static int FindRouterInfo(char *inBuffer
, int iLen
)
2397 if (FindManufacturer(inBuffer
, iLen
, g_szManufacturer
) != 0)
2398 g_szManufacturer
[0] = '\0';
2400 if (FindFriendlyName(inBuffer
, iLen
, g_szFriendlyName
) != 0)
2401 g_szFriendlyName
[0] = '\0';
2403 if (FindModelName(inBuffer
, iLen
, g_szModelName
) != 0)
2404 g_szModelName
[0] = '\0';
2406 if (FindModelDescription(inBuffer
, iLen
, g_szModelDescription
) != 0)
2407 g_szModelDescription
[0] = '\0';
2409 //TracePrint(ELL_TRACE,
2410 // "UPnP Router Info:\n"
2411 // " - manufacturer [%s]\n"
2412 // " - friendly name [%s]\n"
2413 // " - model name [%s]\n"
2414 // " - model desc [%s]\n",
2415 // g_szManufacturer, g_szFriendlyName, g_szModelName, g_szModelDescription);
2420 static void ParseURL(
2421 const char *szBuf
, char *pszHostPort
,
2422 struct sockaddr_in
*psaddr
, char *pszPath
)
2427 unsigned short port
;
2432 if (0 == strncmp(p
, "http://", 7))
2448 // find the port separetor
2454 // HTTP's by default port 80, so don't have it in the "Host:" header
2455 if (80 == port
) *q
= '\0';
2458 if (pszHostPort
) strcpy(pszHostPort
, p
);
2460 if (NULL
!= q
) *q
= '\0';
2462 if (NULL
!= psaddr
) {
2463 psaddr
->sin_family
= AF_INET
;
2464 psaddr
->sin_addr
.s_addr
= inet_addr(p
);
2465 psaddr
->sin_port
= htons(port
);
2468 //TracePrint(ELL_TRACE, "ParseURL [%s] -> [%s][%s] %lu.%lu.%lu.%lu:%u\n",
2470 pszHostPort
?pszHostPort
:"",
2472 (psaddr
->sin_addr
.s_addr
>> 24) & 0xff,
2473 (psaddr
->sin_addr
.s_addr
>> 16) & 0xff,
2474 (psaddr
->sin_addr
.s_addr
>> 8) & 0xff,
2475 (psaddr
->sin_addr
.s_addr
>> 0) & 0xff,
2480 static void GetDeviceDescription(void)
2482 char *outBuffer
= NULL
;
2483 char *inBuffer
= NULL
;
2486 char szURLBase
[1024];
2487 char szControlURL
[1024];
2488 char szEventURL
[1024];
2490 if (!g_fUPnPEnabled
) {
2491 if (g_fLogging
& NALOG_ERROR
)
2492 fprintf(g_log
, "GetDeviceDescription: upnp not enabled\n");
2496 if ((outBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2497 if (g_fLogging
& NALOG_ERROR
)
2498 fprintf(g_log
, "can't malloc for outBuffer\n");
2501 if ((inBuffer
= (char *) malloc(MAX_SOAPMSGSIZE
)) == NULL
) {
2502 if (g_fLogging
& NALOG_ERROR
)
2503 fprintf(g_log
, "can't malloc for inBuffer\n");
2507 iBufLen
= sprintf(outBuffer
, szSSDPMsgDescribeDeviceFMT
, g_szNATDevDescURL
,
2508 g_szRouterHostPortDesc
);
2510 if (g_fLogging
& NALOG_INFO1
)
2511 fprintf(g_log
, "Describe Device: [%s]\n", outBuffer
);
2512 iLen
= SendTCPMsg_saddr_parse(outBuffer
, iBufLen
, inBuffer
, MAX_SOAPMSGSIZE
,
2513 &g_saddrRouterDesc
);
2515 g_fControlURLSet
= FALSE
;
2517 if (FindControlURL(inBuffer
, iLen
, szControlURL
) != 0) {
2518 if (g_fLogging
& NALOG_ERROR
)
2519 fprintf(g_log
, "GetDeviceDesc: can't find control URL\n");
2523 // start modifying global
2524 pthread_mutex_lock(&g_xUPnP
);
2527 // now see if there's the URLBase
2528 if (FindURLBase(inBuffer
, iLen
, szURLBase
) != 0) {
2529 // not there? try default numbers from device description
2530 memcpy(&g_saddrRouterBase
, &g_saddrRouterDesc
,
2531 sizeof(g_saddrRouterBase
));
2532 strcpy(g_szRouterHostPortBase
, g_szRouterHostPortDesc
);
2536 g_szRouterHostPortBase
, &g_saddrRouterBase
, NULL
);
2538 if ((strlen(g_szRouterHostPortBase
) == 0) ||
2539 (g_saddrRouterBase
.sin_addr
.s_addr
== INADDR_NONE
)) {
2540 memcpy(&g_saddrRouterBase
, &g_saddrRouterDesc
,
2541 sizeof(g_saddrRouterBase
));
2542 strcpy(g_szRouterHostPortBase
, g_szRouterHostPortDesc
);
2547 ParseURL(szControlURL
,
2548 g_szRouterHostPortSOAP
, &g_saddrRouterSOAP
, g_szControlURL
);
2549 if ((strlen(g_szRouterHostPortSOAP
) == 0) ||
2550 (g_saddrRouterSOAP
.sin_addr
.s_addr
== INADDR_NONE
)) {
2551 memcpy(&g_saddrRouterSOAP
, &g_saddrRouterBase
,
2552 sizeof(g_saddrRouterSOAP
));
2553 strcpy(g_szRouterHostPortSOAP
, g_szRouterHostPortBase
);
2557 ////TracePrint(ELL_TRACE, "UPnP Control URL set to[%s][%s]...\n",
2558 // g_szRouterHostPortSOAP, g_szControlURL);
2560 g_fControlURLSet
= TRUE
;
2561 gettimeofday(&g_tvLastUpdateTime
, NULL
);
2562 pthread_cond_broadcast(&g_condUPnPControlURL
);
2564 if (g_fLogging
& NALOG_INFO1
)
2565 fprintf(g_log
, "Got Device Description\n");
2568 FindRouterInfo(inBuffer
, iLen
);
2570 if (FindEventURL(inBuffer
, iLen
, szEventURL
) != 0) {
2571 szEventURL
[0] = '\0';
2574 ParseURL(szEventURL
,
2575 g_szRouterHostPortEvent
, &g_saddrRouterEvent
, g_szEventURL
);
2576 if ((strlen(g_szRouterHostPortEvent
) == 0) ||
2577 (g_saddrRouterEvent
.sin_addr
.s_addr
== INADDR_NONE
)) {
2578 memcpy(&g_saddrRouterEvent
, &g_saddrRouterBase
,
2579 sizeof(g_saddrRouterEvent
));
2580 strcpy(g_szRouterHostPortEvent
, g_szRouterHostPortBase
);
2587 if (outBuffer
!= NULL
) free(outBuffer
);
2588 if (inBuffer
!= NULL
) free(inBuffer
);
2590 pthread_mutex_unlock(&g_xUPnP
);
2594 static void GetIPByName(char *hostname
, unsigned long *ip_ret
)
2598 ip
= inet_addr(hostname
);
2599 if (ip
== INADDR_NONE
) {
2600 struct hostent
*pHEnt
;
2601 pHEnt
= gethostbyname(hostname
);
2602 if (pHEnt
== NULL
) {
2603 if (g_fLogging
& NALOG_ALERT
)
2604 fprintf(g_log
, "Can't translate [%s] to IP...\n", hostname
);
2605 g_dwLocalIP
= htonl(INADDR_ANY
);
2608 ip
= ntohl(*(unsigned long *)(pHEnt
->h_addr
));
2609 if (g_fLogging
& NALOG_INFO1
)
2610 fprintf(g_log
, "hostname [%s] to ip: %ld.%ld.%ld.%ld\n",
2620 static void SetLocalIP()
2622 PIPINFO pIPInfo
= NULL
;
2623 int count
= GetIPInfo(&pIPInfo
);
2624 if (NULL
!= pIPInfo
)
2626 // choose first non IPV6 address
2627 // iterate through array and set port information
2629 unsigned long dwFirst
= 0;
2630 for(i
= 0; i
< count
; i
++)
2632 if (!(pIPInfo
[i
].iFlags
& ISIPV6
) &&
2633 (strncmp(pIPInfo
[i
].szIfName
, "ppp", 3) != 0))
2635 unsigned long dwTemp
;
2637 memcpy(&dwTemp
, pIPInfo
[i
].abIP
, sizeof(unsigned long));
2639 if (0 != GetNATIPNetmask(dwTemp
)) {
2640 g_dwLocalIP
= dwTemp
;
2649 g_dwLocalIP
= dwFirst
;
2650 FreeIPInfo(pIPInfo
);
2655 static int FindTagContent(const char *text
, const char *tagname
, char *buf
)
2659 p
= strstr(text
, tagname
);
2661 if (g_fLogging
& NALOG_INFO0
)
2662 fprintf(g_log
, "FindTagContent: can't find %s\n", tagname
);
2663 return NA_E_PARSE_ERROR
;
2666 if (sscanf(p
, "%*[^>]> %[^ <] <", buf
) < 1) {
2667 if (g_fLogging
& NALOG_INFO0
)
2668 fprintf(g_log
, "FindTagContent: Can't parse tag %s\n", tagname
);
2669 return NA_E_PARSE_ERROR
;
2672 return NA_E_SUCCESS
;
2675 mStatus
LNT_UnmapPort(mDNSIPPort PubPort
, mDNSBool tcp
)
2679 //char szRemoteHost[1024];
2680 //unsigned long dwIP;
2681 Property propArgs
[3];
2683 unsigned short port
= PubPort
.NotAnInteger
;
2684 int protocol
= tcp
? IPPROTO_TCP
: IPPROTO_UDP
;
2685 sprintf(szEPort
, "%u", port
);
2687 bzero(propArgs
, sizeof(propArgs
));
2688 propArgs
[0].pszName
= "NewRemoteHost";
2689 propArgs
[0].pszValue
= "";
2690 propArgs
[0].pszType
= "string";
2691 propArgs
[1].pszName
= "NewExternalPort";
2692 propArgs
[1].pszValue
= szEPort
;
2693 propArgs
[1].pszType
= "ui2";
2694 propArgs
[2].pszName
= "NewProtocol";
2695 if (protocol
== IPPROTO_TCP
) {
2696 propArgs
[2].pszValue
= "TCP";
2698 else if (protocol
== IPPROTO_UDP
) {
2699 propArgs
[2].pszValue
= "UDP";
2704 propArgs
[2].pszType
= "string";
2706 resp
= SendSOAPMsgControlAction(
2707 "DeletePortMapping", 3, propArgs
, FALSE
);
2709 return mStatus_NATTraversal
;
2712 if (strcmp(resp
->pszStatus
, "200") != 0) {
2713 DeleteHTTPResponse(resp
);
2714 return mStatus_NATTraversal
;
2717 DeleteHTTPResponse(resp
);
2718 return mStatus_NoError
;
2722 static int GetMappingUnused(unsigned short eport
, int protocol
);
2724 extern mStatus
LNT_MapPort(mDNSIPPort priv
, mDNSIPPort pub
, mDNSBool tcp
)
2731 Property propArgs
[8];
2733 unsigned short iport
= priv
.NotAnInteger
;
2734 unsigned short eport
= pub
.NotAnInteger
;
2735 int protocol
= tcp
? IPPROTO_TCP
: IPPROTO_UDP
;
2738 if (NA_E_EXISTS
== GetMappingUnused(eport
, protocol
))
2739 return mStatus_AlreadyRegistered
;
2741 //DeletePortMapping(eport, protocol);
2743 sprintf(szEPort
, "%u", eport
);
2745 sprintf(szIPort
, "%u", iport
);
2748 sprintf(szLocalIP
, "%u.%u.%u.%u",
2749 (unsigned int)((dwIP
>> 24) & 0xff),
2750 (unsigned int)((dwIP
>> 16) & 0xff),
2751 (unsigned int)((dwIP
>> 8) & 0xff),
2752 (unsigned int)((dwIP
>> 0) & 0xff));
2754 bzero(propArgs
, sizeof(propArgs
));
2755 propArgs
[0].pszName
= "NewRemoteHost";
2756 propArgs
[0].pszValue
= "";
2757 propArgs
[0].pszType
= "string";
2758 propArgs
[1].pszName
= "NewExternalPort";
2759 propArgs
[1].pszValue
= szEPort
;
2760 propArgs
[1].pszType
= "ui2";
2761 propArgs
[2].pszName
= "NewProtocol";
2762 if (protocol
== IPPROTO_TCP
) {
2763 propArgs
[2].pszValue
= "TCP";
2765 else if (protocol
== IPPROTO_UDP
) {
2766 propArgs
[2].pszValue
= "UDP";
2769 return mStatus_BadParamErr
;
2771 propArgs
[2].pszType
= "string";
2772 propArgs
[3].pszName
= "NewInternalPort";
2773 propArgs
[3].pszValue
= szIPort
;
2774 propArgs
[3].pszType
= "ui2";
2775 propArgs
[4].pszName
= "NewInternalClient";
2776 propArgs
[4].pszValue
= szLocalIP
;
2777 propArgs
[4].pszType
= "string";
2778 propArgs
[5].pszName
= "NewEnabled";
2779 propArgs
[5].pszValue
= "1";
2780 propArgs
[5].pszType
= "boolean";
2781 propArgs
[6].pszName
= "NewPortMappingDescription";
2782 sprintf(descr
, "iC%u", eport
);
2783 //propArgs[6].pszValue = "V";
2784 propArgs
[6].pszValue
= descr
;
2785 propArgs
[6].pszType
= "string";
2786 propArgs
[7].pszName
= "NewLeaseDuration";
2787 propArgs
[7].pszValue
= "0";
2788 propArgs
[7].pszType
= "ui4";
2790 resp
= SendSOAPMsgControlAction(
2791 "AddPortMapping", 8, propArgs
, FALSE
);
2794 return mStatus_NATTraversal
;
2797 if (strcmp(resp
->pszStatus
, "200") != 0) {
2798 DeleteHTTPResponse(resp
);
2799 return mStatus_NATTraversal
;
2802 DeleteHTTPResponse(resp
);
2803 return mStatus_NoError
;
2806 static int GetMappingUnused(unsigned short eport
, int protocol
)
2810 Property propArgs
[3];
2812 unsigned long ip
= 0;
2814 sprintf( szPort
, "%u", eport
);
2816 bzero(&propArgs
, sizeof(propArgs
));
2817 propArgs
[0].pszName
= "NewRemoteHost";
2818 propArgs
[0].pszValue
= "";
2819 propArgs
[0].pszType
= "string";
2820 propArgs
[1].pszName
= "NewExternalPort";
2821 propArgs
[1].pszValue
= szPort
;
2822 propArgs
[1].pszType
= "ui2";
2823 propArgs
[2].pszName
= "NewProtocol";
2824 if (protocol
== IPPROTO_TCP
) {
2825 propArgs
[2].pszValue
= "TCP";
2827 else if (protocol
== IPPROTO_UDP
) {
2828 propArgs
[2].pszValue
= "UDP";
2831 return NA_E_INVALID_PARAMETER
;
2833 propArgs
[2].pszType
= "string";
2835 resp
= SendSOAPMsgControlAction(
2836 "GetSpecificPortMappingEntry", 3, propArgs
, FALSE
);
2838 if ((strcmp(resp
->pszStatus
, "200") == 0) &&
2839 (FindTagContent(resp
->pszBody
, "NewInternalClient", buf
) == 0))
2841 GetIPByName(buf
, &ip
);
2842 if (ip
== g_dwLocalIP
) {
2843 // (perhaps we let it go?)
2844 DeleteHTTPResponse(resp
);
2845 return NA_E_SUCCESS
;
2848 DeleteHTTPResponse(resp
);
2852 DeleteHTTPResponse(resp
);
2855 return NA_E_SUCCESS
;
2858 mStatus
LNT_GetPublicIP(mDNSOpaque32
*IpPtr
)
2862 static struct timeval tvLastGoodIP
= {0,0};
2863 static unsigned long dwLastGoodIP
;
2865 unsigned long *ip
= (unsigned long *)IpPtr
;
2866 if (ip
== NULL
) return mStatus_BadParamErr
;
2868 gettimeofday(&tv
, NULL
);
2869 GetTimeElapsed(&tvLastGoodIP
, &tv
, &tv
);
2872 return dwLastGoodIP
;
2875 resp
= SendSOAPMsgControlAction(
2876 "GetExternalIPAddress", 0, NULL
, FALSE
);
2879 return mStatus_NATTraversal
;
2881 if (FindTagContent(resp
->pszBody
, "NewExternalIPAddress", buf
) == 0) {
2882 if (g_fLogging
& NALOG_INFO1
)
2883 fprintf(g_log
, "Mapped remote host = %s\n", buf
);
2884 *ip
= inet_addr(buf
);
2885 DeleteHTTPResponse(resp
);
2887 gettimeofday(&tvLastGoodIP
, NULL
);
2890 return mStatus_NoError
;
2893 DeleteHTTPResponse(resp
);
2894 return mStatus_NATTraversal
;
2897 static void SendDiscoveryMsg()
2899 // do it twice to avoid lost packet
2900 //SendUDPMsg(szSSDPMsgDiscoverNAT);
2901 SendUDPMsg(szSSDPMsgDiscoverRoot
);
2902 SendUDPMsg(szSSDPMsgDiscoverIGD
);
2903 SendUDPMsg(szSSDPMsgDiscoverNAT
);
2906 // Set up threads for upnp responses, etc.
2907 int LegacyNATInit(void)
2909 //pthread_t UDPthread;
2910 pthread_attr_t attr
;
2912 //struct timeval tv;
2914 static int fFirstInitLocks
= TRUE
;
2924 if (fFirstInitLocks
)
2927 if (pthread_mutex_init(&g_xUPnP
, NULL
)) {
2928 if (g_fLogging
& NALOG_ERROR
)
2929 fprintf(log
, "UpnpInit - mutex init failed\n");
2930 return NA_E_INTERNAL_ERROR
;
2932 if (pthread_cond_init(&g_condUPnP
, NULL
)) {
2933 pthread_mutex_destroy(&g_xUPnP
);
2934 if (g_fLogging
& NALOG_ERROR
)
2935 fprintf(log
, "UpnpInit - cond init failed\n");
2936 return NA_E_INTERNAL_ERROR
;
2938 if (pthread_cond_init(&g_condUPnPControlURL
, NULL
)) {
2939 pthread_mutex_destroy(&g_xUPnP
);
2940 pthread_cond_destroy(&g_condUPnP
);
2941 if (g_fLogging
& NALOG_ERROR
)
2942 fprintf(log
, "UpnpInit - cond init failed\n");
2943 return NA_E_INTERNAL_ERROR
;
2945 if (pthread_mutex_init(&g_xUPnPMsg
, NULL
)) {
2946 pthread_mutex_destroy(&g_xUPnP
);
2947 pthread_cond_destroy(&g_condUPnP
);
2948 pthread_cond_destroy(&g_condUPnPControlURL
);
2949 if (g_fLogging
& NALOG_ERROR
)
2950 fprintf(log
, "UpnpInit - mutex init failed\n");
2951 return NA_E_INTERNAL_ERROR
;
2954 fFirstInitLocks
= FALSE
;
2959 // initialize UDP socket for SSDP
2960 g_sUDP
= SSDPListen();
2961 g_sUDPCancel
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
); // sock to signal canccelation to UDP thread
2962 if (g_sUDP
< 0 || g_sUDPCancel
< 0) {
2963 if (g_fLogging
& NALOG_ERROR
)
2964 fprintf(log
, "UpnpInit - Failed to init multicast socket.\n");
2965 return NA_E_INTERNAL_ERROR
;
2969 pthread_attr_init(&attr
);
2970 iRet
= pthread_create(&g_UDPthread
, &attr
, UDPProc
, log
);
2972 g_fFirstInit
= TRUE
; // so we'll redo this part next time
2975 if (g_fLogging
& NALOG_ERROR
)
2976 fprintf(log
, "UpnpInit - pthread create failed (%d)\n", iRet
);
2977 return NA_E_THREAD_ERROR
;
2980 // set this to FALSE only if first call succeeded
2981 g_fFirstInit
= FALSE
;
2983 //TracePrint(ELL_TRACE, "UPnP init passed\n");
2986 //tv.tv_usec = 20000; // wait 20ms for thread/udp/multicast init
2987 //select(0, 0, 0, 0, &tv);
2990 // send discovery message
2993 return NA_E_SUCCESS
;
2996 int LegacyNATDestroy()
2998 void *UDPThreadRetVal
;
3000 if (g_sTCPCancel
>= 0) close(g_sTCPCancel
);
3001 if (g_sUDPCancel
>= 0) close(g_sUDPCancel
);
3002 pthread_join(g_UDPthread
, &UDPThreadRetVal
);
3005 g_fFirstInit
= TRUE
;
3006 g_fUPnPEnabled
= FALSE
;
3007 g_fControlURLSet
= FALSE
;
3008 return NA_E_SUCCESS
;