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