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