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