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