]> git.saurik.com Git - apple/mdnsresponder.git/blame - mDNSMacOSX/LegacyNATTraversal.c
mDNSResponder-98.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / LegacyNATTraversal.c
CommitLineData
7f0064bd
A
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 $
27Revision 1.11 2004/12/03 03:34:20 ksekar
28<rdar://problem/3882674> LegacyNATTraversal.c leaks threads
29
30Revision 1.10 2004/12/01 02:43:49 cheshire
31Update copyright message
32
33Revision 1.9 2004/10/27 02:25:05 cheshire
34<rdar://problem/3816029> Random memory smashing bug
35
36Revision 1.8 2004/10/27 02:17:21 cheshire
37Turn off "safe_close: ERROR" error messages -- there are too many of them
38
39Revision 1.7 2004/10/26 21:15:40 cheshire
40<rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0
41Additional fixes: Code should set fds to -1 after closing sockets.
42
43Revision 1.6 2004/10/26 20:59:20 cheshire
44<rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0
45
46Revision 1.5 2004/10/26 01:01:35 cheshire
47Use "#if 0" instead of commenting out code
48
49Revision 1.4 2004/10/10 06:51:36 cheshire
50Declared some strings "const" as appropriate
51
52Revision 1.3 2004/09/21 23:40:12 ksekar
53<rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure
54
55Revision 1.2 2004/09/17 01:08:52 cheshire
56Renamed 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
61Revision 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
106static 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////////////////////////////////////////////////////////////////////////
185typedef struct tagProperty {
186 char *pszName;
187 char *pszValue;
188 char *pszType;
189} Property, *PProperty;
190
191typedef 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////////////////////////////////////////////////////////////////////////
206static 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
214static 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
222static 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
235static 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
251static 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
266static 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
284static 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
303static 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
309static 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
319static 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
331static 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
345static 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
358static int g_fFirstInit = TRUE;
359static int g_fQuit = FALSE;
360static FILE *g_log;
361static int g_fLogging;
362
363// Globally-accessible UDP socket
364static int g_sUDP = -1;
365static int g_sUDPCancel = -1;
366
367// Globally-accessible TCP socket
368static int g_sTCP = -1;
369static int g_sTCPCancel = -1;
370
371// Event Vars
372static int g_fEventEnabled = FALSE;
373static unsigned short g_wEventPort;
374static struct sockaddr_in g_saddrRouterEvent;
375static char g_szRouterHostPortEvent[1024];
376static char g_szEventURL[1024];
377
378// UPnP Router info
379static char g_szFriendlyName[1024];
380static char g_szManufacturer[1024];
381static char g_szModelName[1024];
382static char g_szModelDescription[1024];
383
384// URL base
385static struct sockaddr_in g_saddrRouterBase;
386static char g_szRouterHostPortBase[1024];
387
388// the threads
389static pthread_t g_UDPthread = NULL;
390static pthread_t g_TCPthread = NULL;
391
392// Local IP
393static unsigned long g_dwLocalIP = 0;
394
395// Globally accessible info about the router/UPnP
396static int g_fUPnPEnabled = FALSE;
397static char g_szUSN[1024];
398
399static struct sockaddr_in g_saddrRouterDesc;
400static char g_szRouterHostPortDesc[1024];
401static char g_szNATDevDescURL[1024];
402
403static struct sockaddr_in g_saddrRouterSOAP;
404static char g_szRouterHostPortSOAP[1024];
405static char g_szControlURL[1024];
406static int g_fControlURLSet = FALSE;
407
408// Lock/condvar for synchronous upnp calls
409static pthread_mutex_t g_xUPnP;
410static pthread_mutex_t g_xUPnPMsg;
411static pthread_cond_t g_condUPnP;
412static pthread_cond_t g_condUPnPControlURL;
413static struct timeval g_tvUPnPInitTime;
414static struct timeval g_tvLastUpdateTime;
415
416// timeout values in seconds
417static int g_iFunctionTimeout = NA_DEFAULT_FUNCTION_TIMEOUT;
418
419static void GetDeviceDescription(void);
420static 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
432typedef 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
440typedef struct hostent HOSTENT, *PHOSTENT;
441
442static 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
451static 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
563static void FreeIPInfo(PIPINFO pIPInfo)
564{
565 if (pIPInfo != NULL) free(pIPInfo);
566}
567
568
569////////////////////////////////////////////////////////////////////////
570// Function Definitions
571////////////////////////////////////////////////////////////////////////
572
573static 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
579static 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
632static 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
671static void *TCPProc(void *in);
672
673static 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
705static 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.
749static 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)
806static 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
934cleanup:
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.
945static 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
973static 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
993static 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
1136static 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
1152static 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
1171static 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
1187static 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
1218for 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
1264static 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
1285static void SetLocalIP();
1286
1287static int SendTCPMsg_saddr_parse(
1288 char *msg, int iLen,
1289 char *result, int resultSize,
1290 struct sockaddr_in *saHost);
1291
1292static 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
1447cleanup:
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
1457static 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
1561static 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
1588static 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
1614static 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"
1638static 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"
1658static 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
1675static 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;
1767gettimeofday(&tv_end, NULL);
1768GetTimeElapsed(&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;
1817gettimeofday(&tv_end, NULL);
1818GetTimeElapsed(&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
1939cleanup:
1940 close(s);
1941 return iRetcode;
1942}
1943
1944static 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)
2148static 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
2263cleanup:
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
2272static 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
2304static 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
2350static 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
2358static int FindManufacturer(char *pbuf, int iLen, char *szManuf)
2359{
2360 return FindIGDInfo(pbuf, iLen, "manufacturer", szManuf);
2361}
2362
2363static int FindFriendlyName(char *pbuf, int iLen, char *szValue)
2364{
2365 return FindIGDInfo(pbuf, iLen, "friendlyName", szValue);
2366}
2367
2368static int FindModelName(char *pbuf, int iLen, char *szValue)
2369{
2370 return FindIGDInfo(pbuf, iLen, "modelName", szValue);
2371}
2372
2373static int FindModelDescription(char *pbuf, int iLen, char *szValue)
2374{
2375 return FindIGDInfo(pbuf, iLen, "modelDescription", szValue);
2376}
2377
2378static 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
2386static int FindControlURL(char *pbuf, int iLen, char *szControlURL)
2387{
2388 return FindWANIPInfo(pbuf, iLen, "controlURL", szControlURL);
2389}
2390
2391static int FindEventURL(char *pbuf, int iLen, char *szEventURL)
2392{
2393 return FindWANIPInfo(pbuf, iLen, "eventSubURL", szEventURL);
2394}
2395
2396static 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
2421static 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
2481static 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
2587cleanup:
2588 if (outBuffer != NULL) free(outBuffer);
2589 if (inBuffer != NULL) free(inBuffer);
2590
2591 pthread_mutex_unlock(&g_xUPnP);
2592}
2593
2594
2595static 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
2621static 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
2656static 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
2676mStatus 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
2723static int GetMappingUnused(unsigned short eport, int protocol);
2724
2725extern 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
2807static 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
2859mStatus 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
2898static 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.
2908int 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
2997int 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}