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