]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/LegacyNATTraversal.c
mDNSResponder-107.4.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / LegacyNATTraversal.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
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
12 * file.
13 *
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.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23
24 Change History (most recent first):
25
26 $Log: LegacyNATTraversal.c,v $
27 Revision 1.12 2005/07/22 21:36:16 ksekar
28 Fix GCC 4.0/Intel compiler warnings
29
30 Revision 1.11 2004/12/03 03:34:20 ksekar
31 <rdar://problem/3882674> LegacyNATTraversal.c leaks threads
32
33 Revision 1.10 2004/12/01 02:43:49 cheshire
34 Update copyright message
35
36 Revision 1.9 2004/10/27 02:25:05 cheshire
37 <rdar://problem/3816029> Random memory smashing bug
38
39 Revision 1.8 2004/10/27 02:17:21 cheshire
40 Turn off "safe_close: ERROR" error messages -- there are too many of them
41
42 Revision 1.7 2004/10/26 21:15:40 cheshire
43 <rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0
44 Additional fixes: Code should set fds to -1 after closing sockets.
45
46 Revision 1.6 2004/10/26 20:59:20 cheshire
47 <rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0
48
49 Revision 1.5 2004/10/26 01:01:35 cheshire
50 Use "#if 0" instead of commenting out code
51
52 Revision 1.4 2004/10/10 06:51:36 cheshire
53 Declared some strings "const" as appropriate
54
55 Revision 1.3 2004/09/21 23:40:12 ksekar
56 <rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure
57
58 Revision 1.2 2004/09/17 01:08:52 cheshire
59 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
60 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
61 declared in that file are ONLY appropriate to single-address-space embedded applications.
62 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
63
64 Revision 1.1 2004/08/18 17:35:41 ksekar
65 <rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways
66
67
68 */
69
70 #include "mDNSEmbeddedAPI.h"
71 #include "mDNSMacOSX.h"
72 #include <stdio.h>
73 #include <stdlib.h>
74 #include <unistd.h>
75 #include <string.h>
76 #include <errno.h>
77 #include <netdb.h>
78 #include <fcntl.h>
79 #include <pthread.h>
80 #include <sched.h>
81 #include <time.h>
82 #include <sys/time.h>
83 #include <sys/types.h>
84 #include <sys/socket.h>
85 #include <netinet/in.h>
86 #include <netinet/tcp.h>
87 #include <errno.h>
88
89 #include <sys/ioctl.h>
90 #include <net/if.h>
91 #include <netinet/in.h>
92 #include <arpa/inet.h>
93 #include <sys/sysctl.h>
94 #include <net/route.h>
95 #include "memory.h"
96 #include <ctype.h>
97 #include <arpa/inet.h>
98
99 //#include "IPAddr.h"
100 //#include "upnp.h"
101 //#include "debug.h"
102
103 // use error codes
104 //#include "netaddr.h"
105
106 // TODO: remove later and do variable length
107 #define MAX_SOAPMSGSIZE 65536
108
109 static int safe_close(int fd)
110 {
111 if (fd < 3) { /* LogMsg("safe_close: ERROR sd %d < 3", fd); */ return(-1); }
112 return(close(fd));
113 }
114
115 #define close safe_close
116
117 ////////////////////////////////////////////////////////////////////////
118 // NetAddr Functions
119 ////////////////////////////////////////////////////////////////////////
120
121 // Return codes
122 #define NA_E_SUCCESS (0)
123 #define NA_E_INTERNAL_ERROR (-1) /* somewhere something wrong */
124 #define NA_E_INVALID_PARAMETER (-2) /* bad params */
125 #define NA_E_OPERATION_FAILED (-3) /* can't fulfill request */
126 #define NA_E_TIMEOUT (-4) /* operation timed out */
127 #define NA_E_THREAD_ERROR (-5) /* some error related to threads */
128 #define NA_E_PARSE_ERROR (-6) /* a parsing error occured */
129 #define NA_E_NOT_READY (-7) /* this op can't proceed yet */
130 #define NA_E_NOT_FOUND (-8) /* resource/prereq not found */
131 #define NA_E_NOT_AVAILABLE (-9) /* service not available */
132 #define NA_E_EXISTS (-10) /* can't modify existing item */
133 #define NA_E_AGAIN (-11) /* something wrong - try again */
134 #define NA_E_NOT_SUPPORTED (-12) /* wait until next version */
135 #define NA_E_ABORT (-14) /* operation aborted */
136 #define NA_E_NET (-15) /* network layer problem */
137
138 // Logging flags - log types (increasing degree of detail)
139 #define NALOG_ERROR (1UL) /* error messages */
140 #define NALOG_ALERT (2UL) /* useful warning/alerts */
141 #define NALOG_INFO0 (4UL) /* info - potential problem */
142 #define NALOG_INFO1 (8UL) /* extra info */
143 #define NALOG_DUMP (16UL) /* data dumps */
144
145 #define NALOG_RSRV1 (32UL) /* reserved */
146 #define NALOG_RSRV2 (64UL) /* reserved */
147 #define NALOG_RSRV3 (128UL) /* reserved */
148
149 // Logging flags - component (not used for now)
150 #define NALOG_UPNP (256) /* UPnP */
151
152 // Default Logging levels
153 #define NALOG_LEVEL0 (0)
154 #define NALOG_LEVEL1 (NALOG_UPNP | NALOG_ERROR)
155 #define NALOG_LEVEL2 (NALOG_LEVEL1 | NALOG_ALERT)
156 #define NALOG_LEVEL3 (NALOG_LEVEL2 | NALOG_INFO0)
157 #define NALOG_LEVEL4 (NALOG_LEVEL3 | NALOG_INFO1)
158 #define NALOG_LEVEL5 (NALOG_LEVEL4 | NALOG_DUMP)
159 #define NALOG_DEFAULT_LEVEL (NALOG_LEVEL2)
160
161 // Default timeout values (in m-seconds (milli))
162 // 50 milliseconds for function timeout
163 #define NA_DEFAULT_FUNCTION_TIMEOUT (50)
164
165 ////////////////////////////////////////////////////////////////////////
166 // GLOBAL Defines
167 ////////////////////////////////////////////////////////////////////////
168 #define SSDP_IP "239.255.255.250"
169 #define SSDP_PORT 1900
170 #define SSDP_TTL 4
171
172 #define CRLF "\r\n"
173 #define H_CRLF "\r\n"
174 // SOAP message's CRLF:
175 //#define S_CRLF "\r\n"
176 #define S_CRLF
177
178 // standard 200 ok msg
179 #define HTTP200OK "HTTP/1.1 200 OK\r\n\r\n"
180 #define HTTP200OKLEN (sizeof(HTTP200OK) - 1)
181
182 // maximum time to wait for an event (in microseconds)
183 #define MAX_EXPECTEVENTTIME (10000)
184
185 ////////////////////////////////////////////////////////////////////////
186 // GLOBAL Data Types
187 ////////////////////////////////////////////////////////////////////////
188 typedef struct tagProperty {
189 char *pszName;
190 char *pszValue;
191 char *pszType;
192 } Property, *PProperty;
193
194 typedef struct tagHTTPResponse {
195 char *pszStatus;
196 char *pszReason;
197 int iNumHeaders;
198 Property aHeaders[30]; // assume at most this many headers
199 char *pszBody;
200
201 // for admin use
202 int fFree;
203 char *buf;
204 } HTTPResponse, *PHTTPResponse, **PPHTTPResponse;
205
206 ////////////////////////////////////////////////////////////////////////
207 // GLOBAL Constants
208 ////////////////////////////////////////////////////////////////////////
209 static const char szSSDPMsgDiscoverRoot[] =
210 "M-SEARCH * HTTP/1.1\r\n"
211 "Host:239.255.255.250:1900\r\n"
212 "ST:upnp:rootdevice\r\n"
213 "Man:\"ssdp:discover\"\r\n"
214 "MX:3\r\n"
215 "\r\n";
216
217 static const char szSSDPMsgDiscoverIGD[] =
218 "M-SEARCH * HTTP/1.1\r\n"
219 "Host:239.255.255.250:1900\r\n"
220 "ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
221 "Man:\"ssdp:discover\"\r\n"
222 "MX:3\r\n"
223 "\r\n";
224
225 static const char szSSDPMsgDiscoverNAT[] =
226 "M-SEARCH * HTTP/1.1\r\n"
227 "Host:239.255.255.250:1900\r\n"
228 "ST:urn:schemas-upnp-org:service:WANIPConnection:1\r\n"
229 "Man:\"ssdp:discover\"\r\n"
230 "MX:3\r\n"
231 "\r\n";
232
233 //// Subscribe message
234 // 1$s: control URL
235 // 2$s: local's host/port ("host:port")
236 // 3$s: router's host/port ("host:port")
237 // 4$d: subscription timeout in seconds
238 static const char szEventMsgSubscribeFMT[] =
239 "SUBSCRIBE %1$s HTTP/1.1\r\n"
240 "NT: upnp:event\r\n"
241 "Callback: <http://%2$s/notify>\r\n"
242 "Timeout: Second-%4$d\r\n"
243 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
244 "Host: %3$s\r\n"
245 "Content-Length: 0\r\n"
246 "Pragma: no-cache\r\n"
247 "\r\n";
248
249 //// Unsubscribe message
250 // 1$s: control URL
251 // 2$s: SID (some uuid passed back during subscribe)
252 // 3$s: router's host ("host")
253 #if 0
254 static const char szEventMsgUnsubscribeFMT[] =
255 "UNSUBSCRIBE %1$s HTTP/1.1\r\n"
256 "SID: %2$s\r\n"
257 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
258 "Host: %3$s\r\n"
259 "Content-Length: 0\r\n"
260 "Pragma: no-cache\r\n"
261 "\r\n";
262 #endif
263
264 //// Generic SOAP Control:Action request messages
265 // 1$s: control URL
266 // 2$s: router's host/port ("host:port")
267 // 3$s: action (string)
268 // 4$d: content-length
269 static const char szSOAPMsgControlAHeaderFMT[] =
270 //"M-POST %1$s HTTP/1.1\r\n"
271 "POST %1$s HTTP/1.1\r\n"
272 "Content-Type: text/xml; charset=\"utf-8\"\r\n"
273 //"TEST: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
274 //"Man: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
275 //"01-SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#%3$s\"\r\n"
276 "SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#%3$s\"\r\n"
277 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n"
278 "Host: %2$s\r\n"
279 "Content-Length: %4$d\r\n"
280 "Connection: close\r\n"
281 // "Connection: Keep-Alive\r\n"
282 "Pragma: no-cache\r\n"
283 "\r\n";
284
285 // 1$: action (string)
286 // 2$: argument list
287 static const char szSOAPMsgControlABodyFMT[] =
288 "<?xml version=\"1.0\"?>" CRLF
289 "<SOAP-ENV:Envelope" S_CRLF
290 " xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"" S_CRLF
291 " SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" S_CRLF
292 "<SOAP-ENV:Body>" S_CRLF
293 "<m:%1$s" S_CRLF
294 " xmlns:m=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" S_CRLF
295 "%2$s"
296 "</m:%1$s>" S_CRLF
297 "</SOAP-ENV:Body>" S_CRLF
298 "</SOAP-ENV:Envelope>" S_CRLF
299 // CRLF
300 // "0"
301 // CRLF
302 CRLF;
303
304 // 1$: argument name
305 // 2$: argument value
306 static const char szSOAPMsgControlAArgumentFMT[] =
307 "<%1$s>%2$s</%1$s>" S_CRLF;
308
309 // 1$: argument name
310 // 2$: argument value
311 // 3$: argument type
312 static const char szSOAPMsgControlAArgumentFMT_t[] =
313 "<%1$s"
314 " xmlns:dt=\"urn:schemas-microsoft-com:datatypes\""
315 " dt:dt=\"%3$s\">%2$s</%1$s>" S_CRLF;
316
317 #if 0
318 //// Generic SOAP Control:Query request messages
319 // 1$s: control URL
320 // 2$s: router's host/port ("host:port")
321 // 3$d: content-length
322 static const char szSOAPMsgControlQHeaderFMT[] =
323 "M-POST %1$s HTTP/1.1\r\n"
324 //"POST %1$s HTTP/1.1\r\n"
325 "Host: %2$s\r\n"
326 "Content-Length: %3$d\r\n"
327 "Content-Type: text/xml; charset-\"utf-8\"\r\n"
328 //"Man: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
329 //"SOAPAction: \"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"\r\n"
330 "01-SOAPAction: \"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"\r\n"
331 "\r\n";
332
333 // 1$: variable name
334 static const char szSOAPMsgControlQBodyFMT[] =
335 "<s:Envelope" S_CRLF
336 " xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"" S_CRLF
337 " s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" S_CRLF
338 "<s:Body>" S_CRLF
339 "<u:QueryStateVariable xmlns:u=\"urn:schemas-upnp-org:control-1-0\"" S_CRLF
340 "<u:varName>%s</u:varName>" S_CRLF
341 "</u:QueryStateVariable>" S_CRLF
342 "</s:Body>" S_CRLF
343 "</s:Envelope>" S_CRLF
344 "" S_CRLF;
345 #endif
346 // 1$: device description URL
347 // 2$: host/port
348 static const char szSSDPMsgDescribeDeviceFMT[] =
349 "GET %s HTTP/1.1\r\n"
350 "Accept: text/xml, application/xml\r\n"
351 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
352 "Host: %s\r\n"
353 "Connection: close\r\n"
354 // "Connection: Keep-Alive\r\n"
355 "\r\n";
356
357 ////////////////////////////////////////////////////////////////////////
358 // GLOBAL Variables
359 ////////////////////////////////////////////////////////////////////////
360
361 static int g_fFirstInit = TRUE;
362 static int g_fQuit = FALSE;
363 static FILE *g_log;
364 static int g_fLogging;
365
366 // Globally-accessible UDP socket
367 static int g_sUDP = -1;
368 static int g_sUDPCancel = -1;
369
370 // Globally-accessible TCP socket
371 static int g_sTCP = -1;
372 static int g_sTCPCancel = -1;
373
374 // Event Vars
375 static int g_fEventEnabled = FALSE;
376 static unsigned short g_wEventPort;
377 static struct sockaddr_in g_saddrRouterEvent;
378 static char g_szRouterHostPortEvent[1024];
379 static char g_szEventURL[1024];
380
381 // UPnP Router info
382 static char g_szFriendlyName[1024];
383 static char g_szManufacturer[1024];
384 static char g_szModelName[1024];
385 static char g_szModelDescription[1024];
386
387 // URL base
388 static struct sockaddr_in g_saddrRouterBase;
389 static char g_szRouterHostPortBase[1024];
390
391 // the threads
392 static pthread_t g_UDPthread = NULL;
393 static pthread_t g_TCPthread = NULL;
394
395 // Local IP
396 static unsigned long g_dwLocalIP = 0;
397
398 // Globally accessible info about the router/UPnP
399 static int g_fUPnPEnabled = FALSE;
400 static char g_szUSN[1024];
401
402 static struct sockaddr_in g_saddrRouterDesc;
403 static char g_szRouterHostPortDesc[1024];
404 static char g_szNATDevDescURL[1024];
405
406 static struct sockaddr_in g_saddrRouterSOAP;
407 static char g_szRouterHostPortSOAP[1024];
408 static char g_szControlURL[1024];
409 static int g_fControlURLSet = FALSE;
410
411 // Lock/condvar for synchronous upnp calls
412 static pthread_mutex_t g_xUPnP;
413 static pthread_mutex_t g_xUPnPMsg;
414 static pthread_cond_t g_condUPnP;
415 static pthread_cond_t g_condUPnPControlURL;
416 static struct timeval g_tvUPnPInitTime;
417 static struct timeval g_tvLastUpdateTime;
418
419 // timeout values in seconds
420 static int g_iFunctionTimeout = NA_DEFAULT_FUNCTION_TIMEOUT;
421
422 static void GetDeviceDescription(void);
423 static void SetLocalIP(void);
424
425 ////////////////////////////////////////////////////////////////////////
426 // IPAddr Functions
427 ////////////////////////////////////////////////////////////////////////
428
429
430 #define ISIPV6 0x01
431 #define ISPPP 0x02
432 #define IFNAMELEN 16 /* Interface Name Length */
433 #define IPLEN 16 /* 16 bytes(128 bits) for IPv6 */
434
435 typedef struct tagIPINFO
436 {
437 int iFlags;
438 char szIfName[IFNAMELEN]; /* Interface name */
439 unsigned char abIP[IPLEN]; /* IP in host byte order */
440 unsigned short wPort;
441 } IPINFO, *PIPINFO, **PPIPINFO;
442
443 typedef struct hostent HOSTENT, *PHOSTENT;
444
445 static unsigned long GetNATIPNetmask(unsigned long dwIP)
446 {
447 if ((dwIP & 0xFF000000) == 0x0A000000) return 0xFF000000;
448 if ((dwIP & 0xFFF00000) == 0xAC100000) return 0xFFF00000;
449 if ((dwIP & 0xFFFF0000) == 0xC0a80000) return 0xFFFF0000;
450
451 return 0; /* No NAT IP */
452 }
453
454 static int GetIPInfo(PPIPINFO ppIPInfo)
455 {
456 int fd;
457 int iLastLen, iLen, iNum = 0, iMax = 0;
458 unsigned long dwIP;
459 char *pcBuf, *pcTemp;
460 PIPINFO pIPInfo = NULL;
461 struct ifconf ifc;
462 struct ifreq *ifr, ifrcopy;
463
464 if (ppIPInfo == NULL) return 0;
465
466 fd = socket(AF_INET, SOCK_DGRAM, 0);
467
468 iLastLen = -1;
469 iLen = 100 * sizeof(struct ifreq);
470
471 for (;;)
472 {
473 pcBuf = (char *)malloc(iLen);
474 ifc.ifc_len = iLen;
475 ifc.ifc_buf = pcBuf;
476
477 if (ioctl(fd, SIOCGIFCONF, &ifc) < 0)
478 {
479 if (errno != EINVAL || iLastLen != -1)
480 {
481 // DbgPrint(ELL_ERROR, "ioctl failed(%d)\n", errno);
482 free(pcBuf);
483 close(fd);
484 return 0;
485 }
486 }
487 else
488 {
489 if (ifc.ifc_len == iLastLen) break;
490 iLastLen = ifc.ifc_len;
491 }
492
493 iLen += 10 * sizeof(struct ifreq);
494 free(pcBuf);
495 }
496
497 for (pcTemp = pcBuf; pcTemp < pcBuf + ifc.ifc_len; )
498 {
499 if (iNum >= iMax)
500 {
501 PIPINFO pIPInfoNew;
502
503 iMax += 10;
504 pIPInfoNew = (PIPINFO)realloc(pIPInfo, sizeof(IPINFO) * iMax);
505 if (pIPInfoNew == NULL)
506 {
507 free(pIPInfo);
508 free(pcBuf);
509 close(fd);
510 return 0;
511 }
512 else pIPInfo = pIPInfoNew;
513
514 memset(pIPInfo + (iMax - 10), 0, sizeof(IPINFO) * 10);
515 }
516
517 ifr = (struct ifreq *)pcTemp;
518
519 pcTemp += sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len;
520
521 /* discard invalid address families & loopback */
522 if ((ifr->ifr_addr.sa_family != AF_INET &&
523 ifr->ifr_addr.sa_family != AF_INET6) ||
524 strncmp(ifr->ifr_name, "lo", 2) == 0) continue;
525
526 ifrcopy = *ifr;
527 ioctl(fd, SIOCGIFFLAGS, &ifrcopy);
528 if ((ifrcopy.ifr_flags & IFF_UP) == 0) continue;
529
530 switch (ifr->ifr_addr.sa_family)
531 {
532 case AF_INET:
533 memcpy(pIPInfo[iNum].szIfName, ifr->ifr_name, IFNAMELEN);
534 dwIP =
535 ntohl(((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr);
536 memcpy(pIPInfo[iNum].abIP, &dwIP, sizeof(unsigned long));
537 if (ifrcopy.ifr_flags & IFF_POINTOPOINT)
538 pIPInfo[iNum].iFlags |= ISPPP;
539 iNum++;
540 break;
541
542 case AF_INET6:
543 memcpy(pIPInfo[iNum].szIfName, ifr->ifr_name, IFNAMELEN);
544 memcpy(pIPInfo[iNum].abIP,
545 ((struct sockaddr_in6 *)&(ifr->ifr_addr))-> sin6_addr.s6_addr,
546 16);
547 pIPInfo[iNum].iFlags |= ISIPV6;
548 if (ifrcopy.ifr_flags & IFF_POINTOPOINT)
549 pIPInfo[iNum].iFlags |= ISPPP;
550 iNum++;
551 break;
552
553 default:
554 break;
555 }
556 }
557
558 free(pcBuf);
559 close(fd);
560
561 *ppIPInfo = pIPInfo;
562
563 return iNum;
564 }
565
566 static void FreeIPInfo(PIPINFO pIPInfo)
567 {
568 if (pIPInfo != NULL) free(pIPInfo);
569 }
570
571
572 ////////////////////////////////////////////////////////////////////////
573 // Function Definitions
574 ////////////////////////////////////////////////////////////////////////
575
576 static void SendDiscoveryMsg();
577
578 // SSDPListen
579 // Creates a UDP multicast socket and listens to the SSDP IP/PORT
580 // Returns
581 // -1 on error, or the socket descriptor if success
582 static int SSDPListen()
583 {
584 char fLoop;
585 int iTTL;
586 struct ip_mreq mreq;
587 struct sockaddr_in saddr;
588 int sd;
589
590 // IPPROTO_IP == 0; IPPROTO_TCP == 6; IPPROTO_UDP == 17; etc.
591 sd = socket(AF_INET, SOCK_DGRAM, 0);
592 if (sd == -1) {
593 if (g_fLogging & NALOG_ERROR)
594 fprintf(g_log, "Can't create socket! SSDPListen exiting\n");
595 return NA_E_NET;
596 }
597
598 // sock options values
599 fLoop = 0; // false - don't send copy to self
600 iTTL = SSDP_TTL;
601
602 // bind to listen to ssdp multicast address
603 bzero(&saddr, sizeof(saddr));
604 saddr.sin_len = sizeof(saddr);
605 saddr.sin_family = AF_INET;
606 //saddr.sin_addr.s_addr = inet_addr(SSDP_IP);
607 //saddr.sin_port = htons(SSDP_PORT);
608 saddr.sin_addr.s_addr = htonl(g_dwLocalIP);
609 saddr.sin_port = 0;
610
611 // and set the multicast add_member structure
612 // (TODO: need to find interfaces later - ioctl, with:
613 // SIOCFIFCONF to find if's, SIOCGIFADDR to get addr, and SIOCFIFFLAGS
614 // to check for IFF_MULTICAST flag for multicast support on an if)
615 bzero(&mreq, sizeof(mreq));
616 mreq.imr_interface.s_addr = g_dwLocalIP;
617 mreq.imr_multiaddr.s_addr = inet_addr(SSDP_IP);
618
619 if (
620 bind(sd, (struct sockaddr *)&saddr, sizeof(saddr)) //||
621 //setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &fLoop, sizeof(fLoop)) ||
622 //setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &iTTL, sizeof(iTTL)) ||
623 //setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))
624 ) {
625 if (g_fLogging & NALOG_ERROR)
626 fprintf(g_log,
627 "bind/setsockopt for multicast failed... errno = %d\n", errno);
628 close(sd);
629 return NA_E_NET;
630 }
631
632 return sd;
633 }
634
635 static int EventListen()
636 {
637 struct sockaddr_in saddr;
638 int sd;
639
640 // try 5 ports before failing completely
641 for (g_wEventPort = 5000; g_wEventPort < 5005; g_wEventPort++)
642 {
643 sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
644 if (sd == -1) {
645 if (g_fLogging & NALOG_ERROR)
646 fprintf(g_log, "Can't create socket! EventListen exiting\n");
647 return NA_E_NET;
648 }
649
650 bzero(&saddr, sizeof(saddr));
651 saddr.sin_len = sizeof(saddr);
652 saddr.sin_family = AF_INET;
653 saddr.sin_addr.s_addr = htonl(g_dwLocalIP);
654 saddr.sin_port = htons(g_wEventPort);
655
656 // return if okay
657 if (bind(sd, (struct sockaddr *)&saddr, sizeof(saddr)) == 0)
658 {
659 listen(sd, 128);
660 ////TracePrint(ELL_TRACE, "UPnP: EventListen @%u\n", g_wEventPort);
661 return sd;
662 }
663
664 // unsuccessful - close sd and try again
665 if (g_fLogging & NALOG_ERROR)
666 fprintf(g_log,
667 "bind TCP port %u failed: errno = %d\n", g_wEventPort, errno);
668 close(sd);
669 }
670
671 return NA_E_NET;
672 }
673
674 static void *TCPProc(void *in);
675
676 static int EventInit()
677 {
678 int iRet;
679 pthread_attr_t attr;
680
681 if (g_fEventEnabled == FALSE)
682 {
683 // initialize TCP socket for Eventing
684 g_sTCP = EventListen();
685 if (g_sTCP < 0) {
686 if (g_fLogging & NALOG_ERROR)
687 fprintf(g_log, "EventInit - Failed to init tcp socket.\n");
688 return NA_E_INTERNAL_ERROR;
689 }
690
691 // make TCP thread
692 pthread_attr_init(&attr);
693 iRet = pthread_create(&g_TCPthread, &attr, TCPProc, 0);
694 if (iRet != 0) {
695 close(g_sTCP);
696 g_sTCP = -1;
697 if (g_fLogging & NALOG_ERROR)
698 fprintf(g_log, "EventInit: TCPProc create failed(%d)\n", iRet);
699 return NA_E_THREAD_ERROR;
700 }
701 }
702
703 g_fEventEnabled = TRUE;
704
705 return NA_E_SUCCESS;
706 }
707
708 static void DumpHex(char *buf, int len)
709 {
710 int i;
711 int nexti;
712 int j;
713 int endj;
714
715 if (g_fLogging & NALOG_DUMP) {
716 if (buf == NULL) return;
717 if (len <= 0) return;
718
719 for (i = 0; i < len; i = nexti) {
720 fprintf(g_log, "%04x: ", i);
721 nexti = i + 16;
722 endj = (nexti > len) ? len : nexti;
723 for (j = i; j < endj; j++)
724 fprintf(g_log, "%02x ", buf[j] & 0xff);
725 if (j == len) {
726 if ((j % 16) != 0) {
727 char pad[3 * 16 + 1]; // don't need the last 3 bytes anyway
728 j = (16 - (j % 16)) * 3;
729 memset(pad, ' ', j);
730 pad[j] = '\0';
731 fputs(pad, g_log);
732 }
733 }
734 for (j = i; j < endj; j++)
735 isprint(buf[j]) ? fputc(buf[j], g_log) : fputc('.', g_log);
736 fputc('\n', g_log);
737 }
738
739 }
740 }
741
742 // FindHTTPHeaderNewLine
743 // Returns a pointer to the beginning of a CRLF, that is not a
744 // part of LWS. (LWS is CRLF followed by a space or tab, and in
745 // HTTP, considered as equivalent to a single space) (LWS stands
746 // for "linear white space")
747 // Returns a pointer the beginning of CRLF, and sets the EOH flag to
748 // whether this is the last header in the HTTP header section.
749 // Also, if pbuf is NULL, or if there isn't any CRLF found in the
750 // string, or if the HTTP syntax is wrong, NULL is returned, and
751 // the EOH flag is not touched.
752 static char *FindHTTPHeaderNewLine(char *pbuf, int iBufSize, int *pfEOH)
753 {
754 char *result;
755 int i = 0;
756
757 if (pbuf == NULL) return NULL;
758
759 for (;;) {
760 result = memchr(pbuf, '\r', iBufSize);
761 if (result == NULL) {
762 if (g_fLogging & NALOG_INFO0) {
763 fprintf(g_log, "FindHTTPHeaderNewLine: er @(%d)\n", i);
764 fflush(g_log);
765 }
766 return NULL;
767 }
768 i++; // count chars
769
770 // decrement iBufSize, and move pbuf forward
771 iBufSize -= (result - pbuf);
772 pbuf = result;
773
774 ++pbuf; // now pointing right after "\r"
775 --iBufSize;
776 if (*pbuf == '\0') break;
777 if (*pbuf != '\n') continue;
778
779 ++pbuf; // now pointing after "\r\n"
780 --iBufSize;
781 if (*pbuf == '\0') break;
782 if ((*pbuf == ' ') || (*pbuf == '\t')) continue;
783
784 // at this point we know we're at the end of a header field,
785 // and there's more stuff coming...
786
787 // just need to check if this is the last header
788 if ((pbuf[0] == '\r') && (pbuf[1] == '\n'))
789 *pfEOH = TRUE;
790 else
791 *pfEOH = FALSE;
792
793 return result;
794 }
795
796 return NULL;
797 }
798
799 // NewHTTPResponse_sz
800 // Creates an HTTPResponse structure from a string (sz). Set
801 // fDestroyOriginal to TRUE if the buffer passed in can be overwritten.
802 // Otherwise, NewHTTPResponse_sz will duplicate the buffer.
803 // Returns the created HTTPResponse structure if successful, or if an
804 // error occured (out of memory, or bad http syntax), returns NULL.
805 // NOTE: ALWAYS call DeleteHTTPResponse after using the HTTPResponse structure.
806 // NOTE: The input is assumed to be correct. If there're HTTP syntax errors,
807 // and the pszHTTPResponse is not null-terminated, result may be undefined.
808 // (to be fixed next version)
809 static PHTTPResponse NewHTTPResponse_sz(
810 char *pszHTTPResponse,
811 int iBufferSize,
812 int fDestroyOriginal)
813 {
814 PHTTPResponse pResponse;
815 int fEOH;
816 char *pszEOL;
817 int iNumHeaders;
818 char *pBuf;
819
820 if ((pResponse = (PHTTPResponse)malloc(sizeof(HTTPResponse))) == NULL) {
821 if (g_fLogging & NALOG_INFO0) {
822 fprintf(g_log, "NewHTTPResponse_sz: er 1\n");
823 fflush(g_log);
824 }
825 return NULL;
826 }
827
828 // make copy of buffer now
829 if (fDestroyOriginal) {
830 pResponse->buf = NULL;
831 pBuf = pszHTTPResponse;
832 }
833 else {
834 int len = strlen(pszHTTPResponse);
835 if ((len+1) > iBufferSize) {
836 if (g_fLogging & NALOG_INFO0)
837 fprintf(g_log, "Length: %d > %d\n", len+1, iBufferSize);
838 iBufferSize = len+1;
839 }
840 if ((pResponse->buf = (char *)malloc(iBufferSize)) == NULL) {
841 free(pResponse);
842 if (g_fLogging & NALOG_INFO0) {
843 fprintf(g_log, "NewHTTPResponse_sz: er 2\n");
844 fflush(g_log);
845 }
846 return NULL;
847 }
848 memcpy(pResponse->buf, pszHTTPResponse, iBufferSize);
849 pBuf = pResponse->buf;
850 }
851
852 // get the first line
853 pszEOL = FindHTTPHeaderNewLine(pBuf, iBufferSize, &fEOH);
854 if (pszEOL == NULL) {
855 if (g_fLogging & NALOG_INFO0) {
856 fprintf(g_log, "NewHTTPResponse_sz: er 3\n");
857 fflush(g_log);
858 }
859 goto cleanup;
860 }
861
862 *pszEOL = '\0'; // terminate the status line
863 pszEOL += 2; // point to the rest of the buffer
864
865 // set the status string first
866 pResponse->pszStatus = strchr(pBuf, ' ');
867 if (pResponse->pszStatus == NULL) {
868 if (g_fLogging & NALOG_INFO0) {
869 fprintf(g_log, "NewHTTPResponse_sz: er 4\n");
870 fflush(g_log);
871 }
872 goto cleanup; // syntax error
873 }
874
875 pResponse->pszStatus++; // point to the actual status
876
877 pResponse->pszReason = strchr(pResponse->pszStatus, ' ');
878 if (pResponse->pszReason == NULL) {
879 if (g_fLogging & NALOG_INFO0) {
880 fprintf(g_log, "NewHTTPResponse_sz: er 5\n");
881 fflush(g_log);
882 }
883 goto cleanup; // syntax error
884 }
885
886 pResponse->pszReason[0] = '\0'; // terminate status string
887 pResponse->pszReason++; // point to the reason string
888
889 iNumHeaders = 0; // initialize to 0 headers
890
891 // parse header fields line by line (while not end of headers)
892 while (!fEOH) {
893 PProperty pHeader = &(pResponse->aHeaders[iNumHeaders]);
894 // point header field name to the first char of the line
895 pHeader->pszName = pszEOL;
896
897 // search for the end of line
898 pszEOL = FindHTTPHeaderNewLine(pszEOL,
899 iBufferSize - (pszEOL - pBuf), // remainder size
900 &fEOH);
901 if (pszEOL == NULL) goto cleanup; // syntax error
902
903 *pszEOL = '\0'; // terminate this string
904 pszEOL += 2; // point to beginning of next line
905
906 pHeader->pszValue = strchr(pHeader->pszName, ':');
907 if (pHeader->pszValue == NULL) {
908 if (g_fLogging & NALOG_INFO0) {
909 fprintf(g_log, "NewHTTPResponse_sz: er 6\n");
910 fflush(g_log);
911 }
912 goto cleanup; // syntax error (header field has no ":")
913 }
914
915 pHeader->pszValue[0] = '\0'; // terminate the header name string
916 pHeader->pszValue++; // point after the ":"
917 // get rid of leading spaces for the value part
918 while (
919 (pHeader->pszValue[0] == ' ') ||
920 (pHeader->pszValue[0] == '\t') ||
921 (pHeader->pszValue[0] == '\r') ||
922 (pHeader->pszValue[0] == '\n')
923 ) {
924 pHeader->pszValue++; // skip the space
925 }
926
927 iNumHeaders++; // added one more header
928 pHeader++; // point to the next header in pResponse->aHeaders
929 }
930
931 pResponse->iNumHeaders = iNumHeaders; // remember to set it in pResponse
932
933 pResponse->pszBody = pszEOL + 2; // point after the empty line
934
935 return pResponse;
936
937 cleanup:
938 if (pResponse->buf != NULL) free(pResponse->buf);
939 free(pResponse);
940 return NULL;
941 }
942
943 // DeleteHTTPResponse
944 // Deallocates stuff in the HTTPResponse structure, effectively returning
945 // memory to the system and destroying the structure.
946 // NOTE: The pointer pResponse WILL BE FREED, and will be unusable after
947 // the call to DeleteHTTPResponse.
948 static void DeleteHTTPResponse(PHTTPResponse pResponse)
949 {
950 // int i;
951
952 if (pResponse == NULL) return;
953
954 // Current impl is just simple array - no need to free()
955 //for (i = 0; i < pResponse->iNumHeaders; i++) {
956 // free(pResponse->aHeaders[i]);
957 //}
958
959 if (pResponse->buf != NULL)
960 free(pResponse->buf);
961 free(pResponse);
962 }
963
964 //typedef struct tagHTTPResponse {
965 // char *pszStatus;
966 // char *pszReason;
967 // int iNumHeaders;
968 // Property aHeaders[30]; // assume at most this many headers
969 // char *pszBody;
970 //
971 // // for admin use
972 // int fFree;
973 // char *buf;
974 //} HTTPResponse, *PHTTPResponse, **PPHTTPResponse;
975
976 static void PrintHTTPResponse(PHTTPResponse pResponse)
977 {
978 int i;
979
980 if (g_fLogging & (NALOG_INFO1)) {
981 if (pResponse == NULL) return;
982 fprintf(g_log, " *** HTTP response begin *** \n");
983 fprintf(g_log, " * status = [%s], reason = [%s] *\n",
984 pResponse->pszStatus, pResponse->pszReason);
985 for (i = 0; i < pResponse->iNumHeaders; i++) {
986 fprintf(g_log, " * Header \"%s\" = [%s]\n",
987 pResponse->aHeaders[i].pszName,
988 pResponse->aHeaders[i].pszValue);
989 }
990 if (g_fLogging & NALOG_DUMP)
991 fprintf(g_log, " * body = [%s] *\n", pResponse->pszBody);
992 fprintf(g_log, " *** HTTP response end *** \n");
993 }
994 }
995
996 static int DiscoverRouter(PHTTPResponse pResponse)
997 {
998 int i;
999 int fLocation = FALSE;
1000 int fUSN = FALSE;
1001 int fIsNATDevice = FALSE;
1002
1003 #if 0
1004 if (strcmp(pResponse->pszStatus, "200") != 0)
1005 return -1;
1006 #endif
1007
1008 if (pResponse == NULL) {
1009 if (g_fLogging & NALOG_INFO0)
1010 fprintf(g_log, "DiscoverRouter: pResponse == NULL\n");
1011 return -1;
1012 }
1013
1014 // check to see if this is a relevant packet
1015 for (i = 0; i < pResponse->iNumHeaders; i++) {
1016 PProperty pHeader = &(pResponse->aHeaders[i]);
1017
1018 if ((strcasecmp(pHeader->pszName, "ST") == 0) ||
1019 (strcasecmp(pHeader->pszName, "NT") == 0)) {
1020 if ((strcmp(pHeader->pszValue,
1021 "urn:schemas-upnp-org:service:WANIPConnection:1") == 0) ||
1022 (strcmp(pHeader->pszValue,
1023 "urn:schemas-upnp-org:device:InternetGatewayDevice:1") == 0)) {
1024 fIsNATDevice = TRUE;
1025 }
1026 }
1027 }
1028
1029 // leave the message alone if we don't need it
1030 if (!fIsNATDevice)
1031 return -1;
1032
1033 // Now that we know we're looking at the message about the NAT device:
1034 pthread_mutex_lock(&g_xUPnP);
1035
1036 // set upnp to be unconfigured for now
1037 g_fUPnPEnabled = FALSE;
1038
1039 // loop through the headers
1040 for (i = 0; i < pResponse->iNumHeaders; i++) {
1041 PProperty pHeader = &(pResponse->aHeaders[i]);
1042
1043 if (strcasecmp(pHeader->pszName, "Location") == 0) {
1044 char *p;
1045 char *q;
1046
1047 if (g_fLogging & NALOG_INFO1)
1048 fprintf(g_log, "Checking Location...\n");
1049 p = pHeader->pszValue;
1050 if (strncmp(p, "http://", 7) != 0)
1051 continue; // hope for another Location header to correct it
1052 p += 7; // skip over "http://"
1053 q = strchr(p, '/');
1054
1055 // set the control URL first
1056 if (q == NULL) {
1057 g_szNATDevDescURL[0] = '/';
1058 g_szNATDevDescURL[1] = '\0';
1059 }
1060 else {
1061 strncpy(g_szNATDevDescURL, q, sizeof(g_szNATDevDescURL) - 1);
1062 g_szNATDevDescURL[sizeof(g_szNATDevDescURL) - 1] = '\0';
1063 // terminate the host/port string
1064 *q = '\0';
1065 }
1066
1067 if (g_fLogging & NALOG_INFO1)
1068 fprintf(g_log, " Device Description URL set to[%s]...\n",
1069 g_szNATDevDescURL);
1070
1071 // see if port is specified
1072 q = strchr(p, ':');
1073 if (q == NULL) {
1074 sprintf(g_szRouterHostPortDesc, "%s", p);
1075
1076 g_saddrRouterDesc.sin_addr.s_addr = inet_addr(p);
1077 g_saddrRouterDesc.sin_port = htons(80);
1078 }
1079 else {
1080 // don't include the ":80" - HTTP is by default port 80
1081 if (atoi(q+1) == 80) *q = '\0';
1082
1083 strcpy(g_szRouterHostPortDesc, p);
1084
1085 // terminate the host part and point to it
1086 *q = '\0';
1087 q++;
1088
1089 g_saddrRouterDesc.sin_addr.s_addr = inet_addr(p);
1090 g_saddrRouterDesc.sin_port = htons(atoi(q));
1091 }
1092
1093 g_saddrRouterDesc.sin_family = AF_INET;
1094
1095 if (g_fLogging & NALOG_INFO1)
1096 fprintf(g_log, " Router Address set to[%s]...\n",
1097 g_szRouterHostPortDesc);
1098 fLocation = TRUE;
1099 }
1100 else if (strcasecmp(pHeader->pszName, "USN") == 0) {
1101 if (g_fLogging & NALOG_INFO1)
1102 fprintf(g_log, "Checking USN...\n");
1103 strncpy(g_szUSN, pHeader->pszValue, sizeof(g_szUSN) - 1);
1104 g_szUSN[sizeof(g_szUSN) - 1] = '\0';
1105 fUSN = TRUE;
1106 }
1107 else {
1108 ; // do nothing for other headers for now
1109 }
1110 }
1111
1112 // now check flags and set enabled if all set
1113 if (fLocation && fUSN) {
1114 if (g_fLogging & NALOG_INFO1) {
1115 fprintf(g_log,
1116 "Description Host/port string: [%s]\n"
1117 "NATDevDescURL: [%s], USN: [%s]\n",
1118 g_szRouterHostPortDesc,
1119 g_szNATDevDescURL, g_szUSN);
1120 if (g_fLogging & NALOG_INFO1)
1121 fprintf(g_log, "Got router information\n");
1122 }
1123
1124 g_fUPnPEnabled = TRUE;
1125 pthread_cond_broadcast(&g_condUPnP);
1126 }
1127
1128 // remember to unlock before return
1129 pthread_mutex_unlock(&g_xUPnP);
1130
1131 return 0;
1132 }
1133
1134 // granularity is specified as: granularity = 1/nth seconds
1135 #define UPNP_TIMEOUT_GRANULARITY (1000)
1136 #define U_TOGRAN UPNP_TIMEOUT_GRANULARITY
1137
1138 // result = a - b
1139 static void TimevalSubtract(
1140 struct timeval *result,
1141 const struct timeval *a,
1142 const struct timeval *b)
1143 {
1144 result->tv_sec = a->tv_sec - b->tv_sec;
1145
1146 if (b->tv_usec > a->tv_usec) {
1147 result->tv_sec--;
1148 result->tv_usec = 1000000 + a->tv_usec - b->tv_usec;
1149 }
1150 else
1151 result->tv_usec = a->tv_usec - b->tv_usec;
1152 }
1153
1154 // elapsed = end - start
1155 static void GetTimeElapsed(
1156 const struct timeval *tv_start,
1157 const struct timeval *tv_end,
1158 struct timeval *tv_elapsed)
1159 {
1160 TimevalSubtract(tv_elapsed, tv_end, tv_start);
1161 #if 0
1162 tv_elapsed->tv_sec = tv_end->tv_sec - tv_start->tv_sec;
1163
1164 if (tv_start->tv_usec > tv_end->tv_usec) {
1165 tv_elapsed->tv_sec--;
1166 tv_elapsed->tv_usec = 1000000 + tv_end->tv_usec - tv_start->tv_usec;
1167 }
1168 else
1169 tv_elapsed->tv_usec = tv_end->tv_usec - tv_start->tv_usec;
1170 #endif
1171 }
1172
1173 // returns +1, 0, or -1, if a>b, a==b, a<b, respectively
1174 static int CompareTime(
1175 const struct timeval *a,
1176 const struct timeval *b
1177 )
1178 {
1179 if ((a->tv_sec == b->tv_sec) &&
1180 (a->tv_usec == b->tv_usec)) return 0;
1181
1182 if (a->tv_sec > b->tv_sec) return 1;
1183 else if (a->tv_sec < b->tv_sec) return -1;
1184
1185 // if seconds are equal...
1186 if (a->tv_usec > b->tv_usec) return 1;
1187 else return -1;
1188 }
1189
1190 static int WaitControlURLSet(double timeout)
1191 {
1192 struct timespec ts;
1193 struct timeval tv;
1194 struct timeval tv_start;
1195 int iRet;
1196 long to_sec = (int) (timeout / U_TOGRAN);
1197 long to_usec =
1198 (int) (((timeout / U_TOGRAN) - to_sec) * 1000000.0);
1199 //long to_sec = (int) timeout;
1200 //long to_usec = (int) ((timeout - to_sec) * 1000000.0);
1201 struct timeval elapsed;
1202
1203 // get function start time
1204 gettimeofday(&tv_start, NULL);
1205
1206 pthread_mutex_lock(&g_xUPnP);
1207
1208 #if 0
1209 // if last update is too long ago then wait for it
1210 GetTimeElapsed(&g_tvLastUpdateTime, &tv_start, &elapsed);
1211 if ((elapsed.tv_sec + (elapsed.tv_usec / 1000000.0)) >
1212 (((double) g_iUPnPTimeout) / U_TOGRAN))
1213 g_fControlURLSet = 0;
1214 #endif
1215
1216 while (!g_fControlURLSet) {
1217 // get current time
1218 gettimeofday(&tv, NULL);
1219
1220 #if 0
1221 for now ignore device timeout
1222 // see if we've past the device's timeout first
1223 GetTimeElapsed(&g_tvUPnPInitTime, &tv, &elapsed);
1224 if ((elapsed.tv_sec > g_timeout_sec) ||
1225 ( (elapsed.tv_sec == g_timeout_sec) &&
1226 (elapsed.tv_usec > g_timeout_usec)
1227 ))
1228 {
1229 pthread_mutex_unlock(&g_xUPnP);
1230 return FALSE;
1231 }
1232 #endif
1233
1234 // calculate ts to sleep till
1235 ts.tv_sec = tv.tv_sec + to_sec;
1236 ts.tv_nsec = (tv.tv_usec + to_usec) * 1000;
1237 if (ts.tv_nsec > 1000000000) {
1238 ts.tv_nsec -= 1000000000;
1239 ts.tv_sec += 1;
1240 }
1241
1242 // now get how long we've been in this function already and deduct
1243 GetTimeElapsed(&tv_start, &tv, &elapsed);
1244 ts.tv_sec -= elapsed.tv_sec;
1245 if (ts.tv_nsec < (elapsed.tv_usec * 1000)) {
1246 ts.tv_sec--;
1247 ts.tv_nsec = 1000000000 + ts.tv_nsec - (elapsed.tv_usec * 1000);
1248 }
1249 else {
1250 ts.tv_nsec -= (elapsed.tv_usec * 1000);
1251 }
1252
1253 iRet = pthread_cond_timedwait(&g_condUPnPControlURL, &g_xUPnP, &ts);
1254
1255 // if timeout then return false
1256 if (iRet != 0)
1257 {
1258 pthread_mutex_unlock(&g_xUPnP);
1259 return FALSE;
1260 }
1261 }
1262 pthread_mutex_unlock(&g_xUPnP);
1263
1264 return TRUE;
1265 }
1266
1267 static int WaitUPnPFunction()
1268 {
1269 struct timeval start;
1270 // struct timeval end;
1271 double wait2;
1272 // struct timeval elapsed;
1273
1274 gettimeofday(&start, NULL);
1275
1276 wait2 = (double)g_iFunctionTimeout;
1277
1278 WaitControlURLSet(wait2);
1279
1280 //gettimeofday(&end, NULL);
1281 //GetTimeElapsed(&start, &end, &elapsed);
1282 //fprintf(stderr, "== wait2: (%f) %d.%06d\n",
1283 // wait2/U_TOGRAN, elapsed.tv_sec, elapsed.tv_usec);
1284
1285 return g_fControlURLSet;
1286 }
1287
1288 static void SetLocalIP();
1289
1290 static int SendTCPMsg_saddr_parse(
1291 char *msg, int iLen,
1292 char *result, int resultSize,
1293 struct sockaddr_in *saHost);
1294
1295 static void *TCPProc(void *in)
1296 {
1297 int iRet;
1298 unsigned char buf[MAX_SOAPMSGSIZE];
1299 int iBufLen;
1300
1301 (void)in; // unused
1302 WaitUPnPFunction();
1303 //TracePrint(ELL_TRACE, "UPnP: Begin TCPProc\n");
1304
1305 // do the subscription
1306 {
1307 char callback[100];
1308 char response[2000];
1309 PHTTPResponse resp;
1310 int n;
1311 sprintf(callback, "%lu.%lu.%lu.%lu:%u",
1312 (g_dwLocalIP >> 24) & 0xFF,
1313 (g_dwLocalIP >> 16) & 0xFF,
1314 (g_dwLocalIP >> 8) & 0xFF,
1315 (g_dwLocalIP >> 0) & 0xFF,
1316 g_wEventPort);
1317
1318 n = sprintf((char *)buf,
1319 szEventMsgSubscribeFMT,
1320 g_szEventURL,
1321 callback, g_szRouterHostPortEvent, 1800);
1322
1323 memset(response, 0, 2000);
1324 n = SendTCPMsg_saddr_parse(
1325 (char *)buf, n,
1326 response, 2000,
1327 &g_saddrRouterEvent);
1328 if (n > 0)
1329 {
1330 response[n] = '\0';
1331 resp = NewHTTPResponse_sz((char *)buf, n, TRUE);
1332 if (NULL != resp)
1333 {
1334 ////TracePrint(ELL_TRACE, "UPnP Subscribe returns %s/%d\n", resp->pszStatus, n);
1335 }
1336 else
1337 {
1338 ////TracePrint(ELL_TRACE, "UPnP Subscribe not enough response (%d) \n[%s]\n",
1339 // n, response);
1340 }
1341 DeleteHTTPResponse(resp);
1342 }
1343 else
1344 {
1345 ////TracePrint(ELL_TRACE, "UPnP Subscribe failed (%d)\n", n);
1346 return NULL;
1347 }
1348 }
1349
1350 //TracePrint(ELL_TRACE, "UPnP: TCPProc begin loop\n");
1351
1352 g_sTCPCancel = -1;
1353
1354 for (;;)
1355 {
1356 // ssize_t n;
1357 struct sockaddr_in recvaddr;
1358 socklen_t recvaddrlen;
1359 fd_set readfds;
1360 struct timeval timeout;
1361 int sEvent;
1362 int fFirstRecv;
1363 int sMax;
1364
1365 // for after responding to long(?) TCP event
1366 if (g_fQuit)
1367 goto cleanup;
1368
1369 if (g_sTCPCancel != -1) close(g_sTCPCancel);
1370 sMax = g_sTCPCancel = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1371 if (sMax < g_sTCP) sMax = g_sTCP;
1372
1373 FD_ZERO(&readfds);
1374 FD_SET(g_sTCP, &readfds);
1375 FD_SET(g_sTCPCancel, &readfds);
1376 iRet = select(sMax+1, &readfds, NULL, NULL, NULL);
1377 if (iRet <= 0) {
1378 if (EBADF == errno)
1379 continue;
1380 //TracePrint(ELL_TRACE, "UPnP Event select failed (%d)\n", errno);
1381 continue;
1382 }
1383
1384 recvaddrlen = sizeof(recvaddr);
1385 sEvent = accept(g_sTCP, (struct sockaddr *)&recvaddr, &recvaddrlen);
1386 // not likely - (system's descriptor/file table full)
1387 if (sEvent <= 0) continue;
1388
1389 ////TracePrint(ELL_TRACE, "UPnP receiving event..\n");
1390
1391 // read all we could from this event
1392 fFirstRecv = 1;
1393 iBufLen = 0;
1394 for (;;)
1395 {
1396 FD_ZERO(&readfds);
1397 FD_SET(sEvent, &readfds);
1398 timeout.tv_sec = 0;
1399 timeout.tv_usec = 400000; // long cause we're dealing with input
1400 iRet = select(sEvent+1, &readfds, NULL, NULL, &timeout);
1401 if (iRet <= 0) {
1402 if (g_fQuit)
1403 {
1404 close(sEvent);
1405 goto cleanup;
1406 }
1407 break;
1408 }
1409
1410 // recv
1411 iRet = recv(sEvent, buf + iBufLen, MAX_SOAPMSGSIZE - iBufLen, 0);
1412 if (iRet < 0)
1413 {
1414 // something is wrong
1415 break;
1416 }
1417 else if (iRet == 0)
1418 {
1419 break;
1420 }
1421
1422 iBufLen += iRet;
1423
1424 if (fFirstRecv)
1425 {
1426 int iTemp;
1427 iTemp = send(sEvent, HTTP200OK, HTTP200OKLEN, 0);
1428 shutdown(sEvent, 1);
1429 fFirstRecv = 0;
1430 }
1431 }
1432
1433 // now send 200 OK and be done
1434 close(sEvent);
1435
1436 ////TracePrint(ELL_TRACE, "UPnP event (%d) received (%d)\n", g_fExpectEvent, iBufLen);
1437
1438 // and parse the XML here.
1439 if (iBufLen < MAX_SOAPMSGSIZE)
1440 {
1441 buf[iBufLen] = '\0';
1442 // for now do nothing
1443 }
1444 else
1445 {
1446 buf[MAX_SOAPMSGSIZE - 1] = '\0';
1447 }
1448 }
1449
1450 cleanup:
1451 //TracePrint(ELL_TRACE, "UPnP: TCPProc end\n");
1452 close(g_sTCP);
1453 g_sTCP = -1;
1454 g_fEventEnabled = FALSE;
1455 if (g_sTCPCancel != -1) close(g_sTCPCancel);
1456 g_sTCPCancel = -1;
1457 return NULL;
1458 }
1459
1460 static void *UDPProc(void *in)
1461 {
1462 // char fLoop = 0; // false - don't send copy to self
1463 // int iTTL = SSDP_TTL;
1464 int iRet;
1465 // struct ip_mreq mreq;
1466 // struct sockaddr_in saddr;
1467 unsigned char buf[65536];
1468 // FILE *log = g_log;
1469 static time_t last_getdevicedesc_t = 0;
1470
1471 (void)in; // unused
1472 pthread_mutex_lock(&g_xUPnP);
1473 gettimeofday(&g_tvUPnPInitTime, NULL);
1474 pthread_mutex_unlock(&g_xUPnP);
1475
1476 for (;;) {
1477 ssize_t n;
1478 struct sockaddr_in recvaddr;
1479 socklen_t recvaddrlen;
1480 fd_set readfds;
1481 //struct timeval timeout;
1482 //int i;
1483 int sMax;
1484
1485 if (g_sUDPCancel < g_sUDP) sMax = g_sUDP;
1486 else sMax = g_sUDPCancel;
1487
1488 FD_ZERO(&readfds);
1489 FD_SET(g_sUDP, &readfds);
1490 FD_SET(g_sUDPCancel, &readfds);
1491 iRet = select(sMax+1, &readfds, NULL, NULL, NULL);
1492
1493 if (iRet <= 0) {
1494 if (g_fQuit)
1495 {
1496 close(g_sUDP);
1497 close(g_sUDPCancel);
1498 g_sUDP = -1;
1499 g_sUDPCancel = -1;
1500 return NULL;
1501 }
1502 continue;
1503 }
1504
1505 if (!FD_ISSET(g_sUDP, &readfds)) continue;
1506 recvaddrlen = sizeof(recvaddr);
1507 n = recvfrom(g_sUDP, buf, sizeof(buf), 0,
1508 (struct sockaddr *)&recvaddr, &recvaddrlen);
1509 if (n < 0) {
1510 if (g_fLogging & NALOG_ERROR)
1511 fprintf(g_log, "recv failed (%d)\n", errno);
1512 close(g_sUDP);
1513 close(g_sUDPCancel);
1514 g_sUDP = -1;
1515 g_sUDPCancel = -1;
1516 return NULL;
1517 }
1518 buf[n] = '\0';
1519 if (strncmp((char *)buf, "HTTP/1.1", 8) == 0) {
1520 PHTTPResponse pResponse = NewHTTPResponse_sz((char *)buf, n, TRUE);
1521 PrintHTTPResponse(pResponse);
1522 if (DiscoverRouter(pResponse) == 0)
1523 {
1524 time_t now = time(NULL);
1525 if (!g_fControlURLSet ||
1526 ((now - last_getdevicedesc_t) > 5))
1527 {
1528 GetDeviceDescription();
1529 SetLocalIP();
1530 last_getdevicedesc_t = now;
1531 }
1532 }
1533 DeleteHTTPResponse(pResponse);
1534 }
1535 else if (strncmp((char *)buf, "NOTIFY * HTTP/1.1", 7) == 0) {
1536 // temporarily use this to fudge - will have the exact same
1537 // parsing, only status/reason set to "*" and "HTTP/1.1".
1538 // TODO: add support for HTTP requests
1539 PHTTPResponse pResponse = NewHTTPResponse_sz((char *)buf, n, TRUE);
1540 if (DiscoverRouter(pResponse) == 0)
1541 {
1542 time_t now = time(NULL);
1543 if (!g_fControlURLSet ||
1544 ((now - last_getdevicedesc_t) > 5))
1545 {
1546 GetDeviceDescription();
1547 SetLocalIP();
1548 last_getdevicedesc_t = now;
1549 }
1550 }
1551 DeleteHTTPResponse(pResponse);
1552 }
1553 else {
1554 if (g_fLogging & NALOG_DUMP)
1555 fprintf(g_log, "(%ld) Buffer: \n[%s]\n", time(NULL), buf);
1556 fflush(g_log);
1557 }
1558 }
1559
1560 close(g_sUDP);
1561 g_sUDP = -1;
1562 }
1563
1564 static void SendUDPMsg(const char *msg) {
1565 struct sockaddr_in saSendTo;
1566 int iRet;
1567 int iLen;
1568
1569 bzero(&saSendTo, sizeof(saSendTo));
1570 saSendTo.sin_family = AF_INET;
1571 saSendTo.sin_addr.s_addr = inet_addr(SSDP_IP);
1572 saSendTo.sin_port = htons(SSDP_PORT);
1573
1574 iLen = strlen(msg);
1575
1576 if (g_fLogging & NALOG_DUMP)
1577 fprintf(g_log, "SendUDP: [%s]\n", msg);
1578
1579 iRet = sendto(g_sUDP, msg, iLen, 0,
1580 (struct sockaddr *)&saSendTo, sizeof(saSendTo));
1581
1582 // sanity check
1583 if (iRet != iLen)
1584 if (g_fLogging & NALOG_ALERT)
1585 fprintf(g_log,
1586 "SendUDPMsg: iRet(%d) != strlen(msg)(%d)! (errno %d)\n",
1587 iRet, iLen, errno);
1588 }
1589
1590 // strstr, case insensitive, and is limited by len
1591 static char *strcasestr_n(const char *big, const char *little, int len)
1592 {
1593 int bigLen;
1594 int littleLen;
1595 int i;
1596 int end;
1597
1598 if (little == NULL) return (char *)big;
1599 if (big == NULL) return NULL;
1600
1601 //bigLen = strlen(big);
1602 bigLen = len;
1603 littleLen = strlen(little);
1604
1605 if (bigLen < littleLen) return NULL;
1606
1607 end = bigLen - littleLen;
1608 for (i = 0; i <= end; (i++), (big++)) {
1609 if (strncasecmp(big, little, littleLen) == 0)
1610 return (char *)big;
1611 }
1612
1613 return NULL;
1614 }
1615
1616 // this is strnstr, only portable
1617 static char *strstr_n(const char *big, const char *little, int len)
1618 {
1619 int iBigLen;
1620 int iLittleLen;
1621
1622 (void)len; // unused
1623
1624 if ((big == NULL) || (little == NULL)) return NULL;
1625
1626 iBigLen = strlen(big);
1627 iLittleLen = strlen(little);
1628
1629 // this part is basically strnstr, except this is portable
1630 for (;;) {
1631 if (iBigLen < iLittleLen)
1632 return NULL;
1633 if (strncmp(big, little, iLittleLen) == 0)
1634 return (char *)big;
1635 ++big;
1636 --iBigLen;
1637 }
1638 }
1639
1640 // returns -1 for "not found"
1641 static int FindContentLength(char *pbuf, int iLen)
1642 {
1643 // non reusable HTTP header parsing code:
1644 // ----------------------------------------------
1645 char *p;
1646 int iResult;
1647
1648 // find content length header
1649 p = strcasestr_n(pbuf, "\r\nContent-Length:", iLen);
1650 if (p == NULL) return -1;
1651
1652 p += sizeof("\r\nContent-Length:") - 1; // minus '\0'
1653
1654 iResult = atoi(p);
1655
1656 return iResult;
1657 // ----------------------------------------------
1658 }
1659
1660 // returns -1 for "not found"
1661 static int FindBody(char *pbuf, int iLen)
1662 {
1663 // non reusable HTTP header parsing code:
1664 // ----------------------------------------------
1665 char *p;
1666 // int iResult;
1667
1668 // find the empty line
1669 p = strstr_n(pbuf, "\r\n\r\n", iLen);
1670 if (p == NULL) return -1;
1671
1672 p += sizeof("\r\n\r\n") - 1; // minus '\0'
1673
1674 return (p - pbuf);
1675 // ----------------------------------------------
1676 }
1677
1678 static int SendTCPMsg_saddr_2part(
1679 char *msg, int iLen,
1680 char *msg2, int iLen2,
1681 char *result, int resultSize,
1682 struct sockaddr_in *saHost)
1683 {
1684 int s;
1685 struct sockaddr_in saSendTo;
1686 int iRet;
1687 int iBufLen;
1688 int fND;
1689 int fcntl_flags;
1690 int iRetcode;
1691 struct timeval tv;
1692 fd_set writefds;
1693
1694 struct timeval tv_start;
1695 struct timeval tv_end;
1696 struct timeval tv_elapsed;
1697
1698 int iContentLength = -1;
1699 int iBodyOffset = -1;
1700
1701 gettimeofday(&tv_start, NULL);
1702
1703 if (g_fUPnPEnabled != TRUE) {
1704 //TracePrint(ELL_TRACE, "UPnP not enabled\n");
1705 if (g_fLogging & NALOG_ERROR)
1706 fprintf(g_log, "UPnP not enabled (no UPnP device found yet)\n");
1707 return NA_E_NOT_AVAILABLE;
1708 }
1709
1710 s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1711 if (s == -1) {
1712 if (g_fLogging & NALOG_ERROR)
1713 fprintf(g_log, "Can't get TCP socket (%d)\n", errno);
1714 return NA_E_NET;
1715 }
1716
1717 fND = 1;
1718 if (setsockopt(s, IPPROTO_IP, TCP_NODELAY, &fND, sizeof(fND)) != 0) {
1719 if (g_fLogging & NALOG_ERROR)
1720 fprintf(g_log, "SendTCPMsg/2part: Can't set TCP_NODELAY option!\n");
1721 iRetcode = NA_E_NET;
1722 goto cleanup;
1723 }
1724
1725 fcntl_flags = 0;
1726 fcntl_flags = fcntl(s, F_GETFL, 0);
1727 fcntl_flags |= O_NONBLOCK;
1728 if (fcntl(s, F_SETFL, fcntl_flags) != 0) {
1729 if (g_fLogging & NALOG_ERROR)
1730 fprintf(g_log, "SendTCPMsg/2part: Can't set O_NONBLOCK option!\n");
1731 iRetcode = NA_E_NET;
1732 goto cleanup;
1733 }
1734
1735 if (saHost == NULL)
1736 memcpy(&saSendTo, &g_saddrRouterDesc, sizeof(saSendTo));
1737 else
1738 memcpy(&saSendTo, saHost, sizeof(saSendTo));
1739
1740 iRet = connect(s, (struct sockaddr *) &saSendTo, sizeof(saSendTo));
1741 if ((iRet < 0) && (errno != EINPROGRESS)) {
1742 //TracePrint(ELL_TRACE, "UPnP connect failed\n");
1743 if (g_fLogging & NALOG_ERROR)
1744 fprintf(g_log, "SendTCPMsg/2part: connect failed (%d)\n", errno);
1745 iRetcode = NA_E_NET;
1746 goto cleanup;
1747 }
1748
1749 if (g_fLogging & NALOG_INFO1)
1750 fprintf(g_log,
1751 "- Before Sending TCP Msg1: %d == %lu?\n", iLen, strlen(msg));
1752 if (g_fLogging & NALOG_DUMP)
1753 fprintf(g_log, "Sending TCP msg part 1:\n[%s]\n", msg);
1754
1755 tv.tv_sec = g_iFunctionTimeout / UPNP_TIMEOUT_GRANULARITY;
1756 tv.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN;
1757 FD_ZERO(&writefds);
1758 FD_SET(s, &writefds);
1759 iRet = select(s+1, 0, &writefds, 0, &tv);
1760 if (iRet < 0) {
1761 if (g_fLogging & NALOG_ERROR)
1762 fprintf(g_log, "SendTCPMsg/2part: select failed (%d)\n", errno);
1763 iRetcode = NA_E_NET;
1764 goto cleanup;
1765 }
1766 if (iRet == 0) {
1767 if (g_fLogging & NALOG_ERROR)
1768 fprintf(g_log, "SendTCPMsg/2part: select timed out\n");
1769 iRetcode = NA_E_TIMEOUT;
1770 gettimeofday(&tv_end, NULL);
1771 GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed);
1772 //TracePrint(ELL_TRACE, "UPnP 2part: timeout @1st after %lu.%06lu secs\n",
1773 // tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1774 goto cleanup;
1775 }
1776
1777 iRet = send(s, msg, iLen, 0);
1778 // sanity check
1779 if (iRet != iLen)
1780 if (g_fLogging & NALOG_ALERT)
1781 fprintf(g_log, "SendTCPMsg/2part: iRet(%d) != strlen(msg)(%d)!\n",
1782 iRet, iLen);
1783
1784 //TracePrint(ELL_TRACE, "UPnP 2part: 1st %d == %d (%d) (%d)?\n", iRet, iLen, strlen(msg), errno);
1785
1786 tv.tv_sec = g_iFunctionTimeout / UPNP_TIMEOUT_GRANULARITY;
1787 tv.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN;
1788 FD_ZERO(&writefds);
1789 FD_SET(s, &writefds);
1790 // calculate how much time elapsed
1791 gettimeofday(&tv_end, NULL);
1792 GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed);
1793 if (CompareTime(&tv_elapsed, &tv) > 0) {
1794 close(s);
1795 return NA_E_TIMEOUT;
1796 //tv.tv_sec = 0;
1797 //tv.tv_usec = 0;
1798 }
1799 else {
1800 // subtract that from timeout accordingly
1801 tv.tv_sec -= tv_elapsed.tv_sec;
1802 if (tv.tv_usec < tv_elapsed.tv_usec) {
1803 tv.tv_sec--;
1804 tv.tv_usec = 1000000 + tv.tv_usec - tv_elapsed.tv_usec;
1805 }
1806 else
1807 tv.tv_usec = tv.tv_usec - tv_elapsed.tv_usec;
1808 }
1809 iRet = select(s+1, 0, &writefds, 0, &tv);
1810 if (iRet < 0) {
1811 if (g_fLogging & NALOG_ERROR)
1812 fprintf(g_log, "SendTCPMsg/2part: select2 failed (%d)\n", errno);
1813 iRetcode = NA_E_NET;
1814 goto cleanup;
1815 }
1816 if (iRet == 0) {
1817 if (g_fLogging & NALOG_ERROR)
1818 fprintf(g_log, "SendTCPMsg/2part: select2 timed out\n");
1819 iRetcode = NA_E_TIMEOUT;
1820 gettimeofday(&tv_end, NULL);
1821 GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed);
1822 //TracePrint(ELL_TRACE, "UPnP 2part: timeout @2nd after %lu.%06lu secs\n",
1823 // tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1824 goto cleanup;
1825 }
1826
1827 iRet = send(s, msg2, iLen2, 0);
1828 if (g_fLogging & NALOG_INFO1)
1829 fprintf(g_log,
1830 "SendTCPMsg/parse: Before Sending TCP Msg2: %d == %lu?\n",
1831 iLen2, strlen(msg2));
1832 if (g_fLogging & NALOG_DUMP)
1833 fprintf(g_log, "Sending TCP msg part 2:\n[%s]\n", msg2);
1834
1835 //TracePrint(ELL_TRACE, "UPnP 2part: 2nd %d == %d (%d) (%d)?\n", iRet, iLen2, strlen(msg2), errno);
1836
1837 // sanity check
1838 if (iRet != iLen2)
1839 if (g_fLogging & NALOG_ALERT)
1840 fprintf(g_log, "SendTCPMsg/2part: iRet(%d) != strlen(msg2)(%d)!\n",
1841 iRet, iLen2);
1842
1843 if (result == NULL) { // if caller just want to send/display msgs
1844 if (g_fLogging & NALOG_DUMP)
1845 fprintf(g_log, "TCP Buffer: [");
1846 }
1847
1848 if (g_fLogging & NALOG_INFO1)
1849 fprintf(g_log, "start recv @%lu\n", time(NULL));
1850
1851 iBufLen = 0;
1852 iContentLength = -1;
1853 iBodyOffset = -1;
1854 for (;;) {
1855 fd_set readfds;
1856 struct timeval timeout;
1857 int i;
1858
1859 FD_ZERO(&readfds);
1860 FD_SET(s, &readfds);
1861 //timeout.tv_sec = g_iFunctionTimeout / U_TOGRAN;
1862 //timeout.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN;
1863 // just do flat 2 sec now, since connection already established
1864 timeout.tv_sec = 1;
1865 timeout.tv_usec = 0;
1866
1867
1868 iRet = select(s+1, &readfds, NULL, NULL, &timeout);
1869 if (iRet <= 0)
1870 {
1871 //TracePrint(ELL_TRACE, "UPnP 2part: select timeout? (%d, %d)\n",
1872 // iRet, errno);
1873 break;
1874 }
1875
1876 //gettimeofday(&tv_end, NULL);
1877 //GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed);
1878 //fprintf(stderr, "2 == loop: %d.%06d\n", tv_elapsed.tv_sec, tv_elapsed.tv_usec);
1879
1880 // if only sending messages
1881 if (result == NULL) {
1882 char t[1000];
1883 i = recv(s, t, 1000-1, 0); // leave room for '\0' for dump
1884 if (i== 0) break;
1885 if (g_fLogging & NALOG_DUMP) {
1886 t[i] = '\0';
1887 fprintf(g_log, "%s", t);
1888 }
1889 continue;
1890 }
1891
1892 // EO result buf: discard extra bytes
1893 if (resultSize <= iBufLen) {
1894 char t[1000];
1895 i = recv(s, &t, 1000, 0);
1896 if (i== 0) break;
1897 // Note that there's no dump here - prevents DoS attack from
1898 // flooding the logs/diskspace
1899 continue;
1900 }
1901
1902 i = recv(s, result + iBufLen, resultSize - iBufLen, 0);
1903 if (i <= 0) {
1904 //TracePrint(ELL_TRACE, "UPnP 2part: recv done %d (%d, %d)\n",
1905 // iBufLen, i, errno);
1906 break;
1907 }
1908
1909 iBufLen += i;
1910
1911 // parse and see if we can find content-length to quit early
1912 iContentLength = FindContentLength(result, iBufLen);
1913
1914 // now if we're still in header, see if we can find body
1915 iBodyOffset = FindBody(result, iBufLen);
1916
1917 // now check if we can leave early. conditions are:
1918 // past headers, and we've already recv'ed content-length of body
1919 if ((iBodyOffset >= 0) &&
1920 (iContentLength >= 0) &&
1921 ((iBufLen - iBodyOffset) >= iContentLength))
1922 {
1923 //TracePrint(ELL_TRACE, "UPnP 2part: read all specified %d (%d, %d) (%d, %d)\n",
1924 // iBufLen, i, errno, iBodyOffset, iContentLength);
1925 break;
1926 }
1927 }
1928
1929 //fprintf(stderr, "2 -- \n");
1930
1931 if (g_fLogging & NALOG_INFO1)
1932 fprintf(g_log, "done recv @%lu\n", time(NULL));
1933
1934 if (result == NULL) { // if caller just want to send/display msgs
1935 if (g_fLogging & NALOG_DUMP)
1936 fprintf(g_log, "]\n");
1937 }
1938
1939 close(s);
1940 return iBufLen;
1941
1942 cleanup:
1943 close(s);
1944 return iRetcode;
1945 }
1946
1947 static int SendTCPMsg_saddr_parse(
1948 char *msg, int iLen,
1949 char *result, int resultSize,
1950 struct sockaddr_in *saHost)
1951 {
1952 int s;
1953 struct sockaddr_in saSendTo;
1954 int iRet;
1955 int iBufLen;
1956 int fcntl_flags;
1957 fd_set writefds;
1958 struct timeval tv;
1959
1960 struct timeval tv_start;
1961 // struct timeval tv_end;
1962 // struct timeval tv_elapsed;
1963
1964 // HTTP parsing vars
1965 char *pszCurHdr;
1966 int iContentLength;
1967 int iBodyOffset;
1968 // char prevChar;
1969
1970 tv.tv_sec = 0;
1971 tv.tv_usec = 25000;
1972 select(0, NULL, NULL, NULL, &tv);
1973
1974 pthread_mutex_lock(&g_xUPnPMsg);
1975
1976 gettimeofday(&tv_start, NULL);
1977
1978 if (g_fUPnPEnabled != TRUE) {
1979 //TracePrint(ELL_TRACE, "UPnP not enabled\n");
1980 if (g_fLogging & NALOG_ERROR)
1981 fprintf(g_log, "UPnP not enabled (no UPnP device found yet)\n");
1982 pthread_mutex_unlock(&g_xUPnPMsg);
1983 return NA_E_NOT_AVAILABLE;
1984 }
1985
1986 s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1987 if (s == -1) {
1988 if (g_fLogging & NALOG_ERROR)
1989 fprintf(g_log, "Can't get TCP socket (%d)\n", errno);
1990 pthread_mutex_unlock(&g_xUPnPMsg);
1991 return NA_E_NET;
1992 }
1993
1994 fcntl_flags = 0;
1995 fcntl_flags = fcntl(s, F_GETFL, 0);
1996 fcntl_flags |= O_NONBLOCK;
1997 if (fcntl(s, F_SETFL, fcntl_flags) != 0) {
1998 if (g_fLogging & NALOG_ERROR)
1999 fprintf(g_log, "SendTCPMsg/parse: Can't set O_NONBLOCK option!\n");
2000 close(s);
2001 pthread_mutex_unlock(&g_xUPnPMsg);
2002 return NA_E_NET;
2003 }
2004
2005 if (saHost == NULL)
2006 memcpy(&saSendTo, &g_saddrRouterDesc, sizeof(saSendTo));
2007 else
2008 memcpy(&saSendTo, saHost, sizeof(saSendTo));
2009
2010 iRet = connect(s, (struct sockaddr *) &saSendTo, sizeof(saSendTo));
2011 if ((iRet < 0) && (errno != EINPROGRESS)) {
2012 //TracePrint(ELL_TRACE, "UPnP connect failed\n");
2013 if (g_fLogging & NALOG_ERROR)
2014 fprintf(g_log, "SendTCPMsg/parse: connect failed (%d)\n", errno);
2015 close(s);
2016 pthread_mutex_unlock(&g_xUPnPMsg);
2017 return NA_E_NET;
2018 }
2019
2020 if (g_fLogging & NALOG_INFO1)
2021 fprintf(g_log, "SendTCPMsg/parse: Before Sending TCP Msg: %d == %lu?\n",
2022 iLen, strlen(msg));
2023 if (g_fLogging & NALOG_DUMP)
2024 fprintf(g_log,"Sending TCP msg:\n[%s]\n", msg);
2025
2026 tv.tv_sec = g_iFunctionTimeout / UPNP_TIMEOUT_GRANULARITY;
2027 tv.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN;
2028 FD_ZERO(&writefds);
2029 FD_SET(s, &writefds);
2030 iRet = select(s+1, 0, &writefds, 0, &tv);
2031 if (iRet < 0) {
2032 if (g_fLogging & NALOG_ERROR)
2033 fprintf(g_log, "SendTCPMsg/parse: select failed (%d)\n", errno);
2034 close(s);
2035 pthread_mutex_unlock(&g_xUPnPMsg);
2036 return NA_E_NET;
2037 }
2038 if (iRet == 0) {
2039 if (g_fLogging & NALOG_ERROR)
2040 fprintf(g_log, "SendTCPMsg/parse: select timed out\n");
2041 close(s);
2042 pthread_mutex_unlock(&g_xUPnPMsg);
2043 return NA_E_TIMEOUT;
2044 }
2045
2046 iRet = send(s, msg, iLen, 0);
2047
2048 // sanity check
2049 if (iRet != iLen)
2050 if (g_fLogging & NALOG_ALERT)
2051 fprintf(g_log, "SendTCPMsg: iRet (%d) != strlen(msg) (%d)!\n",
2052 iRet, iLen);
2053
2054 if (result == NULL) { // if caller just want to send/display msgs
2055 if (g_fLogging & NALOG_DUMP)
2056 fprintf(g_log, "TCP Buffer: [");
2057 }
2058
2059 if (g_fLogging & NALOG_INFO1)
2060 fprintf(g_log, "start recv @%lu\n", time(NULL));
2061
2062 iBufLen = 0;
2063 pszCurHdr = result;
2064 iContentLength = -1;
2065 iBodyOffset = -1;
2066 for (;;) {
2067 fd_set readfds;
2068 struct timeval timeout;
2069 int i;
2070
2071 FD_ZERO(&readfds);
2072 FD_SET(s, &readfds);
2073 //timeout.tv_sec = g_iFunctionTimeout / U_TOGRAN;
2074 //timeout.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN;
2075 // just do flat 2 sec now, since connection already established
2076 timeout.tv_sec = 1;
2077 timeout.tv_usec = 0;
2078
2079 iRet = select(s+1, &readfds, NULL, NULL, &timeout);
2080 if (iRet <= 0) {
2081 //fprintf(stderr, "**********: select failed (%d/%d)\n", iRet, errno);
2082 break;
2083 }
2084
2085 //gettimeofday(&tv_end, NULL);
2086 //GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed);
2087 //fprintf(stderr, "p == loop: %d.%06d\n", tv_elapsed.tv_sec, tv_elapsed.tv_usec);
2088
2089 // if only sending messages
2090 if (result == NULL) {
2091 char t[1000];
2092 i = recv(s, t, 1000-1, 0); // leave room for '\0' for dump
2093 if (i== 0) break;
2094 if (g_fLogging & NALOG_DUMP) {
2095 t[i] = '\0';
2096 fprintf(g_log, "%s", t);
2097 }
2098 continue;
2099 }
2100
2101 // EO result buf: discard extra bytes
2102 if (resultSize <= iBufLen) {
2103 char t[1000];
2104 i = recv(s, &t, 1000, 0);
2105 if (i== 0) break;
2106 // Note that there's no dump here - prevents DoS attack from
2107 // flooding the logs/diskspace
2108 continue;
2109 }
2110
2111 i = recv(s, result + iBufLen, resultSize - iBufLen, 0);
2112 if (0 == i) {
2113
2114 break;
2115 }
2116 else if (i < 0) {
2117 if (EAGAIN == errno) continue;
2118 break;
2119 }
2120
2121 iBufLen += i;
2122
2123 // parse and see if we can find content-length to quit early
2124 iContentLength = FindContentLength(result, iBufLen);
2125
2126 // now if we're still in header, see if we can find body
2127 iBodyOffset = FindBody(result, iBufLen);
2128
2129 }
2130
2131 //fprintf(stderr, "p -- \n");
2132
2133 if (g_fLogging & NALOG_INFO1)
2134 fprintf(g_log, "done recv @%lu\n", time(NULL));
2135
2136 if (result == NULL) { // if caller just want to send/display msgs
2137 if (g_fLogging & NALOG_DUMP)
2138 fprintf(g_log, "]\n");
2139 }
2140
2141 close(s);
2142 pthread_mutex_unlock(&g_xUPnPMsg);
2143 return iBufLen;
2144 }
2145
2146
2147
2148 // szSOAPMsgControlAHeaderFMT - 4 args (ctrl_url, host/port, action, length)
2149 // szSOAPMsgControlABodyFMT - 2 args (action, args string)
2150 // szSOAPMsgControlAArgumentFMT - 2 args (name/value)
2151 static PHTTPResponse SendSOAPMsgControlAction(
2152 char *action,
2153 int argc,
2154 PProperty args,
2155 int f2Part)
2156 {
2157 //char outBuffer[65536];
2158 //char outBufferBody[65536];
2159 //char outBufferArgs[65536];
2160 char *outBuffer = NULL;
2161 char *outBufferBody = NULL;
2162 char *outBufferArgs = NULL;
2163 char *inBuffer = NULL;
2164 int iLen;
2165 int iHeaderLen;
2166 int iBodyLen;
2167 int iArgsLen;
2168 int iResultLen;
2169 int i;
2170 int n;
2171 PHTTPResponse pResponse = NULL;
2172
2173
2174 if (!WaitUPnPFunction())
2175 return NULL;
2176
2177 if ((outBuffer = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) {
2178 if (g_fLogging & NALOG_ERROR)
2179 fprintf(g_log, "can't malloc for outBuffer\n");
2180 goto cleanup;
2181 }
2182 if ((outBufferBody = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) {
2183 if (g_fLogging & NALOG_ERROR)
2184 fprintf(g_log, "can't malloc for outBufferBody\n");
2185 goto cleanup;
2186 }
2187 if ((outBufferArgs = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) {
2188 if (g_fLogging & NALOG_ERROR)
2189 fprintf(g_log, "can't malloc for outBufferArgs\n");
2190 goto cleanup;
2191 }
2192 if ((inBuffer = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) {
2193 if (g_fLogging & NALOG_ERROR)
2194 fprintf(g_log, "can't malloc for inBuffer\n");
2195 goto cleanup;
2196 }
2197
2198 iArgsLen = 0;
2199 if (args != NULL)
2200 for (i=0; i<argc; i++) {
2201 n = 0;
2202 if (args[i].pszType == NULL) {
2203 n = sprintf(outBufferArgs + iArgsLen,
2204 szSOAPMsgControlAArgumentFMT,
2205 args[i].pszName, args[i].pszValue);
2206 }
2207 else {
2208 n = sprintf(outBufferArgs + iArgsLen,
2209 szSOAPMsgControlAArgumentFMT_t,
2210 args[i].pszName, args[i].pszValue, args[i].pszType);
2211 }
2212 iArgsLen += n;
2213 }
2214 outBufferArgs[iArgsLen] = '\0';
2215
2216 iBodyLen = sprintf(outBufferBody, szSOAPMsgControlABodyFMT,
2217 action, outBufferArgs);
2218
2219 iHeaderLen = sprintf(outBuffer, szSOAPMsgControlAHeaderFMT,
2220 g_szControlURL, g_szRouterHostPortSOAP, action, iBodyLen);
2221
2222 if (f2Part) {
2223 DumpHex(outBuffer, iHeaderLen+1);
2224 DumpHex(outBufferBody, iBodyLen+1);
2225 iResultLen = SendTCPMsg_saddr_2part(
2226 outBuffer, iHeaderLen,
2227 outBufferBody, iBodyLen,
2228 inBuffer, MAX_SOAPMSGSIZE,
2229 &g_saddrRouterSOAP);
2230 }
2231 else {
2232 strcpy(outBuffer + iHeaderLen, outBufferBody);
2233 iLen = iHeaderLen + iBodyLen;
2234
2235 DumpHex(outBuffer, iLen+1);
2236
2237 //strcat(outBuffer, CRLF "0" CRLF CRLF);
2238 //iLen += 7;
2239
2240 iResultLen = SendTCPMsg_saddr_parse(
2241 outBuffer, iLen,
2242 inBuffer, MAX_SOAPMSGSIZE,
2243 &g_saddrRouterSOAP);
2244 }
2245
2246 if (iResultLen > 0) {
2247 if (iResultLen > MAX_SOAPMSGSIZE) {
2248 if (g_fLogging & NALOG_ALERT)
2249 fprintf(g_log, "result truncated..\n");
2250 iResultLen = MAX_SOAPMSGSIZE;
2251 }
2252 pResponse = NewHTTPResponse_sz(inBuffer, iResultLen, FALSE);
2253 if (pResponse != NULL) {
2254 PrintHTTPResponse(pResponse);
2255 //DeleteHTTPResponse(pResponse);
2256 // - return response to caller
2257 }
2258 }
2259 else {
2260 if (g_fLogging & NALOG_ERROR)
2261 fprintf(g_log, "No TCP Response\n");
2262 //TracePrint(ELL_TRACE, "UPnP SendSOAPMsg got no TCP response (%d)\n",
2263 // iResultLen);
2264 }
2265
2266 cleanup:
2267 if (outBuffer != NULL) free(outBuffer);
2268 if (outBufferBody != NULL) free(outBufferBody);
2269 if (outBufferArgs != NULL) free(outBufferArgs);
2270 if (inBuffer != NULL) free(inBuffer);
2271
2272 return pResponse;
2273 }
2274
2275 static int FindURLBase(char *pbuf, int iLen, char *szURLBase)
2276 {
2277 // non reusable XML parsing code:
2278 // ----------------------------------------------
2279 char *p;
2280 int i = 0;
2281
2282 // now skip after end of this tag, then skip until controlURL tag
2283 p = strstr_n(pbuf, "<URLBase>", iLen);
2284 if (p == NULL) return -1;
2285
2286 // skip to the actual stuff
2287 p += sizeof("<URLBase>") - 1; // minus '\0'
2288
2289 // skip white spaces (just in case)
2290 while (isspace(*p))
2291 p++;
2292
2293 // copy into szURLBase
2294 while ((*p != '\0') && (*p != '<') && !isspace(*p)) {
2295 if (i++ > 1000) break;
2296 *szURLBase = *p;
2297 szURLBase++;
2298 p++;
2299 }
2300 *szURLBase = '\0';
2301
2302 return 0;
2303 // ----------------------------------------------
2304 }
2305
2306
2307 static int FindDescInfo(
2308 char *pbuf,
2309 int iLen,
2310 const char *szParentName,
2311 const char *szName,
2312 char *szValue)
2313 {
2314 char *p;
2315 char szSearch[100];
2316 int iSearchLen;
2317 int i = 0;
2318
2319 // find the device within pbuf
2320 p = strstr_n(
2321 pbuf,
2322 szParentName,
2323 iLen);
2324 if (p == NULL)
2325 return -1;
2326
2327 // adjust strlen
2328 iLen -= (p - pbuf);
2329 pbuf = p;
2330
2331 // now skip after end of this tag, then skip until manufacturer tag
2332 iSearchLen = sprintf(szSearch, "<%s>", szName);
2333 p = strstr_n(pbuf, szSearch, iLen);
2334 if (p == NULL) return -1;
2335 p += iSearchLen;
2336
2337 // skip white spaces (just in case)
2338 while (isspace(*p))
2339 p++;
2340
2341 // copy into szValue
2342 while ((*p != '\0') && (*p != '<')) {
2343 if (i++ > 1000) break;
2344 *szValue = *p;
2345 szValue++;
2346 p++;
2347 }
2348 *szValue = '\0';
2349
2350 return 0;
2351 }
2352
2353 static int FindIGDInfo(char *pbuf, int iLen, const char *szName, char *szValue)
2354 {
2355 return FindDescInfo(
2356 pbuf, iLen,
2357 "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
2358 szName, szValue);
2359 }
2360
2361 static int FindManufacturer(char *pbuf, int iLen, char *szManuf)
2362 {
2363 return FindIGDInfo(pbuf, iLen, "manufacturer", szManuf);
2364 }
2365
2366 static int FindFriendlyName(char *pbuf, int iLen, char *szValue)
2367 {
2368 return FindIGDInfo(pbuf, iLen, "friendlyName", szValue);
2369 }
2370
2371 static int FindModelName(char *pbuf, int iLen, char *szValue)
2372 {
2373 return FindIGDInfo(pbuf, iLen, "modelName", szValue);
2374 }
2375
2376 static int FindModelDescription(char *pbuf, int iLen, char *szValue)
2377 {
2378 return FindIGDInfo(pbuf, iLen, "modelDescription", szValue);
2379 }
2380
2381 static int FindWANIPInfo(char *pbuf, int iLen, const char *szName, char *szValue)
2382 {
2383 return FindDescInfo(
2384 pbuf, iLen,
2385 "urn:schemas-upnp-org:service:WANIPConnection:1",
2386 szName, szValue);
2387 }
2388
2389 static int FindControlURL(char *pbuf, int iLen, char *szControlURL)
2390 {
2391 return FindWANIPInfo(pbuf, iLen, "controlURL", szControlURL);
2392 }
2393
2394 static int FindEventURL(char *pbuf, int iLen, char *szEventURL)
2395 {
2396 return FindWANIPInfo(pbuf, iLen, "eventSubURL", szEventURL);
2397 }
2398
2399 static int FindRouterInfo(char *inBuffer, int iLen)
2400 {
2401 if (FindManufacturer(inBuffer, iLen, g_szManufacturer) != 0)
2402 g_szManufacturer[0] = '\0';
2403
2404 if (FindFriendlyName(inBuffer, iLen, g_szFriendlyName) != 0)
2405 g_szFriendlyName[0] = '\0';
2406
2407 if (FindModelName(inBuffer, iLen, g_szModelName) != 0)
2408 g_szModelName[0] = '\0';
2409
2410 if (FindModelDescription(inBuffer, iLen, g_szModelDescription) != 0)
2411 g_szModelDescription[0] = '\0';
2412
2413 //TracePrint(ELL_TRACE,
2414 // "UPnP Router Info:\n"
2415 // " - manufacturer [%s]\n"
2416 // " - friendly name [%s]\n"
2417 // " - model name [%s]\n"
2418 // " - model desc [%s]\n",
2419 // g_szManufacturer, g_szFriendlyName, g_szModelName, g_szModelDescription);
2420
2421 return 0;
2422 }
2423
2424 static void ParseURL(
2425 const char *szBuf, char *pszHostPort,
2426 struct sockaddr_in *psaddr, char *pszPath)
2427 {
2428 char buf[1024];
2429 char *p;
2430 char *q;
2431 unsigned short port;
2432
2433 strcpy(buf, szBuf);
2434
2435 p = buf;
2436 if (0 == strncmp(p, "http://", 7))
2437 p += 7;
2438
2439 q = strchr(p, '/');
2440
2441 if (pszPath) {
2442 if (NULL == q) {
2443 pszPath[0] = '/';
2444 pszPath[1] = '\0';
2445 }
2446 else {
2447 strcpy(pszPath, q);
2448 *q = '\0';
2449 }
2450 }
2451
2452 // find the port separetor
2453 q = strchr(p, ':');
2454 if (NULL == q)
2455 port = 80;
2456 else {
2457 port = atoi(q + 1);
2458 // HTTP's by default port 80, so don't have it in the "Host:" header
2459 if (80 == port) *q = '\0';
2460 }
2461
2462 if (pszHostPort) strcpy(pszHostPort, p);
2463
2464 if (NULL != q) *q = '\0';
2465
2466 if (NULL != psaddr) {
2467 psaddr->sin_family = AF_INET;
2468 psaddr->sin_addr.s_addr = inet_addr(p);
2469 psaddr->sin_port = htons(port);
2470 }
2471 #if 0
2472 //TracePrint(ELL_TRACE, "ParseURL [%s] -> [%s][%s] %lu.%lu.%lu.%lu:%u\n",
2473 szBuf,
2474 pszHostPort?pszHostPort:"",
2475 pszPath?pszPath:"",
2476 (psaddr->sin_addr.s_addr >> 24) & 0xff,
2477 (psaddr->sin_addr.s_addr >> 16) & 0xff,
2478 (psaddr->sin_addr.s_addr >> 8) & 0xff,
2479 (psaddr->sin_addr.s_addr >> 0) & 0xff,
2480 psaddr->sin_port);
2481 #endif
2482 }
2483
2484 static void GetDeviceDescription(void)
2485 {
2486 char *outBuffer = NULL;
2487 char *inBuffer = NULL;
2488 int iBufLen;
2489 int iLen;
2490 char szURLBase[1024];
2491 char szControlURL[1024];
2492 char szEventURL[1024];
2493
2494 if (!g_fUPnPEnabled) {
2495 if (g_fLogging & NALOG_ERROR)
2496 fprintf(g_log, "GetDeviceDescription: upnp not enabled\n");
2497 return;
2498 }
2499
2500 if ((outBuffer = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) {
2501 if (g_fLogging & NALOG_ERROR)
2502 fprintf(g_log, "can't malloc for outBuffer\n");
2503 goto cleanup;
2504 }
2505 if ((inBuffer = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) {
2506 if (g_fLogging & NALOG_ERROR)
2507 fprintf(g_log, "can't malloc for inBuffer\n");
2508 goto cleanup;
2509 }
2510
2511 iBufLen = sprintf(outBuffer, szSSDPMsgDescribeDeviceFMT, g_szNATDevDescURL,
2512 g_szRouterHostPortDesc);
2513
2514 if (g_fLogging & NALOG_INFO1)
2515 fprintf(g_log, "Describe Device: [%s]\n", outBuffer);
2516 iLen = SendTCPMsg_saddr_parse(outBuffer, iBufLen, inBuffer, MAX_SOAPMSGSIZE,
2517 &g_saddrRouterDesc);
2518
2519 g_fControlURLSet = FALSE;
2520
2521 if (FindControlURL(inBuffer, iLen, szControlURL) != 0) {
2522 if (g_fLogging & NALOG_ERROR)
2523 fprintf(g_log, "GetDeviceDesc: can't find control URL\n");
2524 goto cleanup;
2525 }
2526
2527 // start modifying global
2528 pthread_mutex_lock(&g_xUPnP);
2529
2530 {
2531 // now see if there's the URLBase
2532 if (FindURLBase(inBuffer, iLen, szURLBase) != 0) {
2533 // not there? try default numbers from device description
2534 memcpy(&g_saddrRouterBase, &g_saddrRouterDesc,
2535 sizeof(g_saddrRouterBase));
2536 strcpy(g_szRouterHostPortBase, g_szRouterHostPortDesc);
2537 }
2538 else {
2539 ParseURL(szURLBase,
2540 g_szRouterHostPortBase, &g_saddrRouterBase, NULL);
2541
2542 if ((strlen(g_szRouterHostPortBase) == 0) ||
2543 (g_saddrRouterBase.sin_addr.s_addr == INADDR_NONE)) {
2544 memcpy(&g_saddrRouterBase, &g_saddrRouterDesc,
2545 sizeof(g_saddrRouterBase));
2546 strcpy(g_szRouterHostPortBase, g_szRouterHostPortDesc);
2547 }
2548 }
2549 }
2550
2551 ParseURL(szControlURL,
2552 g_szRouterHostPortSOAP, &g_saddrRouterSOAP, g_szControlURL);
2553 if ((strlen(g_szRouterHostPortSOAP) == 0) ||
2554 (g_saddrRouterSOAP.sin_addr.s_addr == INADDR_NONE)) {
2555 memcpy(&g_saddrRouterSOAP, &g_saddrRouterBase,
2556 sizeof(g_saddrRouterSOAP));
2557 strcpy(g_szRouterHostPortSOAP, g_szRouterHostPortBase);
2558 }
2559
2560
2561 ////TracePrint(ELL_TRACE, "UPnP Control URL set to[%s][%s]...\n",
2562 // g_szRouterHostPortSOAP, g_szControlURL);
2563
2564 g_fControlURLSet = TRUE;
2565 gettimeofday(&g_tvLastUpdateTime, NULL);
2566 pthread_cond_broadcast(&g_condUPnPControlURL);
2567
2568 if (g_fLogging & NALOG_INFO1)
2569 fprintf(g_log, "Got Device Description\n");
2570
2571 // find router info
2572 FindRouterInfo(inBuffer, iLen);
2573
2574 if (FindEventURL(inBuffer, iLen, szEventURL) != 0) {
2575 szEventURL[0] = '\0';
2576 }
2577 else {
2578 ParseURL(szEventURL,
2579 g_szRouterHostPortEvent, &g_saddrRouterEvent, g_szEventURL);
2580 if ((strlen(g_szRouterHostPortEvent) == 0) ||
2581 (g_saddrRouterEvent.sin_addr.s_addr == INADDR_NONE)) {
2582 memcpy(&g_saddrRouterEvent, &g_saddrRouterBase,
2583 sizeof(g_saddrRouterEvent));
2584 strcpy(g_szRouterHostPortEvent, g_szRouterHostPortBase);
2585 }
2586
2587 EventInit();
2588 }
2589
2590 cleanup:
2591 if (outBuffer != NULL) free(outBuffer);
2592 if (inBuffer != NULL) free(inBuffer);
2593
2594 pthread_mutex_unlock(&g_xUPnP);
2595 }
2596
2597
2598 static void GetIPByName(char *hostname, unsigned long *ip_ret)
2599 {
2600 unsigned long ip;
2601
2602 ip = inet_addr(hostname);
2603 if (ip == INADDR_NONE) {
2604 struct hostent *pHEnt;
2605 pHEnt = gethostbyname(hostname);
2606 if (pHEnt == NULL) {
2607 if (g_fLogging & NALOG_ALERT)
2608 fprintf(g_log, "Can't translate [%s] to IP...\n", hostname);
2609 g_dwLocalIP = htonl(INADDR_ANY);
2610 return;
2611 }
2612 ip = ntohl(*(unsigned long *)(pHEnt->h_addr));
2613 if (g_fLogging & NALOG_INFO1)
2614 fprintf(g_log, "hostname [%s] to ip: %ld.%ld.%ld.%ld\n",
2615 hostname,
2616 (ip >> 24) & 0xff,
2617 (ip >> 16) & 0xff,
2618 (ip >> 8) & 0xff,
2619 (ip >> 0) & 0xff);
2620 }
2621 *ip_ret = ip;
2622 }
2623
2624 static void SetLocalIP()
2625 {
2626 PIPINFO pIPInfo = NULL;
2627 int count = GetIPInfo(&pIPInfo);
2628 if (NULL != pIPInfo)
2629 {
2630 // choose first non IPV6 address
2631 // iterate through array and set port information
2632 int i;
2633 unsigned long dwFirst = 0;
2634 for(i = 0; i < count; i++)
2635 {
2636 if (!(pIPInfo[i].iFlags & ISIPV6) &&
2637 (strncmp(pIPInfo[i].szIfName, "ppp", 3) != 0))
2638 {
2639 unsigned long dwTemp;
2640
2641 memcpy(&dwTemp, pIPInfo[i].abIP, sizeof(unsigned long));
2642
2643 if (0 != GetNATIPNetmask(dwTemp)) {
2644 g_dwLocalIP = dwTemp;
2645 break;
2646 }
2647
2648 if (0 == dwFirst)
2649 dwFirst = dwTemp;
2650 }
2651 }
2652 if (i == count)
2653 g_dwLocalIP = dwFirst;
2654 FreeIPInfo(pIPInfo);
2655 }
2656
2657 }
2658
2659 static int FindTagContent(const char *text, const char *tagname, char *buf)
2660 {
2661 char *p;
2662 // parse the xml
2663 p = strstr(text, tagname);
2664 if (p == NULL) {
2665 if (g_fLogging & NALOG_INFO0)
2666 fprintf(g_log, "FindTagContent: can't find %s\n", tagname);
2667 return NA_E_PARSE_ERROR;
2668 }
2669
2670 if (sscanf(p, "%*[^>]> %[^ <] <", buf) < 1) {
2671 if (g_fLogging & NALOG_INFO0)
2672 fprintf(g_log, "FindTagContent: Can't parse tag %s\n", tagname);
2673 return NA_E_PARSE_ERROR;
2674 }
2675
2676 return NA_E_SUCCESS;
2677 }
2678
2679 mStatus LNT_UnmapPort(mDNSIPPort PubPort, mDNSBool tcp)
2680 {
2681 //int iLen;
2682 char szEPort[10];
2683 //char szRemoteHost[1024];
2684 //unsigned long dwIP;
2685 Property propArgs[3];
2686 PHTTPResponse resp;
2687 unsigned short port = PubPort.NotAnInteger;
2688 int protocol = tcp ? IPPROTO_TCP : IPPROTO_UDP;
2689 sprintf(szEPort, "%u", port);
2690
2691 bzero(propArgs, sizeof(propArgs));
2692 propArgs[0].pszName = "NewRemoteHost";
2693 propArgs[0].pszValue = "";
2694 propArgs[0].pszType = "string";
2695 propArgs[1].pszName = "NewExternalPort";
2696 propArgs[1].pszValue = szEPort;
2697 propArgs[1].pszType = "ui2";
2698 propArgs[2].pszName = "NewProtocol";
2699 if (protocol == IPPROTO_TCP) {
2700 propArgs[2].pszValue = "TCP";
2701 }
2702 else if (protocol == IPPROTO_UDP) {
2703 propArgs[2].pszValue = "UDP";
2704 }
2705 else {
2706 return -1;
2707 }
2708 propArgs[2].pszType = "string";
2709
2710 resp = SendSOAPMsgControlAction(
2711 "DeletePortMapping", 3, propArgs, FALSE);
2712 if (resp == NULL) {
2713 return mStatus_NATTraversal;
2714 }
2715
2716 if (strcmp(resp->pszStatus, "200") != 0) {
2717 DeleteHTTPResponse(resp);
2718 return mStatus_NATTraversal;
2719 }
2720
2721 DeleteHTTPResponse(resp);
2722 return mStatus_NoError;
2723 }
2724
2725
2726 static int GetMappingUnused(unsigned short eport, int protocol);
2727
2728 extern mStatus LNT_MapPort(mDNSIPPort priv, mDNSIPPort pub, mDNSBool tcp)
2729 {
2730 char szEPort[6];
2731 char szIPort[6];
2732 unsigned long dwIP;
2733 char szLocalIP[30];
2734 char descr[40];
2735 Property propArgs[8];
2736 PHTTPResponse resp;
2737 unsigned short iport = priv.NotAnInteger;
2738 unsigned short eport = pub.NotAnInteger;
2739 int protocol = tcp ? IPPROTO_TCP : IPPROTO_UDP;
2740
2741
2742 if (NA_E_EXISTS == GetMappingUnused(eport, protocol))
2743 return mStatus_AlreadyRegistered;
2744
2745 //DeletePortMapping(eport, protocol);
2746
2747 sprintf(szEPort, "%u", eport);
2748
2749 sprintf(szIPort, "%u", iport);
2750
2751 dwIP = g_dwLocalIP;
2752 sprintf(szLocalIP, "%u.%u.%u.%u",
2753 (unsigned int)((dwIP >> 24) & 0xff),
2754 (unsigned int)((dwIP >> 16) & 0xff),
2755 (unsigned int)((dwIP >> 8) & 0xff),
2756 (unsigned int)((dwIP >> 0) & 0xff));
2757
2758 bzero(propArgs, sizeof(propArgs));
2759 propArgs[0].pszName = "NewRemoteHost";
2760 propArgs[0].pszValue = "";
2761 propArgs[0].pszType = "string";
2762 propArgs[1].pszName = "NewExternalPort";
2763 propArgs[1].pszValue = szEPort;
2764 propArgs[1].pszType = "ui2";
2765 propArgs[2].pszName = "NewProtocol";
2766 if (protocol == IPPROTO_TCP) {
2767 propArgs[2].pszValue = "TCP";
2768 }
2769 else if (protocol == IPPROTO_UDP) {
2770 propArgs[2].pszValue = "UDP";
2771 }
2772 else {
2773 return mStatus_BadParamErr;
2774 }
2775 propArgs[2].pszType = "string";
2776 propArgs[3].pszName = "NewInternalPort";
2777 propArgs[3].pszValue = szIPort;
2778 propArgs[3].pszType = "ui2";
2779 propArgs[4].pszName = "NewInternalClient";
2780 propArgs[4].pszValue = szLocalIP;
2781 propArgs[4].pszType = "string";
2782 propArgs[5].pszName = "NewEnabled";
2783 propArgs[5].pszValue = "1";
2784 propArgs[5].pszType = "boolean";
2785 propArgs[6].pszName = "NewPortMappingDescription";
2786 sprintf(descr, "iC%u", eport);
2787 //propArgs[6].pszValue = "V";
2788 propArgs[6].pszValue = descr;
2789 propArgs[6].pszType = "string";
2790 propArgs[7].pszName = "NewLeaseDuration";
2791 propArgs[7].pszValue = "0";
2792 propArgs[7].pszType = "ui4";
2793
2794 resp = SendSOAPMsgControlAction(
2795 "AddPortMapping", 8, propArgs, FALSE);
2796
2797 if (resp == NULL) {
2798 return mStatus_NATTraversal;
2799 }
2800
2801 if (strcmp(resp->pszStatus, "200") != 0) {
2802 DeleteHTTPResponse(resp);
2803 return mStatus_NATTraversal;
2804 }
2805
2806 DeleteHTTPResponse(resp);
2807 return mStatus_NoError;
2808 }
2809
2810 static int GetMappingUnused(unsigned short eport, int protocol)
2811 {
2812 char buf[1024];
2813 char szPort[10];
2814 Property propArgs[3];
2815 PHTTPResponse resp;
2816 unsigned long ip = 0;
2817
2818 sprintf( szPort, "%u", eport);
2819
2820 bzero(&propArgs, sizeof(propArgs));
2821 propArgs[0].pszName = "NewRemoteHost";
2822 propArgs[0].pszValue = "";
2823 propArgs[0].pszType = "string";
2824 propArgs[1].pszName = "NewExternalPort";
2825 propArgs[1].pszValue = szPort;
2826 propArgs[1].pszType = "ui2";
2827 propArgs[2].pszName = "NewProtocol";
2828 if (protocol == IPPROTO_TCP) {
2829 propArgs[2].pszValue = "TCP";
2830 }
2831 else if (protocol == IPPROTO_UDP) {
2832 propArgs[2].pszValue = "UDP";
2833 }
2834 else {
2835 return NA_E_INVALID_PARAMETER;
2836 }
2837 propArgs[2].pszType = "string";
2838
2839 resp = SendSOAPMsgControlAction(
2840 "GetSpecificPortMappingEntry", 3, propArgs, FALSE);
2841 if (resp != NULL) {
2842 if ((strcmp(resp->pszStatus, "200") == 0) &&
2843 (FindTagContent(resp->pszBody, "NewInternalClient", buf) == 0))
2844 {
2845 GetIPByName(buf, &ip);
2846 if (ip == g_dwLocalIP) {
2847 // (perhaps we let it go?)
2848 DeleteHTTPResponse(resp);
2849 return NA_E_SUCCESS;
2850 }
2851 else {
2852 DeleteHTTPResponse(resp);
2853 return NA_E_EXISTS;
2854 }
2855 }
2856 DeleteHTTPResponse(resp);
2857 }
2858
2859 return NA_E_SUCCESS;
2860 }
2861
2862 mStatus LNT_GetPublicIP(mDNSOpaque32 *IpPtr)
2863 {
2864 char buf[1024];
2865 PHTTPResponse resp;
2866 static struct timeval tvLastGoodIP = {0,0};
2867 static unsigned long dwLastGoodIP;
2868 struct timeval tv;
2869 unsigned long *ip = (unsigned long *)IpPtr;
2870 if (ip == NULL) return mStatus_BadParamErr;
2871
2872 gettimeofday(&tv, NULL);
2873 GetTimeElapsed(&tvLastGoodIP, &tv, &tv);
2874 if (tv.tv_sec < 4)
2875 {
2876 return dwLastGoodIP;
2877 }
2878
2879 resp = SendSOAPMsgControlAction(
2880 "GetExternalIPAddress", 0, NULL, FALSE);
2881
2882 if (resp == NULL)
2883 return mStatus_NATTraversal;
2884
2885 if (FindTagContent(resp->pszBody, "NewExternalIPAddress", buf) == 0) {
2886 if (g_fLogging & NALOG_INFO1)
2887 fprintf(g_log, "Mapped remote host = %s\n", buf);
2888 *ip = inet_addr(buf);
2889 DeleteHTTPResponse(resp);
2890
2891 gettimeofday(&tvLastGoodIP, NULL);
2892 dwLastGoodIP = *ip;
2893
2894 return mStatus_NoError;
2895 }
2896
2897 DeleteHTTPResponse(resp);
2898 return mStatus_NATTraversal;
2899 }
2900
2901 static void SendDiscoveryMsg()
2902 {
2903 // do it twice to avoid lost packet
2904 //SendUDPMsg(szSSDPMsgDiscoverNAT);
2905 SendUDPMsg(szSSDPMsgDiscoverRoot);
2906 SendUDPMsg(szSSDPMsgDiscoverIGD);
2907 SendUDPMsg(szSSDPMsgDiscoverNAT);
2908 }
2909
2910 // Set up threads for upnp responses, etc.
2911 int LegacyNATInit(void)
2912 {
2913 //pthread_t UDPthread;
2914 pthread_attr_t attr;
2915 int iRet;
2916 //struct timeval tv;
2917
2918 static int fFirstInitLocks = TRUE;
2919 FILE *log = NULL;
2920
2921 g_fLogging = 0;
2922 g_log = stderr;
2923
2924 SetLocalIP();
2925
2926 g_fQuit = FALSE;
2927
2928 if (fFirstInitLocks)
2929 {
2930 // init locks
2931 if (pthread_mutex_init(&g_xUPnP, NULL)) {
2932 if (g_fLogging & NALOG_ERROR)
2933 fprintf(log, "UpnpInit - mutex init failed\n");
2934 return NA_E_INTERNAL_ERROR;
2935 }
2936 if (pthread_cond_init(&g_condUPnP, NULL)) {
2937 pthread_mutex_destroy(&g_xUPnP);
2938 if (g_fLogging & NALOG_ERROR)
2939 fprintf(log, "UpnpInit - cond init failed\n");
2940 return NA_E_INTERNAL_ERROR;
2941 }
2942 if (pthread_cond_init(&g_condUPnPControlURL, NULL)) {
2943 pthread_mutex_destroy(&g_xUPnP);
2944 pthread_cond_destroy(&g_condUPnP);
2945 if (g_fLogging & NALOG_ERROR)
2946 fprintf(log, "UpnpInit - cond init failed\n");
2947 return NA_E_INTERNAL_ERROR;
2948 }
2949 if (pthread_mutex_init(&g_xUPnPMsg, NULL)) {
2950 pthread_mutex_destroy(&g_xUPnP);
2951 pthread_cond_destroy(&g_condUPnP);
2952 pthread_cond_destroy(&g_condUPnPControlURL);
2953 if (g_fLogging & NALOG_ERROR)
2954 fprintf(log, "UpnpInit - mutex init failed\n");
2955 return NA_E_INTERNAL_ERROR;
2956 }
2957
2958 fFirstInitLocks = FALSE;
2959 }
2960
2961 if (g_fFirstInit)
2962 {
2963 // initialize UDP socket for SSDP
2964 g_sUDP = SSDPListen();
2965 g_sUDPCancel = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // sock to signal canccelation to UDP thread
2966 if (g_sUDP < 0 || g_sUDPCancel < 0) {
2967 if (g_fLogging & NALOG_ERROR)
2968 fprintf(log, "UpnpInit - Failed to init multicast socket.\n");
2969 return NA_E_INTERNAL_ERROR;
2970 }
2971
2972 // make UDP thread
2973 pthread_attr_init(&attr);
2974 iRet = pthread_create(&g_UDPthread, &attr, UDPProc, log);
2975 if (iRet != 0) {
2976 g_fFirstInit = TRUE; // so we'll redo this part next time
2977 close(g_sUDP);
2978 g_sUDP = -1;
2979 if (g_fLogging & NALOG_ERROR)
2980 fprintf(log, "UpnpInit - pthread create failed (%d)\n", iRet);
2981 return NA_E_THREAD_ERROR;
2982 }
2983
2984 // set this to FALSE only if first call succeeded
2985 g_fFirstInit = FALSE;
2986
2987 //TracePrint(ELL_TRACE, "UPnP init passed\n");
2988
2989 //tv.tv_sec = 0;
2990 //tv.tv_usec = 20000; // wait 20ms for thread/udp/multicast init
2991 //select(0, 0, 0, 0, &tv);
2992 }
2993
2994 // send discovery message
2995 SendDiscoveryMsg();
2996
2997 return NA_E_SUCCESS;
2998 }
2999
3000 int LegacyNATDestroy()
3001 {
3002 void *UDPThreadRetVal;
3003 g_fQuit = TRUE;
3004 if (g_sTCPCancel >= 0) close(g_sTCPCancel);
3005 if (g_sUDPCancel >= 0) close(g_sUDPCancel);
3006 pthread_join(g_UDPthread, &UDPThreadRetVal);
3007 g_sTCPCancel = -1;
3008 g_sUDPCancel = -1;
3009 g_fFirstInit = TRUE;
3010 g_fUPnPEnabled = FALSE;
3011 g_fControlURLSet = FALSE;
3012 return NA_E_SUCCESS;
3013 }