]>
Commit | Line | Data |
---|---|---|
7f0064bd A |
1 | /* -*- Mode: C; tab-width: 4 -*- |
2 | * | |
3 | * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. | |
4 | * | |
67c8f8a1 A |
5 | * Licensed under the Apache License, Version 2.0 (the "License"); |
6 | * you may not use this file except in compliance with the License. | |
7 | * You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software | |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
7f0064bd | 15 | * limitations under the License. |
7f0064bd A |
16 | |
17 | Change History (most recent first): | |
18 | ||
19 | $Log: LegacyNATTraversal.c,v $ | |
32bb7e43 A |
20 | Revision 1.65 2009/07/03 03:16:07 jessic2 |
21 | <rdar://problem/7026146> BTMM: UPnP works in Leopard but doesn't work in SnowLeopard (URLBase is empty) Made changes to support the case where the URLBase tag exists but there isn't a valid URL | |
22 | ||
23 | Revision 1.64 2009/06/25 21:07:44 herscher | |
24 | <rdar://problem/4147784> B4W should support UPnP | |
25 | ||
26 | Revision 1.63 2009/03/26 03:59:00 jessic2 | |
27 | Changes for <rdar://problem/6492552&6492593&6492609&6492613&6492628&6492640&6492699> | |
28 | ||
29 | Revision 1.62 2009/02/13 06:31:09 cheshire | |
30 | Converted LogOperation messages to LogInfo | |
31 | ||
32 | Revision 1.61 2009/01/23 19:25:43 mcguire | |
33 | <rdar://problem/6514439> UPnP: Should not use NATErr_Refused when too many conflict retries | |
34 | ||
35 | Revision 1.60 2009/01/23 00:38:36 mcguire | |
36 | <rdar://problem/5570906> BTMM: Doesn't work with Linksys WRT54GS firmware 4.71.1 | |
37 | ||
38 | Revision 1.59 2009/01/22 20:32:17 mcguire | |
39 | <rdar://problem/6446934> BTMM: pref pane reports enabled but negotiation failed | |
40 | Make sure we push the pointer out past the LF if we read it. | |
41 | ||
42 | Revision 1.58 2009/01/22 01:15:58 mcguire | |
43 | <rdar://problem/6446934> BTMM: pref pane reports enabled but negotiation failed | |
44 | ||
45 | Revision 1.57 2008/12/19 21:09:22 mcguire | |
46 | <rdar://problem/6431147> UPnP: error messages when canceling seemingly unrelated browse | |
47 | ||
48 | Revision 1.56 2008/12/06 01:42:57 mcguire | |
49 | <rdar://problem/6418958> Need to exponentially back-off after failure to get public address | |
50 | ||
51 | Revision 1.55 2008/12/01 19:43:48 mcguire | |
52 | <rdar://problem/6404766> UPnP: Handle errorCode 718 as a conflict when requesting a port mapping | |
53 | ||
54 | Revision 1.54 2008/11/26 20:57:37 cheshire | |
55 | For consistency with other similar macros, renamed mdnsIsDigit/mdnsIsLetter/mdnsValidHostChar | |
56 | to mDNSIsDigit/mDNSIsLetter/mDNSValidHostChar | |
57 | ||
58 | Revision 1.53 2008/11/26 20:34:04 cheshire | |
59 | Changed "destroying SSDPSocket" LogOperation debugging messages to debugf | |
60 | ||
61 | Revision 1.52 2008/11/26 19:54:03 cheshire | |
62 | Changed some "LogOperation" debugging messages to "debugf" | |
63 | ||
64 | Revision 1.51 2008/11/20 02:23:38 mcguire | |
65 | <rdar://problem/6041208> need to handle URLBase | |
66 | ||
67 | Revision 1.50 2008/09/20 00:34:22 mcguire | |
51e746cf A |
68 | <rdar://problem/6129039> BTMM: Add support for WANPPPConnection |
69 | ||
32bb7e43 A |
70 | Revision 1.49 2008/08/07 21:51:13 mcguire |
71 | <rdar://problem/5904423> UPnP: Possible memory corruption bug | |
72 | <rdar://problem/5930173> UPnP: Combine URL parsing code | |
73 | ||
9f29194f A |
74 | Revision 1.48 2008/07/24 20:23:04 cheshire |
75 | <rdar://problem/3988320> Should use randomized source ports and transaction IDs to avoid DNS cache poisoning | |
76 | ||
77 | Revision 1.47 2008/07/18 21:37:46 mcguire | |
78 | <rdar://problem/5736845> BTMM: alternate SSDP queries between multicast & unicast | |
79 | ||
80 | Revision 1.46 2008/05/13 01:51:12 mcguire | |
81 | <rdar://problem/5839161> UPnP compatibility workaround for Netgear WGT624 | |
82 | ||
030b743d A |
83 | Revision 1.45 2007/12/06 00:22:27 mcguire |
84 | <rdar://problem/5604567> BTMM: Doesn't work with Linksys WAG300N 1.01.06 (sending from 1026/udp) | |
85 | ||
96f69b28 A |
86 | Revision 1.44 2007/11/02 20:45:40 cheshire |
87 | Don't log "connection failed" in customer builds | |
88 | ||
89 | Revision 1.43 2007/10/18 20:09:47 cheshire | |
90 | <rdar://problem/5545930> BTMM: Back to My Mac not working with D-Link DGL-4100 NAT gateway | |
91 | ||
92 | Revision 1.42 2007/10/16 17:37:18 cheshire | |
93 | <rdar://problem/3557903> Performance: Core code will not work on platforms with small stacks | |
94 | Cut SendSOAPMsgControlAction stack from 2144 to 96 bytes | |
95 | ||
96 | Revision 1.41 2007/10/15 23:02:00 cheshire | |
97 | Off-by-one error: Incorrect trailing zero byte on the end of the SSDP Discovery message | |
98 | ||
67c8f8a1 A |
99 | Revision 1.40 2007/09/20 21:41:49 cheshire |
100 | <rdar://problem/5495568> Legacy NAT Traversal - unmap request failed with error -65549 | |
101 | ||
102 | Revision 1.39 2007/09/20 20:41:40 cheshire | |
103 | Reordered functions in file, in preparation for following fix | |
104 | ||
105 | Revision 1.38 2007/09/18 21:42:30 cheshire | |
106 | To reduce programming mistakes, renamed ExtPort to RequestedPort | |
107 | ||
108 | Revision 1.37 2007/09/14 21:26:09 cheshire | |
109 | <rdar://problem/5482627> BTMM: Need to manually avoid port conflicts when using UPnP gateways | |
110 | ||
111 | Revision 1.36 2007/09/14 01:15:50 cheshire | |
112 | Minor fixes for problems discovered in pre-submission testing | |
113 | ||
114 | Revision 1.35 2007/09/13 00:16:42 cheshire | |
115 | <rdar://problem/5468706> Miscellaneous NAT Traversal improvements | |
116 | ||
117 | Revision 1.34 2007/09/12 23:03:08 cheshire | |
118 | <rdar://problem/5476978> DNSServiceNATPortMappingCreate callback not giving correct interface index | |
119 | ||
120 | Revision 1.33 2007/09/12 19:22:20 cheshire | |
121 | Variable renaming in preparation for upcoming fixes e.g. priv/pub renamed to intport/extport | |
122 | Made NAT Traversal packet handlers take typed data instead of anonymous "mDNSu8 *" byte pointers | |
123 | ||
124 | Revision 1.32 2007/09/11 19:19:16 cheshire | |
125 | Correct capitalization of "uPNP" to "UPnP" | |
126 | ||
127 | Revision 1.31 2007/09/10 22:14:16 cheshire | |
128 | When constructing fake NATAddrReply or NATPortMapReply packet, need to calculate | |
129 | plausible upseconds value or core logic will think NAT engine has been rebooted | |
130 | ||
131 | Revision 1.30 2007/09/05 20:46:17 cheshire | |
132 | Tidied up alignment of code layout | |
133 | ||
134 | Revision 1.29 2007/08/03 20:18:01 vazquez | |
135 | <rdar://problem/5382177> LegacyNATTraversal: reading out of bounds can lead to DoS | |
fbae3d85 | 136 | |
67c8f8a1 A |
137 | Revision 1.28 2007/07/31 02:28:36 vazquez |
138 | <rdar://problem/3734269> NAT-PMP: Detect public IP address changes and base station reboot | |
139 | ||
140 | Revision 1.27 2007/07/30 23:17:03 vazquez | |
141 | Since lease times are meaningless in UPnP, return NATMAP_DEFAULT_LEASE in UPnP port mapping reply | |
142 | ||
143 | Revision 1.26 2007/07/27 22:50:08 vazquez | |
144 | Allocate memory for UPnP request and reply buffers instead of using arrays | |
145 | ||
146 | Revision 1.25 2007/07/27 20:33:44 vazquez | |
147 | Make sure we clean up previous port mapping requests before starting an unmap | |
148 | ||
149 | Revision 1.24 2007/07/27 00:57:48 vazquez | |
150 | If a tcp connection is already established for doing a port mapping, don't start it again | |
151 | ||
152 | Revision 1.23 2007/07/26 21:19:26 vazquez | |
153 | Retry port mapping with incremented port number (up to max) in order to handle | |
154 | port mapping conflicts on UPnP gateways | |
155 | ||
156 | Revision 1.22 2007/07/25 21:41:00 vazquez | |
157 | Make sure we clean up opened sockets when there are network transitions and when changing | |
158 | port mappings | |
159 | ||
160 | Revision 1.21 2007/07/25 03:05:03 vazquez | |
161 | Fixes for: | |
162 | <rdar://problem/5338913> LegacyNATTraversal: UPnP heap overflow | |
163 | <rdar://problem/5338933> LegacyNATTraversal: UPnP stack buffer overflow | |
164 | and a myriad of other security problems | |
165 | ||
166 | Revision 1.20 2007/07/16 20:15:10 vazquez | |
167 | <rdar://problem/3867231> LegacyNATTraversal: Need complete rewrite | |
168 | ||
169 | Revision 1.19 2007/06/21 16:37:43 jgraessley | |
170 | Bug #: 5280520 | |
171 | Reviewed by: Stuart Cheshire | |
172 | Additional changes to get this compiling on the embedded platform. | |
173 | ||
174 | Revision 1.18 2007/05/09 01:43:32 cheshire | |
175 | <rdar://problem/5187028> Change sprintf and strcpy to their safer snprintf and strlcpy equivalents | |
176 | ||
177 | Revision 1.17 2007/02/27 02:48:25 cheshire | |
178 | Parameter to LNT_GetPublicIP function is IPv4 address, not anonymous "mDNSOpaque32" object | |
179 | ||
180 | Revision 1.16 2006/08/14 23:24:39 cheshire | |
181 | Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 | |
182 | ||
183 | Revision 1.15 2006/07/05 23:30:57 cheshire | |
184 | Rename LegacyNATInit() -> LNT_Init() | |
c9d2d929 A |
185 | |
186 | Revision 1.14 2005/12/08 03:00:33 cheshire | |
187 | <rdar://problem/4349971> Byte order bugs in Legacy NAT traversal code | |
188 | ||
189 | Revision 1.13 2005/09/07 18:23:05 ksekar | |
190 | <rdar://problem/4151514> Off-by-one overflow in LegacyNATTraversal | |
4aea607d | 191 | |
05292456 A |
192 | Revision 1.12 2005/07/22 21:36:16 ksekar |
193 | Fix GCC 4.0/Intel compiler warnings | |
194 | ||
7f0064bd A |
195 | Revision 1.11 2004/12/03 03:34:20 ksekar |
196 | <rdar://problem/3882674> LegacyNATTraversal.c leaks threads | |
197 | ||
198 | Revision 1.10 2004/12/01 02:43:49 cheshire | |
199 | Update copyright message | |
200 | ||
201 | Revision 1.9 2004/10/27 02:25:05 cheshire | |
202 | <rdar://problem/3816029> Random memory smashing bug | |
203 | ||
204 | Revision 1.8 2004/10/27 02:17:21 cheshire | |
205 | Turn off "safe_close: ERROR" error messages -- there are too many of them | |
206 | ||
207 | Revision 1.7 2004/10/26 21:15:40 cheshire | |
208 | <rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0 | |
209 | Additional fixes: Code should set fds to -1 after closing sockets. | |
210 | ||
211 | Revision 1.6 2004/10/26 20:59:20 cheshire | |
212 | <rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0 | |
213 | ||
214 | Revision 1.5 2004/10/26 01:01:35 cheshire | |
215 | Use "#if 0" instead of commenting out code | |
216 | ||
217 | Revision 1.4 2004/10/10 06:51:36 cheshire | |
218 | Declared some strings "const" as appropriate | |
219 | ||
220 | Revision 1.3 2004/09/21 23:40:12 ksekar | |
221 | <rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure | |
222 | ||
223 | Revision 1.2 2004/09/17 01:08:52 cheshire | |
224 | Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h | |
225 | The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces | |
226 | declared in that file are ONLY appropriate to single-address-space embedded applications. | |
227 | For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. | |
228 | ||
229 | Revision 1.1 2004/08/18 17:35:41 ksekar | |
230 | <rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways | |
67c8f8a1 | 231 | */ |
7f0064bd | 232 | |
67c8f8a1 | 233 | #ifdef _LEGACY_NAT_TRAVERSAL_ |
7f0064bd | 234 | |
67c8f8a1 A |
235 | #include "stdlib.h" // For strtol() |
236 | #include "string.h" // For strlcpy(), For strncpy(), strncasecmp() | |
32bb7e43 A |
237 | |
238 | #if defined( WIN32 ) | |
239 | # include <winsock2.h> | |
240 | # include <ws2tcpip.h> | |
241 | # define strcasecmp _stricmp | |
242 | # define strncasecmp _strnicmp | |
243 | # define mDNSASLLog( UUID, SUBDOMAIN, RESULT, SIGNATURE, FORMAT, ... ) ; | |
244 | ||
245 | static int | |
246 | inet_pton( int family, const char * addr, void * dst ) | |
247 | { | |
248 | struct sockaddr_storage ss; | |
249 | int sslen = sizeof( ss ); | |
250 | ||
251 | ZeroMemory( &ss, sizeof( ss ) ); | |
252 | ss.ss_family = family; | |
253 | ||
254 | if ( WSAStringToAddressA( addr, family, NULL, ( struct sockaddr* ) &ss, &sslen ) == 0 ) | |
255 | { | |
256 | if ( family == AF_INET ) { memcpy( dst, &( ( struct sockaddr_in* ) &ss)->sin_addr, sizeof( IN_ADDR ) ); return 1; } | |
257 | else if ( family == AF_INET6 ) { memcpy( dst, &( ( struct sockaddr_in6* ) &ss)->sin6_addr, sizeof( IN6_ADDR ) ); return 1; } | |
258 | else return 0; | |
259 | } | |
260 | else return 0; | |
261 | } | |
262 | #else | |
263 | # include <arpa/inet.h> // For inet_pton() | |
264 | #endif | |
7f0064bd A |
265 | |
266 | #include "mDNSEmbeddedAPI.h" | |
67c8f8a1 | 267 | #include "uDNS.h" // For natTraversalHandleAddressReply() etc. |
7f0064bd | 268 | |
67c8f8a1 A |
269 | // used to format SOAP port mapping arguments |
270 | typedef struct Property_struct | |
271 | { | |
272 | char *name; | |
273 | char *type; | |
274 | char *value; | |
275 | } Property; | |
276 | ||
277 | // All of the text parsing in this file is intentionally transparent so that we know exactly | |
278 | // what's being done to the text, with an eye towards preventing security problems. | |
279 | ||
280 | // This is an evolving list of useful acronyms to know. Please add to it at will. | |
281 | // ST Service Type | |
282 | // NT Notification Type | |
283 | // USN Unique Service Name | |
284 | // UDN Unique Device Name | |
285 | // UUID Universally Unique Identifier | |
286 | // URN/urn Universal Resource Name | |
287 | ||
288 | // Forward declaration because of circular reference: | |
289 | // SendPortMapRequest -> SendSOAPMsgControlAction -> MakeTCPConnection -> tcpConnectionCallback -> handleLNTPortMappingResponse | |
290 | // In the event of a port conflict, handleLNTPortMappingResponse then increments tcpInfo->retries and calls back to SendPortMapRequest to try again | |
291 | mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n); | |
292 | ||
293 | #define RequestedPortNum(n) (mDNSVal16(mDNSIPPortIsZero((n)->RequestedPort) ? (n)->IntPort : (n)->RequestedPort) + (n)->tcpInfo.retries) | |
294 | ||
32bb7e43 A |
295 | // Note that this function assumes src is already NULL terminated |
296 | mDNSlocal void AllocAndCopy(mDNSu8** dst, mDNSu8* src) | |
297 | { | |
298 | if (src == mDNSNULL) return; | |
299 | if ((*dst = (mDNSu8 *) mDNSPlatformMemAllocate(strlen((char*)src) + 1)) == mDNSNULL) { LogMsg("AllocAndCopy: can't allocate string"); return; } | |
300 | strcpy((char *)*dst, (char*)src); | |
301 | } | |
302 | ||
303 | // This function does a simple parse of an HTTP URL that may include a hostname, port, and path | |
304 | // If found in the URL, addressAndPort and path out params will point to newly allocated space (and will leak if they were previously pointing at allocated space) | |
305 | mDNSlocal mStatus ParseHttpUrl(char* ptr, char* end, mDNSu8** addressAndPort, mDNSIPPort* port, mDNSu8** path) | |
306 | { | |
307 | // if the data begins with "http://", we assume there is a hostname and possibly a port number | |
308 | if (end - ptr >= 7 && strncasecmp(ptr, "http://", 7) == 0) | |
309 | { | |
310 | int i; | |
311 | char* stop = end; | |
312 | char* addrPtr = mDNSNULL; | |
313 | ||
314 | ptr += 7; //skip over "http://" | |
315 | if (ptr >= end) { LogInfo("ParseHttpUrl: past end of buffer parsing host:port"); return mStatus_BadParamErr; } | |
316 | ||
317 | // find the end of the host:port | |
318 | addrPtr = ptr; | |
319 | for (i = 0; addrPtr && addrPtr != end; i++, addrPtr++) if (*addrPtr == '/') break; | |
320 | ||
321 | // allocate the buffer (len i+1 so we have space to terminate the string) | |
322 | if ((*addressAndPort = (mDNSu8 *) mDNSPlatformMemAllocate(i+1)) == mDNSNULL) { LogMsg("ParseHttpUrl: can't allocate address string"); return mStatus_NoMemoryErr; } | |
323 | strncpy((char *)*addressAndPort, ptr, i); | |
324 | (*addressAndPort)[i] = '\0'; | |
325 | ||
326 | // find the port number in the string, by looking backwards for the ':' | |
327 | stop = ptr; // can't go back farther than the original start | |
328 | ptr = addrPtr; // move ptr to the path part | |
329 | ||
330 | for (addrPtr--;addrPtr>stop;addrPtr--) | |
331 | { | |
332 | if (*addrPtr == ':') | |
333 | { | |
334 | int tmpport; | |
335 | addrPtr++; // skip over ':' | |
336 | tmpport = (int)strtol(addrPtr, mDNSNULL, 10); | |
337 | *port = mDNSOpaque16fromIntVal(tmpport); // store it properly converted | |
338 | break; | |
339 | } | |
340 | } | |
341 | } | |
342 | ||
343 | // ptr should now point to the first character we haven't yet processed | |
344 | // everything that remains is the path | |
345 | if (path && ptr < end) | |
346 | { | |
347 | if ((*path = (mDNSu8 *)mDNSPlatformMemAllocate(end - ptr + 1)) == mDNSNULL) { LogMsg("ParseHttpUrl: can't mDNSPlatformMemAllocate path"); return mStatus_NoMemoryErr; } | |
348 | strncpy((char *)*path, ptr, end - ptr); | |
349 | (*path)[end - ptr] = '\0'; | |
350 | } | |
351 | ||
352 | return mStatus_NoError; | |
353 | } | |
354 | ||
355 | enum | |
356 | { | |
357 | HTTPCode_NeedMoreData = -1, // No code found in stream | |
358 | HTTPCode_Other = -2, // Valid code other than those below found in stream | |
359 | HTTPCode_Bad = -3, | |
360 | HTTPCode_200 = 200, | |
361 | HTTPCode_404 = 404, | |
362 | HTTPCode_500 = 500, | |
363 | }; | |
364 | ||
365 | mDNSlocal mDNSs16 ParseHTTPResponseCode(mDNSu8** data, mDNSu8* end) | |
366 | { | |
367 | mDNSu8* ptr = *data; | |
368 | char * code; | |
369 | ||
370 | if (end - ptr < 5) return HTTPCode_NeedMoreData; | |
371 | if (strncasecmp((char*)ptr, "HTTP/", 5) != 0) return HTTPCode_Bad; | |
372 | ptr += 5; | |
373 | // should we care about the HTTP protocol version? | |
374 | ||
375 | // look for first space, which must come before first LF | |
376 | while (ptr && ptr != end) | |
377 | { | |
378 | if (*ptr == '\n') return HTTPCode_Bad; | |
379 | if (*ptr == ' ') break; | |
380 | ptr++; | |
381 | } | |
382 | if (ptr == end) return HTTPCode_NeedMoreData; | |
383 | ptr++; | |
384 | ||
385 | if (end - ptr < 3) return HTTPCode_NeedMoreData; | |
386 | ||
387 | code = (char*)ptr; | |
388 | ptr += 3; | |
389 | while (ptr && ptr != end) | |
390 | { | |
391 | if (*ptr == '\n') break; | |
392 | ptr++; | |
393 | } | |
394 | if (ptr == end) return HTTPCode_NeedMoreData; | |
395 | *data = ++ptr; | |
396 | ||
397 | if (memcmp(code, "200", 3) == 0) return HTTPCode_200; | |
398 | if (memcmp(code, "404", 3) == 0) return HTTPCode_404; | |
399 | if (memcmp(code, "500", 3) == 0) return HTTPCode_500; | |
400 | ||
401 | LogInfo("ParseHTTPResponseCode found unexpected result code: %c%c%c", code[0], code[1], code[2]); | |
402 | return HTTPCode_Other; | |
403 | } | |
404 | ||
67c8f8a1 | 405 | // This function parses the xml body of the device description response from the router. Basically, we look to make sure this is a response |
51e746cf | 406 | // referencing a service we care about (WANIPConnection or WANPPPConnection), look for the "controlURL" header immediately following, and copy the addressing and URL info we need |
67c8f8a1 | 407 | mDNSlocal void handleLNTDeviceDescriptionResponse(tcpLNTInfo *tcpInfo) |
7f0064bd | 408 | { |
9f29194f A |
409 | mDNS *m = tcpInfo->m; |
410 | char *ptr = (char *)tcpInfo->Reply; | |
411 | char *end = (char *)tcpInfo->Reply + tcpInfo->nread; | |
412 | char *stop = mDNSNULL; | |
32bb7e43 A |
413 | mDNSs16 http_result; |
414 | ||
415 | if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return; // already have the info we need | |
416 | ||
417 | http_result = ParseHTTPResponseCode((mDNSu8**)&ptr, (mDNSu8*)end); // Note: modifies ptr | |
418 | if (http_result == HTTPCode_404) LNT_ClearState(m); | |
419 | if (http_result != HTTPCode_200) | |
420 | { | |
421 | mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "noop", "HTTP Result", "HTTP code: %d", http_result); | |
422 | return; | |
423 | } | |
7f0064bd | 424 | |
51e746cf A |
425 | // Always reset our flag to use WANIPConnection. We'll use WANPPPConnection if we find it and don't find WANIPConnection. |
426 | m->UPnPWANPPPConnection = mDNSfalse; | |
427 | ||
428 | // find either service we care about | |
32bb7e43 | 429 | while (ptr && ptr < end) |
7f0064bd | 430 | { |
51e746cf | 431 | if (*ptr == 'W' && (strncasecmp(ptr, "WANIPConnection:1", 17) == 0)) break; |
67c8f8a1 | 432 | ptr++; |
7f0064bd | 433 | } |
51e746cf A |
434 | if (ptr == end) |
435 | { | |
436 | ptr = (char *)tcpInfo->Reply; | |
32bb7e43 | 437 | while (ptr && ptr < end) |
51e746cf A |
438 | { |
439 | if (*ptr == 'W' && (strncasecmp(ptr, "WANPPPConnection:1", 18) == 0)) | |
440 | { | |
441 | m->UPnPWANPPPConnection = mDNStrue; | |
442 | break; | |
443 | } | |
444 | ptr++; | |
445 | } | |
446 | } | |
32bb7e43 | 447 | if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find WANIPConnection:1 or WANPPPConnection:1 string"); return; } |
67c8f8a1 A |
448 | |
449 | // find "controlURL", starting from where we left off | |
32bb7e43 | 450 | while (ptr && ptr < end) |
7f0064bd | 451 | { |
67c8f8a1 A |
452 | if (*ptr == 'c' && (strncasecmp(ptr, "controlURL", 10) == 0)) break; // find the first 'c'; is this controlURL? if not, keep looking |
453 | ptr++; | |
7f0064bd | 454 | } |
32bb7e43 | 455 | if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find controlURL string"); return; } |
67c8f8a1 | 456 | ptr += 11; // skip over "controlURL>" |
32bb7e43 | 457 | if (ptr >= end) { LogInfo("handleLNTDeviceDescriptionResponse: past end of buffer and no body!"); return; } // check ptr again in case we skipped over the end of the buffer |
7f0064bd | 458 | |
9f29194f | 459 | // find the end of the controlURL element |
32bb7e43 | 460 | for (stop = ptr; stop < end; stop++) { if (*stop == '<') { end = stop; break; } } |
9f29194f A |
461 | |
462 | // fill in default port | |
463 | m->UPnPSOAPPort = m->UPnPRouterPort; | |
464 | ||
32bb7e43 A |
465 | // free string pointers and set to NULL |
466 | if (m->UPnPSOAPAddressString != mDNSNULL) | |
7f0064bd | 467 | { |
32bb7e43 A |
468 | mDNSPlatformMemFree(m->UPnPSOAPAddressString); |
469 | m->UPnPSOAPAddressString = mDNSNULL; | |
470 | } | |
471 | if (m->UPnPSOAPURL != mDNSNULL) | |
472 | { | |
473 | mDNSPlatformMemFree(m->UPnPSOAPURL); | |
474 | m->UPnPSOAPURL = mDNSNULL; | |
475 | } | |
476 | ||
477 | if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, &m->UPnPSOAPURL) != mStatus_NoError) return; | |
478 | // the SOAPURL should look something like "/uuid:0013-108c-4b3f0000f3dc" | |
67c8f8a1 | 479 | |
32bb7e43 A |
480 | if (m->UPnPSOAPAddressString == mDNSNULL) |
481 | { | |
482 | ptr = (char *)tcpInfo->Reply; | |
483 | while (ptr && ptr < end) | |
9f29194f | 484 | { |
32bb7e43 A |
485 | if (*ptr == 'U' && (strncasecmp(ptr, "URLBase", 7) == 0)) break; |
486 | ptr++; | |
487 | } | |
488 | ||
489 | if (ptr < end) // found URLBase | |
490 | { | |
491 | LogInfo("handleLNTDeviceDescriptionResponse: found URLBase"); | |
492 | ptr += 8; // skip over "URLBase>" | |
493 | // find the end of the URLBase element | |
494 | for (stop = ptr; stop < end; stop++) { if (*stop == '<') { end = stop; break; } } | |
495 | if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, mDNSNULL) != mStatus_NoError) | |
9f29194f | 496 | { |
32bb7e43 | 497 | LogInfo("handleLNTDeviceDescriptionResponse: failed to parse URLBase"); |
9f29194f A |
498 | } |
499 | } | |
9f29194f | 500 | |
32bb7e43 A |
501 | // if all else fails, use the router address string |
502 | if (m->UPnPSOAPAddressString == mDNSNULL) AllocAndCopy(&m->UPnPSOAPAddressString, m->UPnPRouterAddressString); | |
7f0064bd | 503 | } |
32bb7e43 A |
504 | if (m->UPnPSOAPAddressString == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPAddressString is NULL"); |
505 | else LogInfo("handleLNTDeviceDescriptionResponse: SOAP address string [%s]", m->UPnPSOAPAddressString); | |
7f0064bd | 506 | |
32bb7e43 A |
507 | if (m->UPnPSOAPURL == mDNSNULL) AllocAndCopy(&m->UPnPSOAPURL, m->UPnPRouterURL); |
508 | if (m->UPnPSOAPURL == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPURL is NULL"); | |
509 | else LogInfo("handleLNTDeviceDescriptionResponse: SOAP URL [%s]", m->UPnPSOAPURL); | |
7f0064bd A |
510 | } |
511 | ||
67c8f8a1 | 512 | mDNSlocal void handleLNTGetExternalAddressResponse(tcpLNTInfo *tcpInfo) |
7f0064bd | 513 | { |
67c8f8a1 A |
514 | mDNS *m = tcpInfo->m; |
515 | mDNSu16 err = NATErr_None; | |
516 | mDNSv4Addr ExtAddr; | |
32bb7e43 A |
517 | mDNSu8 *ptr = (mDNSu8*)tcpInfo->Reply; |
518 | mDNSu8 *end = (mDNSu8*)tcpInfo->Reply + tcpInfo->nread; | |
519 | mDNSu8 *addrend; | |
96f69b28 | 520 | static char tagname[20] = "NewExternalIPAddress"; // Array NOT including a terminating nul |
7f0064bd | 521 | |
32bb7e43 | 522 | // LogInfo("handleLNTGetExternalAddressResponse: %s", ptr); |
7f0064bd | 523 | |
32bb7e43 A |
524 | mDNSs16 http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr |
525 | if (http_result == HTTPCode_404) LNT_ClearState(m); | |
526 | if (http_result != HTTPCode_200) | |
527 | { | |
528 | mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "HTTP Result", "HTTP code: %d", http_result); | |
529 | return; | |
530 | } | |
531 | ||
532 | ||
533 | while (ptr < end && strncasecmp((char*)ptr, tagname, sizeof(tagname))) ptr++; | |
96f69b28 A |
534 | ptr += sizeof(tagname); // Skip over "NewExternalIPAddress" |
535 | while (ptr < end && *ptr != '>') ptr++; | |
536 | ptr += 1; // Skip over ">" | |
537 | // Find the end of the address and terminate the string so inet_pton() can convert it | |
538 | addrend = ptr; | |
32bb7e43 | 539 | while (addrend < end && (mDNSIsDigit(*addrend) || *addrend == '.')) addrend++; |
96f69b28 A |
540 | if (addrend >= end) return; |
541 | *addrend = 0; | |
7f0064bd | 542 | |
32bb7e43 A |
543 | if (inet_pton(AF_INET, (char*)ptr, &ExtAddr) <= 0) |
544 | { | |
545 | mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "inet_pton", ""); | |
546 | LogMsg("handleLNTGetExternalAddressResponse: Router returned bad address %s", ptr); | |
547 | err = NATErr_NetFail; | |
548 | ExtAddr = zerov4Addr; | |
549 | } | |
550 | if (!err) LogInfo("handleLNTGetExternalAddressResponse: External IP address is %.4a", &ExtAddr); | |
7f0064bd | 551 | |
67c8f8a1 A |
552 | natTraversalHandleAddressReply(m, err, ExtAddr); |
553 | } | |
7f0064bd | 554 | |
67c8f8a1 | 555 | mDNSlocal void handleLNTPortMappingResponse(tcpLNTInfo *tcpInfo) |
7f0064bd | 556 | { |
67c8f8a1 A |
557 | mDNS *m = tcpInfo->m; |
558 | mDNSIPPort extport = zeroIPPort; | |
32bb7e43 A |
559 | mDNSu8 *ptr = (mDNSu8*)tcpInfo->Reply; |
560 | mDNSu8 *end = (mDNSu8*)tcpInfo->Reply + tcpInfo->nread; | |
67c8f8a1 | 561 | NATTraversalInfo *natInfo; |
32bb7e43 | 562 | mDNSs16 http_result; |
7f0064bd | 563 | |
67c8f8a1 | 564 | for (natInfo = m->NATTraversals; natInfo; natInfo=natInfo->next) { if (natInfo == tcpInfo->parentNATInfo) break; } |
96f69b28 | 565 | |
32bb7e43 | 566 | if (!natInfo) { LogInfo("handleLNTPortMappingResponse: can't find matching tcpInfo in NATTraversals!"); return; } |
7f0064bd | 567 | |
32bb7e43 A |
568 | http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr |
569 | if (http_result == HTTPCode_200) | |
570 | { | |
571 | LogInfo("handleLNTPortMappingResponse: got a valid response, sending reply to natTraversalHandlePortMapReply(internal %d external %d retries %d)", | |
572 | mDNSVal16(natInfo->IntPort), RequestedPortNum(natInfo), tcpInfo->retries); | |
573 | ||
574 | // Make sure to compute extport *before* we zero tcpInfo->retries | |
575 | extport = mDNSOpaque16fromIntVal(RequestedPortNum(natInfo)); | |
576 | tcpInfo->retries = 0; | |
577 | natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, mStatus_NoError, extport, NATMAP_DEFAULT_LEASE); | |
578 | } | |
579 | else if (http_result == HTTPCode_500) | |
67c8f8a1 | 580 | { |
32bb7e43 | 581 | while (ptr && ptr != end) |
67c8f8a1 | 582 | { |
32bb7e43 | 583 | if (((*ptr == 'c' || *ptr == 'C') && end - ptr >= 8 && strncasecmp((char*)ptr, "Conflict", 8) == 0) || (*ptr == '>' && end - ptr >= 15 && strncasecmp((char*)ptr, ">718</errorCode", 15) == 0)) |
67c8f8a1 | 584 | { |
32bb7e43 A |
585 | if (tcpInfo->retries < 100) |
586 | { | |
587 | tcpInfo->retries++; SendPortMapRequest(tcpInfo->m, natInfo); | |
588 | mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict", "Retry %d", tcpInfo->retries); | |
589 | } | |
590 | else | |
67c8f8a1 | 591 | { |
32bb7e43 A |
592 | LogMsg("handleLNTPortMappingResponse too many conflict retries %d %d", mDNSVal16(natInfo->IntPort), mDNSVal16(natInfo->RequestedPort)); |
593 | mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict - too many retries", "Retries: %d", tcpInfo->retries); | |
594 | natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0); | |
67c8f8a1 | 595 | } |
32bb7e43 | 596 | return; |
7f0064bd | 597 | } |
32bb7e43 | 598 | ptr++; |
7f0064bd | 599 | } |
7f0064bd | 600 | } |
32bb7e43 A |
601 | else if (http_result == HTTPCode_Bad) LogMsg("handleLNTPortMappingResponse got data that was not a valid HTTP response"); |
602 | else if (http_result == HTTPCode_Other) LogMsg("handleLNTPortMappingResponse got unexpected response code"); | |
603 | else if (http_result == HTTPCode_404) LNT_ClearState(m); | |
604 | if (http_result != HTTPCode_200 && http_result != HTTPCode_500) | |
605 | mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "HTTP Result", "HTTP code: %d", http_result); | |
7f0064bd A |
606 | } |
607 | ||
67c8f8a1 A |
608 | mDNSlocal void DisposeInfoFromUnmapList(mDNS *m, tcpLNTInfo *tcpInfo) |
609 | { | |
610 | tcpLNTInfo **ptr = &m->tcpInfoUnmapList; | |
611 | while (*ptr && *ptr != tcpInfo) ptr = &(*ptr)->next; | |
612 | if (*ptr) { *ptr = (*ptr)->next; mDNSPlatformMemFree(tcpInfo); } // If we found it, cut it from our list and free the memory | |
7f0064bd A |
613 | } |
614 | ||
67c8f8a1 A |
615 | mDNSlocal void tcpConnectionCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEstablished, mStatus err) |
616 | { | |
617 | mStatus status = mStatus_NoError; | |
618 | tcpLNTInfo *tcpInfo = (tcpLNTInfo *)context; | |
619 | mDNSBool closed = mDNSfalse; | |
620 | long n = 0; | |
621 | long nsent = 0; | |
7f0064bd | 622 | |
32bb7e43 | 623 | if (tcpInfo == mDNSNULL) { LogInfo("tcpConnectionCallback: no tcpInfo context"); status = mStatus_Invalid; goto exit; } |
7f0064bd | 624 | |
67c8f8a1 A |
625 | // The handlers below expect to be called with the lock held |
626 | mDNS_Lock(tcpInfo->m); | |
627 | ||
32bb7e43 | 628 | if (err) { LogInfo("tcpConnectionCallback: received error"); goto exit; } |
7f0064bd | 629 | |
67c8f8a1 A |
630 | if (ConnectionEstablished) // connection is established - send the message |
631 | { | |
32bb7e43 | 632 | LogInfo("tcpConnectionCallback: connection established, sending message"); |
67c8f8a1 A |
633 | nsent = mDNSPlatformWriteTCP(sock, (char *)tcpInfo->Request, tcpInfo->requestLen); |
634 | if (nsent != (long)tcpInfo->requestLen) { LogMsg("tcpConnectionCallback: error writing"); status = mStatus_UnknownErr; goto exit; } | |
7f0064bd | 635 | } |
67c8f8a1 A |
636 | else |
637 | { | |
638 | n = mDNSPlatformReadTCP(sock, (char *)tcpInfo->Reply + tcpInfo->nread, tcpInfo->replyLen - tcpInfo->nread, &closed); | |
32bb7e43 | 639 | LogInfo("tcpConnectionCallback: mDNSPlatformReadTCP read %d bytes", n); |
7f0064bd | 640 | |
32bb7e43 A |
641 | if (n < 0) { LogInfo("tcpConnectionCallback - read returned %d", n); status = mStatus_ConnFailed; goto exit; } |
642 | else if (closed) { LogInfo("tcpConnectionCallback: socket closed by remote end %d", tcpInfo->nread); status = mStatus_ConnFailed; goto exit; } | |
7f0064bd | 643 | |
67c8f8a1 | 644 | tcpInfo->nread += n; |
32bb7e43 | 645 | LogInfo("tcpConnectionCallback tcpInfo->nread %d", tcpInfo->nread); |
67c8f8a1 A |
646 | if (tcpInfo->nread > LNT_MAXBUFSIZE) |
647 | { | |
32bb7e43 | 648 | LogInfo("result truncated..."); |
67c8f8a1 | 649 | tcpInfo->nread = LNT_MAXBUFSIZE; |
7f0064bd | 650 | } |
7f0064bd | 651 | |
67c8f8a1 A |
652 | switch (tcpInfo->op) |
653 | { | |
654 | case LNTDiscoveryOp: handleLNTDeviceDescriptionResponse (tcpInfo); break; | |
655 | case LNTExternalAddrOp: handleLNTGetExternalAddressResponse(tcpInfo); break; | |
656 | case LNTPortMapOp: handleLNTPortMappingResponse (tcpInfo); break; | |
657 | case LNTPortMapDeleteOp: status = mStatus_ConfigChanged; break; | |
658 | default: LogMsg("tcpConnectionCallback: bad tcp operation! %d", tcpInfo->op); status = mStatus_Invalid; break; | |
7f0064bd | 659 | } |
7f0064bd | 660 | } |
67c8f8a1 A |
661 | exit: |
662 | if (err || status) | |
7f0064bd | 663 | { |
32bb7e43 A |
664 | mDNS *m = tcpInfo->m; |
665 | switch (tcpInfo->op) | |
666 | { | |
667 | case LNTDiscoveryOp: if (m->UPnPSOAPAddressString == mDNSNULL) | |
668 | mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP Address", ""); | |
669 | if (m->UPnPSOAPURL == mDNSNULL) | |
670 | mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP path", ""); | |
671 | if (m->UPnPSOAPAddressString && m->UPnPSOAPURL) | |
672 | mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "success", "success", ""); | |
673 | break; | |
674 | case LNTExternalAddrOp: mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success", mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success", ""); | |
675 | break; | |
676 | case LNTPortMapOp: if (tcpInfo->parentNATInfo) | |
677 | mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", (tcpInfo->parentNATInfo->Result) ? "failure" : "success", | |
678 | (tcpInfo->parentNATInfo->Result) ? "failure" : "success", "Result: %d", tcpInfo->parentNATInfo->Result); | |
679 | break; | |
680 | case LNTPortMapDeleteOp: break; | |
681 | default: break; | |
682 | } | |
683 | ||
67c8f8a1 A |
684 | mDNSPlatformTCPCloseConnection(tcpInfo->sock); |
685 | tcpInfo->sock = mDNSNULL; | |
686 | if (tcpInfo->Request) { mDNSPlatformMemFree(tcpInfo->Request); tcpInfo->Request = mDNSNULL; } | |
687 | if (tcpInfo->Reply ) { mDNSPlatformMemFree(tcpInfo->Reply); tcpInfo->Reply = mDNSNULL; } | |
7f0064bd A |
688 | } |
689 | ||
67c8f8a1 | 690 | if (tcpInfo) mDNS_Unlock(tcpInfo->m); |
7f0064bd | 691 | |
67c8f8a1 | 692 | if (status == mStatus_ConfigChanged) DisposeInfoFromUnmapList(tcpInfo->m, tcpInfo); |
7f0064bd | 693 | } |
7f0064bd | 694 | |
67c8f8a1 | 695 | mDNSlocal mStatus MakeTCPConnection(mDNS *const m, tcpLNTInfo *info, const mDNSAddr *const Addr, const mDNSIPPort Port, LNTOp_t op) |
7f0064bd | 696 | { |
67c8f8a1 A |
697 | mStatus err = mStatus_NoError; |
698 | mDNSIPPort srcport = zeroIPPort; | |
699 | ||
700 | if (mDNSIPv4AddressIsZero(Addr->ip.v4) || mDNSIPPortIsZero(Port)) | |
701 | { LogMsg("LNT MakeTCPConnection: bad address/port %#a:%d", Addr, mDNSVal16(Port)); return(mStatus_Invalid); } | |
702 | info->m = m; | |
703 | info->Address = *Addr; | |
704 | info->Port = Port; | |
705 | info->op = op; | |
706 | info->nread = 0; | |
707 | info->replyLen = LNT_MAXBUFSIZE; | |
708 | if (info->Reply != mDNSNULL) mDNSPlatformMemZero(info->Reply, LNT_MAXBUFSIZE); // reuse previously allocated buffer | |
32bb7e43 | 709 | else if ((info->Reply = (mDNSs8 *) mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate reply buffer"); return (mStatus_NoMemoryErr); } |
67c8f8a1 | 710 | |
32bb7e43 | 711 | if (info->sock) { LogInfo("MakeTCPConnection: closing previous open connection"); mDNSPlatformTCPCloseConnection(info->sock); info->sock = mDNSNULL; } |
67c8f8a1 A |
712 | info->sock = mDNSPlatformTCPSocket(m, kTCPSocketFlags_Zero, &srcport); |
713 | if (!info->sock) { LogMsg("LNT MakeTCPConnection: unable to create TCP socket"); mDNSPlatformMemFree(info->Reply); info->Reply = mDNSNULL; return(mStatus_NoMemoryErr); } | |
32bb7e43 | 714 | LogInfo("MakeTCPConnection: connecting to %#a:%d", &info->Address, mDNSVal16(info->Port)); |
67c8f8a1 A |
715 | err = mDNSPlatformTCPConnect(info->sock, Addr, Port, 0, tcpConnectionCallback, info); |
716 | ||
717 | if (err == mStatus_ConnPending) err = mStatus_NoError; | |
718 | else if (err == mStatus_ConnEstablished) | |
7f0064bd | 719 | { |
67c8f8a1 A |
720 | mDNS_DropLockBeforeCallback(); |
721 | tcpConnectionCallback(info->sock, info, mDNStrue, mStatus_NoError); | |
722 | mDNS_ReclaimLockAfterCallback(); | |
723 | err = mStatus_NoError; | |
7f0064bd | 724 | } |
67c8f8a1 | 725 | else |
7f0064bd | 726 | { |
96f69b28 | 727 | // Don't need to log this in customer builds -- it happens quite often during sleep, wake, configuration changes, etc. |
32bb7e43 | 728 | LogInfo("LNT MakeTCPConnection: connection failed"); |
67c8f8a1 A |
729 | mDNSPlatformTCPCloseConnection(info->sock); // Dispose the socket we created with mDNSPlatformTCPSocket() above |
730 | info->sock = mDNSNULL; | |
731 | mDNSPlatformMemFree(info->Reply); | |
732 | info->Reply = mDNSNULL; | |
7f0064bd | 733 | } |
67c8f8a1 | 734 | return(err); |
7f0064bd A |
735 | } |
736 | ||
67c8f8a1 | 737 | mDNSlocal unsigned int AddSOAPArguments(char *buf, unsigned int maxlen, int numArgs, Property *a) |
7f0064bd | 738 | { |
67c8f8a1 A |
739 | static const char f1[] = "<%s>%s</%s>"; |
740 | static const char f2[] = "<%s xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"%s\">%s</%s>"; | |
741 | int i, len = 0; | |
742 | *buf = 0; | |
743 | for (i = 0; i < numArgs; i++) | |
7f0064bd | 744 | { |
67c8f8a1 A |
745 | if (a[i].type) len += mDNS_snprintf(buf + len, maxlen - len, f2, a[i].name, a[i].type, a[i].value, a[i].name); |
746 | else len += mDNS_snprintf(buf + len, maxlen - len, f1, a[i].name, a[i].value, a[i].name); | |
7f0064bd | 747 | } |
67c8f8a1 A |
748 | return(len); |
749 | } | |
7f0064bd | 750 | |
67c8f8a1 A |
751 | mDNSlocal mStatus SendSOAPMsgControlAction(mDNS *m, tcpLNTInfo *info, char *Action, int numArgs, Property *Arguments, LNTOp_t op) |
752 | { | |
753 | // SOAP message header format - | |
754 | // - control URL | |
755 | // - action (string) | |
756 | // - router's host/port ("host:port") | |
757 | // - content-length | |
758 | static const char header[] = | |
759 | "POST %s HTTP/1.1\r\n" | |
760 | "Content-Type: text/xml; charset=\"utf-8\"\r\n" | |
51e746cf | 761 | "SOAPAction: \"urn:schemas-upnp-org:service:WAN%sConnection:1#%s\"\r\n" |
67c8f8a1 A |
762 | "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n" |
763 | "Host: %s\r\n" | |
764 | "Content-Length: %d\r\n" | |
765 | "Connection: close\r\n" | |
766 | "Pragma: no-cache\r\n" | |
767 | "\r\n" | |
768 | "%s\r\n"; | |
769 | ||
770 | static const char body1[] = | |
771 | "<?xml version=\"1.0\"?>\r\n" | |
772 | "<SOAP-ENV:Envelope" | |
773 | " xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"" | |
774 | " SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" | |
775 | "<SOAP-ENV:Body>" | |
51e746cf | 776 | "<m:%s xmlns:m=\"urn:schemas-upnp-org:service:WAN%sConnection:1\">"; |
67c8f8a1 A |
777 | |
778 | static const char body2[] = | |
779 | "</m:%s>" | |
780 | "</SOAP-ENV:Body>" | |
781 | "</SOAP-ENV:Envelope>\r\n"; | |
782 | ||
783 | mStatus err; | |
96f69b28 | 784 | char *body = (char *)&m->omsg; // Typically requires 1110-1122 bytes; m->omsg is 8952 bytes, which is plenty |
67c8f8a1 A |
785 | int bodyLen; |
786 | ||
32bb7e43 A |
787 | if (mDNSIPPortIsZero(m->UPnPSOAPPort) || m->UPnPSOAPURL == mDNSNULL || m->UPnPSOAPAddressString == mDNSNULL) // if no SOAP URL or address exists get out here |
788 | { LogInfo("SendSOAPMsgControlAction: no SOAP port, URL or address string"); return mStatus_Invalid; } | |
67c8f8a1 A |
789 | |
790 | // Create body | |
51e746cf | 791 | bodyLen = mDNS_snprintf (body, sizeof(m->omsg), body1, Action, m->UPnPWANPPPConnection ? "PPP" : "IP"); |
96f69b28 A |
792 | bodyLen += AddSOAPArguments(body + bodyLen, sizeof(m->omsg) - bodyLen, numArgs, Arguments); |
793 | bodyLen += mDNS_snprintf (body + bodyLen, sizeof(m->omsg) - bodyLen, body2, Action); | |
67c8f8a1 A |
794 | |
795 | // Create info->Request; the header needs to contain the bodyLen in the "Content-Length" field | |
796 | if (!info->Request) info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE); | |
797 | if (!info->Request) { LogMsg("SendSOAPMsgControlAction: Can't allocate info->Request"); return mStatus_NoMemoryErr; } | |
51e746cf | 798 | info->requestLen = mDNS_snprintf((char *)info->Request, LNT_MAXBUFSIZE, header, m->UPnPSOAPURL, m->UPnPWANPPPConnection ? "PPP" : "IP", Action, m->UPnPSOAPAddressString, bodyLen, body); |
67c8f8a1 A |
799 | |
800 | err = MakeTCPConnection(m, info, &m->Router, m->UPnPSOAPPort, op); | |
801 | if (err) { mDNSPlatformMemFree(info->Request); info->Request = mDNSNULL; } | |
802 | return err; | |
803 | } | |
804 | ||
805 | // Build port mapping request with new port (up to max) and send it | |
806 | mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n) | |
807 | { | |
808 | char externalPort[6]; | |
809 | char internalPort[6]; | |
810 | char localIPAddrString[30]; | |
811 | char publicPortString[40]; | |
812 | Property propArgs[8]; | |
813 | mDNSu16 ReqPortNum = RequestedPortNum(n); | |
814 | NATTraversalInfo *n2 = m->NATTraversals; | |
815 | ||
816 | // Scan our m->NATTraversals list to make sure the external port we're requesting is locally unique. | |
817 | // UPnP gateways will report conflicts if different devices request the same external port, but if two | |
818 | // clients on the same device request the same external port the second one just stomps over the first. | |
819 | // One way this can happen is like this: | |
820 | // 1. Client A binds local port 80 | |
821 | // 2. Client A requests external port 80 -> internal port 80 | |
822 | // 3. UPnP NAT gateway refuses external port 80 (some other client already has it) | |
823 | // 4. Client A tries again, and successfully gets external port 80 -> internal port 81 | |
824 | // 5. Client B on same machine tries to bind local port 80, and fails | |
825 | // 6. Client B tries again, and successfully binds local port 81 | |
826 | // 7. Client B now requests external port 81 -> internal port 81 | |
827 | // 8. UPnP NAT gateway allows this, stomping over Client A's existing mapping | |
828 | ||
829 | while (n2) | |
7f0064bd | 830 | { |
67c8f8a1 | 831 | if (n2 == n || RequestedPortNum(n2) != ReqPortNum) n2=n2->next; |
7f0064bd | 832 | else |
7f0064bd | 833 | { |
67c8f8a1 | 834 | if (n->tcpInfo.retries < 100) |
7f0064bd | 835 | { |
67c8f8a1 A |
836 | n->tcpInfo.retries++; |
837 | ReqPortNum = RequestedPortNum(n); // Pick a new port number | |
838 | n2 = m->NATTraversals; // And re-scan the list looking for conflicts | |
7f0064bd | 839 | } |
67c8f8a1 | 840 | else |
7f0064bd | 841 | { |
32bb7e43 | 842 | natTraversalHandlePortMapReply(m, n, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0); |
67c8f8a1 | 843 | return mStatus_NoError; |
7f0064bd A |
844 | } |
845 | } | |
7f0064bd | 846 | } |
7f0064bd | 847 | |
67c8f8a1 A |
848 | // create strings to use in the message |
849 | mDNS_snprintf(externalPort, sizeof(externalPort), "%u", ReqPortNum); | |
850 | mDNS_snprintf(internalPort, sizeof(internalPort), "%u", mDNSVal16(n->IntPort)); | |
851 | mDNS_snprintf(publicPortString, sizeof(publicPortString), "iC%u", ReqPortNum); | |
852 | mDNS_snprintf(localIPAddrString, sizeof(localIPAddrString), "%u.%u.%u.%u", | |
853 | m->AdvertisedV4.ip.v4.b[0], m->AdvertisedV4.ip.v4.b[1], m->AdvertisedV4.ip.v4.b[2], m->AdvertisedV4.ip.v4.b[3]); | |
854 | ||
855 | // build the message | |
856 | mDNSPlatformMemZero(propArgs, sizeof(propArgs)); | |
857 | propArgs[0].name = "NewRemoteHost"; | |
858 | propArgs[0].type = "string"; | |
859 | propArgs[0].value = ""; | |
860 | propArgs[1].name = "NewExternalPort"; | |
861 | propArgs[1].type = "ui2"; | |
862 | propArgs[1].value = externalPort; | |
863 | propArgs[2].name = "NewProtocol"; | |
864 | propArgs[2].type = "string"; | |
865 | propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP"; | |
866 | propArgs[3].name = "NewInternalPort"; | |
867 | propArgs[3].type = "ui2"; | |
868 | propArgs[3].value = internalPort; | |
869 | propArgs[4].name = "NewInternalClient"; | |
870 | propArgs[4].type = "string"; | |
871 | propArgs[4].value = localIPAddrString; | |
872 | propArgs[5].name = "NewEnabled"; | |
873 | propArgs[5].type = "boolean"; | |
874 | propArgs[5].value = "1"; | |
875 | propArgs[6].name = "NewPortMappingDescription"; | |
876 | propArgs[6].type = "string"; | |
877 | propArgs[6].value = publicPortString; | |
878 | propArgs[7].name = "NewLeaseDuration"; | |
879 | propArgs[7].type = "ui4"; | |
880 | propArgs[7].value = "0"; | |
881 | ||
32bb7e43 | 882 | LogInfo("SendPortMapRequest: internal %u external %u", mDNSVal16(n->IntPort), ReqPortNum); |
67c8f8a1 A |
883 | return SendSOAPMsgControlAction(m, &n->tcpInfo, "AddPortMapping", 8, propArgs, LNTPortMapOp); |
884 | } | |
885 | ||
886 | mDNSexport mStatus LNT_MapPort(mDNS *m, NATTraversalInfo *n) | |
887 | { | |
32bb7e43 | 888 | LogInfo("LNT_MapPort"); |
67c8f8a1 A |
889 | if (n->tcpInfo.sock) return(mStatus_NoError); // If we already have a connection up don't make another request for the same thing |
890 | n->tcpInfo.parentNATInfo = n; | |
891 | n->tcpInfo.retries = 0; | |
892 | return SendPortMapRequest(m, n); | |
7f0064bd A |
893 | } |
894 | ||
67c8f8a1 A |
895 | mDNSexport mStatus LNT_UnmapPort(mDNS *m, NATTraversalInfo *n) |
896 | { | |
897 | char externalPort[10]; | |
898 | Property propArgs[3]; | |
899 | tcpLNTInfo *info; | |
900 | tcpLNTInfo **infoPtr = &m->tcpInfoUnmapList; | |
901 | mStatus err; | |
902 | ||
903 | // If no NAT gateway to talk to, no need to do all this work for nothing | |
32bb7e43 | 904 | if (mDNSIPPortIsZero(m->UPnPSOAPPort) || !m->UPnPSOAPURL || !m->UPnPSOAPAddressString) return mStatus_NoError; |
67c8f8a1 A |
905 | |
906 | mDNS_snprintf(externalPort, sizeof(externalPort), "%u", mDNSVal16(mDNSIPPortIsZero(n->RequestedPort) ? n->IntPort : n->RequestedPort)); | |
907 | ||
908 | mDNSPlatformMemZero(propArgs, sizeof(propArgs)); | |
909 | propArgs[0].name = "NewRemoteHost"; | |
910 | propArgs[0].type = "string"; | |
911 | propArgs[0].value = ""; | |
912 | propArgs[1].name = "NewExternalPort"; | |
913 | propArgs[1].type = "ui2"; | |
914 | propArgs[1].value = externalPort; | |
915 | propArgs[2].name = "NewProtocol"; | |
916 | propArgs[2].type = "string"; | |
917 | propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP"; | |
918 | ||
919 | n->tcpInfo.parentNATInfo = n; | |
920 | ||
921 | // clean up previous port mapping requests and allocations | |
32bb7e43 | 922 | if (n->tcpInfo.sock) LogInfo("LNT_UnmapPort: closing previous open connection"); |
67c8f8a1 A |
923 | if (n->tcpInfo.sock ) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; } |
924 | if (n->tcpInfo.Request) { mDNSPlatformMemFree(n->tcpInfo.Request); n->tcpInfo.Request = mDNSNULL; } | |
925 | if (n->tcpInfo.Reply ) { mDNSPlatformMemFree(n->tcpInfo.Reply); n->tcpInfo.Reply = mDNSNULL; } | |
926 | ||
927 | // make a copy of the tcpInfo that we can clean up later (the one passed in will be destroyed by the client as soon as this returns) | |
928 | if ((info = mDNSPlatformMemAllocate(sizeof(tcpLNTInfo))) == mDNSNULL) | |
32bb7e43 | 929 | { LogInfo("LNT_UnmapPort: can't allocate tcpInfo"); return(mStatus_NoMemoryErr); } |
67c8f8a1 A |
930 | *info = n->tcpInfo; |
931 | ||
932 | while (*infoPtr) infoPtr = &(*infoPtr)->next; // find the end of the list | |
933 | *infoPtr = info; // append | |
7f0064bd | 934 | |
67c8f8a1 A |
935 | err = SendSOAPMsgControlAction(m, info, "DeletePortMapping", 3, propArgs, LNTPortMapDeleteOp); |
936 | if (err) DisposeInfoFromUnmapList(m, info); | |
937 | return err; | |
7f0064bd A |
938 | } |
939 | ||
67c8f8a1 A |
940 | mDNSexport mStatus LNT_GetExternalAddress(mDNS *m) |
941 | { | |
942 | return SendSOAPMsgControlAction(m, &m->tcpAddrInfo, "GetExternalIPAddress", 0, mDNSNULL, LNTExternalAddrOp); | |
7f0064bd A |
943 | } |
944 | ||
67c8f8a1 A |
945 | mDNSlocal mStatus GetDeviceDescription(mDNS *m, tcpLNTInfo *info) |
946 | { | |
947 | // Device description format - | |
948 | // - device description URL | |
949 | // - host/port | |
950 | static const char szSSDPMsgDescribeDeviceFMT[] = | |
951 | "GET %s HTTP/1.1\r\n" | |
952 | "Accept: text/xml, application/xml\r\n" | |
953 | "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n" | |
954 | "Host: %s\r\n" | |
955 | "Connection: close\r\n" | |
956 | "\r\n"; | |
957 | ||
32bb7e43 A |
958 | if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return mStatus_NoError; // already have the info we need |
959 | ||
960 | if (m->UPnPRouterURL == mDNSNULL || m->UPnPRouterAddressString == mDNSNULL) { LogInfo("GetDeviceDescription: no router URL or address string!"); return (mStatus_Invalid); } | |
67c8f8a1 A |
961 | |
962 | // build message | |
963 | if (info->Request != mDNSNULL) mDNSPlatformMemZero(info->Request, LNT_MAXBUFSIZE); // reuse previously allocated buffer | |
32bb7e43 | 964 | else if ((info->Request = (mDNSs8 *) mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate send buffer for discovery"); return (mStatus_NoMemoryErr); } |
67c8f8a1 | 965 | info->requestLen = mDNS_snprintf((char *)info->Request, LNT_MAXBUFSIZE, szSSDPMsgDescribeDeviceFMT, m->UPnPRouterURL, m->UPnPRouterAddressString); |
32bb7e43 | 966 | LogInfo("Describe Device: [%s]", info->Request); |
67c8f8a1 A |
967 | return MakeTCPConnection(m, info, &m->Router, m->UPnPRouterPort, LNTDiscoveryOp); |
968 | } | |
969 | ||
970 | // This function parses the response to our SSDP discovery message. Basically, we look to make sure this is a response | |
51e746cf | 971 | // referencing a service we care about (WANIPConnection or WANPPPConnection), then look for the "Location:" header and copy the addressing and |
67c8f8a1 A |
972 | // URL info we need. |
973 | mDNSexport void LNT_ConfigureRouterInfo(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *data, mDNSu16 len) | |
974 | { | |
975 | char *ptr = (char *)data; | |
976 | char *end = (char *)data + len; | |
32bb7e43 A |
977 | char *stop = ptr; |
978 | ||
979 | if (!mDNSIPPortIsZero(m->UPnPRouterPort)) return; // already have the info we need | |
7f0064bd | 980 | |
67c8f8a1 A |
981 | // The formatting of the HTTP header is not always the same when it comes to the placement of |
982 | // the service and location strings, so we just look for each of them from the beginning for every response | |
983 | ||
984 | // figure out if this is a message from a service we care about | |
985 | while (ptr && ptr != end) | |
7f0064bd | 986 | { |
51e746cf | 987 | if (*ptr == 'W' && (strncasecmp(ptr, "WANIPConnection:1", 17) == 0)) break; |
67c8f8a1 | 988 | ptr++; |
7f0064bd | 989 | } |
51e746cf A |
990 | if (ptr == end) |
991 | { | |
992 | ptr = (char *)data; | |
993 | while (ptr && ptr != end) | |
994 | { | |
995 | if (*ptr == 'W' && (strncasecmp(ptr, "WANPPPConnection:1", 18) == 0)) break; | |
996 | ptr++; | |
997 | } | |
998 | } | |
67c8f8a1 | 999 | if (ptr == mDNSNULL || ptr == end) return; // not a message we care about |
7f0064bd | 1000 | |
67c8f8a1 A |
1001 | // find "Location:", starting from the beginning |
1002 | ptr = (char *)data; | |
1003 | while (ptr && ptr != end) | |
7f0064bd | 1004 | { |
1a175162 | 1005 | if ((*ptr & 0xDF) == 'L' && (strncasecmp(ptr, "Location:", 9) == 0)) break; // find the first 'L'; is this Location? if not, keep looking |
67c8f8a1 | 1006 | ptr++; |
7f0064bd | 1007 | } |
32bb7e43 A |
1008 | if (ptr == mDNSNULL || ptr == end) |
1009 | { | |
1010 | mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Location", ""); | |
1011 | return; // not a message we care about | |
1012 | } | |
1013 | ptr += 9; //Skip over 'Location:' | |
1014 | while (*ptr == ' ' && ptr < end) ptr++; // skip over spaces | |
1015 | if (ptr >= end) return; | |
67c8f8a1 | 1016 | |
32bb7e43 A |
1017 | // find the end of the line |
1018 | for (stop = ptr; stop != end; stop++) { if (*stop == '\r') { end = stop; break; } } | |
1019 | ||
1020 | // fill in default port | |
1021 | m->UPnPRouterPort = mDNSOpaque16fromIntVal(80); | |
1022 | ||
1023 | // free string pointers and set to NULL | |
1024 | if (m->UPnPRouterAddressString != mDNSNULL) | |
7f0064bd | 1025 | { |
32bb7e43 A |
1026 | mDNSPlatformMemFree(m->UPnPRouterAddressString); |
1027 | m->UPnPRouterAddressString = mDNSNULL; | |
1028 | } | |
1029 | if (m->UPnPRouterURL != mDNSNULL) | |
1030 | { | |
1031 | mDNSPlatformMemFree(m->UPnPRouterURL); | |
1032 | m->UPnPRouterURL = mDNSNULL; | |
1033 | } | |
67c8f8a1 | 1034 | |
32bb7e43 A |
1035 | // the Router URL should look something like "/dyndev/uuid:0013-108c-4b3f0000f3dc" |
1036 | if (ParseHttpUrl(ptr, end, &m->UPnPRouterAddressString, &m->UPnPRouterPort, &m->UPnPRouterURL) != mStatus_NoError) | |
1037 | { | |
1038 | mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Parse URL", ""); | |
1039 | return; | |
7f0064bd | 1040 | } |
7f0064bd | 1041 | |
32bb7e43 A |
1042 | m->UPnPInterfaceID = InterfaceID; |
1043 | ||
1044 | if (m->UPnPRouterAddressString == mDNSNULL) | |
7f0064bd | 1045 | { |
32bb7e43 A |
1046 | mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router address", ""); |
1047 | LogMsg("LNT_ConfigureRouterInfo: UPnPRouterAddressString is NULL"); | |
7f0064bd | 1048 | } |
32bb7e43 A |
1049 | else LogInfo("LNT_ConfigureRouterInfo: Router address string [%s]", m->UPnPRouterAddressString); |
1050 | ||
1051 | if (m->UPnPRouterURL == mDNSNULL) | |
1052 | { | |
1053 | mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router path", ""); | |
1054 | LogMsg("LNT_ConfigureRouterInfo: UPnPRouterURL is NULL"); | |
1055 | } | |
1056 | else LogInfo("LNT_ConfigureRouterInfo: Router URL [%s]", m->UPnPRouterURL); | |
1057 | ||
1058 | LogInfo("LNT_ConfigureRouterInfo: Router port %d", mDNSVal16(m->UPnPRouterPort)); | |
1059 | LogInfo("LNT_ConfigureRouterInfo: Router interface %d", m->UPnPInterfaceID); | |
7f0064bd | 1060 | |
030b743d | 1061 | // Don't need the SSDP socket anymore |
32bb7e43 | 1062 | if (m->SSDPSocket) { debugf("LNT_ConfigureRouterInfo destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } |
7f0064bd | 1063 | |
32bb7e43 | 1064 | mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "success", "success", ""); |
67c8f8a1 A |
1065 | // now send message to get the device description |
1066 | GetDeviceDescription(m, &m->tcpDeviceInfo); | |
7f0064bd A |
1067 | } |
1068 | ||
67c8f8a1 | 1069 | mDNSexport void LNT_SendDiscoveryMsg(mDNS *m) |
7f0064bd | 1070 | { |
51e746cf | 1071 | static const char msg[] = |
67c8f8a1 A |
1072 | "M-SEARCH * HTTP/1.1\r\n" |
1073 | "Host:239.255.255.250:1900\r\n" | |
51e746cf | 1074 | "ST:urn:schemas-upnp-org:service:WAN%sConnection:1\r\n" |
67c8f8a1 A |
1075 | "Man:\"ssdp:discover\"\r\n" |
1076 | "MX:3\r\n\r\n"; | |
9f29194f A |
1077 | static const mDNSAddr multicastDest = { mDNSAddrType_IPv4, { { { 239, 255, 255, 250 } } } }; |
1078 | ||
51e746cf A |
1079 | mDNSu8* buf = (mDNSu8*)&m->omsg; //m->omsg is 8952 bytes, which is plenty |
1080 | unsigned int bufLen; | |
32bb7e43 A |
1081 | |
1082 | if (!mDNSIPPortIsZero(m->UPnPRouterPort)) | |
1083 | { | |
1084 | if (m->SSDPSocket) { debugf("LNT_SendDiscoveryMsg destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } | |
1085 | if (mDNSIPPortIsZero(m->UPnPSOAPPort) && !m->tcpDeviceInfo.sock) GetDeviceDescription(m, &m->tcpDeviceInfo); | |
1086 | return; | |
1087 | } | |
51e746cf A |
1088 | |
1089 | // Always query for WANIPConnection in the first SSDP packet | |
1090 | if (m->retryIntervalGetAddr <= NATMAP_INIT_RETRY) m->SSDPWANPPPConnection = mDNSfalse; | |
1091 | ||
1092 | // Create message | |
1093 | bufLen = mDNS_snprintf((char*)buf, sizeof(m->omsg), msg, m->SSDPWANPPPConnection ? "PPP" : "IP"); | |
7f0064bd | 1094 | |
32bb7e43 | 1095 | debugf("LNT_SendDiscoveryMsg Router %.4a Current External Address %.4a", &m->Router.ip.v4, &m->ExternalAddress); |
7f0064bd | 1096 | |
32bb7e43 | 1097 | if (!mDNSIPv4AddressIsZero(m->Router.ip.v4)) |
030b743d | 1098 | { |
32bb7e43 | 1099 | if (!m->SSDPSocket) { m->SSDPSocket = mDNSPlatformUDPSocket(m, zeroIPPort); debugf("LNT_SendDiscoveryMsg created SSDPSocket %p", &m->SSDPSocket); } |
51e746cf A |
1100 | mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &m->Router, SSDPPort); |
1101 | mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &multicastDest, SSDPPort); | |
030b743d | 1102 | } |
9f29194f | 1103 | |
51e746cf | 1104 | m->SSDPWANPPPConnection = !m->SSDPWANPPPConnection; |
7f0064bd A |
1105 | } |
1106 | ||
32bb7e43 A |
1107 | mDNSexport void LNT_ClearState(mDNS *const m) |
1108 | { | |
1109 | if (m->tcpAddrInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpAddrInfo.sock); m->tcpAddrInfo.sock = mDNSNULL; } | |
1110 | if (m->tcpDeviceInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpDeviceInfo.sock); m->tcpDeviceInfo.sock = mDNSNULL; } | |
1111 | m->UPnPSOAPPort = m->UPnPRouterPort = zeroIPPort; // Reset UPnP ports | |
1112 | } | |
1113 | ||
67c8f8a1 | 1114 | #endif /* _LEGACY_NAT_TRAVERSAL_ */ |