]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/LegacyNATTraversal.c
mDNSResponder-1310.80.1.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / LegacyNATTraversal.c
1 /*
2 * Copyright (c) 2004-2019 Apple Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifdef _LEGACY_NAT_TRAVERSAL_
18
19 #include "stdlib.h" // For strtol()
20 #include "string.h" // For strlcpy(), For strncpy(), strncasecmp()
21 #include "assert.h" // For assert()
22
23 #if defined( WIN32 )
24 # include "CommonServices.h"
25 # include <winsock2.h>
26 # include <ws2tcpip.h>
27 # define strcasecmp _stricmp
28 # define strncasecmp _strnicmp
29
30 static int
31 inet_pton( int family, const char * addr, void * dst )
32 {
33 struct sockaddr_storage ss;
34 int sslen = sizeof( ss );
35
36 ZeroMemory( &ss, sizeof( ss ) );
37 ss.ss_family = (ADDRESS_FAMILY)family;
38
39 if ( WSAStringToAddressA( (LPSTR)addr, family, NULL, ( struct sockaddr* ) &ss, &sslen ) == 0 )
40 {
41 if ( family == AF_INET ) { memcpy( dst, &( ( struct sockaddr_in* ) &ss)->sin_addr, sizeof( IN_ADDR ) ); return 1; }
42 else if ( family == AF_INET6 ) { memcpy( dst, &( ( struct sockaddr_in6* ) &ss)->sin6_addr, sizeof( IN6_ADDR ) ); return 1; }
43 else return 0;
44 }
45 else return 0;
46 }
47 #else
48 # include <arpa/inet.h> // For inet_pton()
49 #endif
50
51 #include "mDNSEmbeddedAPI.h"
52 #include "uDNS.h" // For natTraversalHandleAddressReply() etc.
53
54 // used to format SOAP port mapping arguments
55 typedef struct Property_struct
56 {
57 char *name;
58 char *type;
59 char *value;
60 } Property;
61
62 // All of the text parsing in this file is intentionally transparent so that we know exactly
63 // what's being done to the text, with an eye towards preventing security problems.
64
65 // This is an evolving list of useful acronyms to know. Please add to it at will.
66 // ST Service Type
67 // NT Notification Type
68 // USN Unique Service Name
69 // UDN Unique Device Name
70 // UUID Universally Unique Identifier
71 // URN/urn Universal Resource Name
72
73 // Forward declaration because of circular reference:
74 // SendPortMapRequest -> SendSOAPMsgControlAction -> MakeTCPConnection -> tcpConnectionCallback -> handleLNTPortMappingResponse
75 // In the event of a port conflict, handleLNTPortMappingResponse then increments tcpInfo->retries and calls back to SendPortMapRequest to try again
76 mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n);
77
78 #define RequestedPortNum(n) (mDNSVal16(mDNSIPPortIsZero((n)->RequestedPort) ? (n)->IntPort : (n)->RequestedPort) + (mDNSu16)(n)->tcpInfo.retries)
79
80 // Note that this function assumes src is already NULL terminated
81 mDNSlocal void AllocAndCopy(char **const dst, const char *const src)
82 {
83 size_t srcLen;
84 if (src == mDNSNULL) return;
85 srcLen = strlen(src) + 1;
86 if ((srcLen > UINT32_MAX) || ((*dst = mDNSPlatformMemAllocate((mDNSu32)srcLen)) == mDNSNULL))
87 {
88 LogMsg("AllocAndCopy: can't allocate string");
89 return;
90 }
91 memcpy(*dst, src, srcLen);
92 }
93
94 // This function does a simple parse of an HTTP URL that may include a hostname, port, and path
95 // 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)
96 mDNSlocal mStatus ParseHttpUrl(const mDNSu8 *ptr, const mDNSu8 *const end, char **const addressAndPort, mDNSIPPort *const port, char **const path)
97 {
98 // if the data begins with "http://", we assume there is a hostname and possibly a port number
99 if (end - ptr >= 7 && strncasecmp((char*)ptr, "http://", 7) == 0)
100 {
101 int i;
102 const mDNSu8 *stop = end;
103 const mDNSu8 *addrPtr = mDNSNULL;
104
105 ptr += 7; //skip over "http://"
106 if (ptr >= end) { LogInfo("ParseHttpUrl: past end of buffer parsing host:port"); return mStatus_BadParamErr; }
107
108 // find the end of the host:port
109 addrPtr = ptr;
110 for (i = 0; addrPtr && addrPtr != end; i++, addrPtr++) if (*addrPtr == '/') break;
111
112 // allocate the buffer (len i+1 so we have space to terminate the string)
113 if ((*addressAndPort = mDNSPlatformMemAllocate(i+1)) == mDNSNULL)
114 { LogMsg("ParseHttpUrl: can't allocate address string"); return mStatus_NoMemoryErr; }
115 strncpy((char*)*addressAndPort, (char*)ptr, i);
116 (*addressAndPort)[i] = '\0';
117
118 // find the port number in the string, by looking backwards for the ':'
119 stop = ptr; // can't go back farther than the original start
120 ptr = addrPtr; // move ptr to the path part
121
122 for (addrPtr--; addrPtr>stop; addrPtr--)
123 {
124 if (*addrPtr == ':')
125 {
126 addrPtr++; // skip over ':'
127 *port = mDNSOpaque16fromIntVal((mDNSu16)strtol((char*)addrPtr, mDNSNULL, 10)); // store it properly converted
128 break;
129 }
130 }
131 }
132
133 // ptr should now point to the first character we haven't yet processed
134 // everything that remains is the path
135 if (path && ptr < end)
136 {
137 if ((*path = mDNSPlatformMemAllocate((mDNSu32)(end - ptr) + 1)) == mDNSNULL)
138 { LogMsg("ParseHttpUrl: can't mDNSPlatformMemAllocate path"); return mStatus_NoMemoryErr; }
139 strncpy((char*)*path, (char*)ptr, end - ptr);
140 (*path)[end - ptr] = '\0';
141 }
142
143 return mStatus_NoError;
144 }
145
146 enum
147 {
148 HTTPCode_NeedMoreData = -1, // No code found in stream
149 HTTPCode_Other = -2, // Valid code other than those below found in stream
150 HTTPCode_Bad = -3,
151 HTTPCode_200 = 200,
152 HTTPCode_404 = 404,
153 HTTPCode_500 = 500,
154 };
155
156 mDNSlocal mDNSs16 ParseHTTPResponseCode(const mDNSu8 **const data, const mDNSu8 *const end)
157 {
158 const mDNSu8 *ptr = *data;
159 const mDNSu8 *code;
160
161 if (end - ptr < 5) return HTTPCode_NeedMoreData;
162 if (strncasecmp((char*)ptr, "HTTP/", 5) != 0) return HTTPCode_Bad;
163 ptr += 5;
164 // should we care about the HTTP protocol version?
165
166 // look for first space, which must come before first LF
167 while (ptr && ptr != end)
168 {
169 if (*ptr == '\n') return HTTPCode_Bad;
170 if (*ptr == ' ') break;
171 ptr++;
172 }
173 if (ptr == end) return HTTPCode_NeedMoreData;
174 ptr++;
175
176 if (end - ptr < 3) return HTTPCode_NeedMoreData;
177
178 code = ptr;
179 ptr += 3;
180 while (ptr && ptr != end)
181 {
182 if (*ptr == '\n') break;
183 ptr++;
184 }
185 if (ptr == end) return HTTPCode_NeedMoreData;
186 *data = ++ptr;
187
188 if (memcmp((char*)code, "200", 3) == 0) return HTTPCode_200;
189 if (memcmp((char*)code, "404", 3) == 0) return HTTPCode_404;
190 if (memcmp((char*)code, "500", 3) == 0) return HTTPCode_500;
191
192 LogInfo("ParseHTTPResponseCode found unexpected result code: %c%c%c", code[0], code[1], code[2]);
193 return HTTPCode_Other;
194 }
195
196 // This function parses the xml body of the device description response from the router. Basically, we look to
197 // make sure this is a response referencing a service we care about (WANIPConnection or WANPPPConnection),
198 // look for the "controlURL" header immediately following, and copy the addressing and URL info we need
199 mDNSlocal void handleLNTDeviceDescriptionResponse(tcpLNTInfo *tcpInfo)
200 {
201 mDNS *m = tcpInfo->m;
202 const mDNSu8 *ptr = tcpInfo->Reply;
203 const mDNSu8 *end = tcpInfo->Reply + tcpInfo->nread;
204 const mDNSu8 *stop;
205 mDNSs16 http_result;
206
207 if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return; // already have the info we need
208
209 http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr
210 if (http_result == HTTPCode_404) LNT_ClearState(m);
211 if (http_result != HTTPCode_200)
212 {
213 LogInfo("handleLNTDeviceDescriptionResponse: HTTP Result code: %d", http_result);
214 return;
215 }
216
217 // Always reset our flag to use WANIPConnection. We'll use WANPPPConnection if we find it and don't find WANIPConnection.
218 m->UPnPWANPPPConnection = mDNSfalse;
219
220 // find either service we care about
221 while (ptr && ptr < end)
222 {
223 if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANIPConnection:1", 17) == 0)) break;
224 ptr++;
225 }
226 if (ptr == end)
227 {
228 ptr = tcpInfo->Reply;
229 while (ptr && ptr < end)
230 {
231 if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANPPPConnection:1", 18) == 0))
232 {
233 m->UPnPWANPPPConnection = mDNStrue;
234 break;
235 }
236 ptr++;
237 }
238 }
239 if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find WANIPConnection:1 or WANPPPConnection:1 string"); return; }
240
241 // find "controlURL", starting from where we left off
242 while (ptr && ptr < end)
243 {
244 if ((*ptr & 0xDF) == 'C' && (strncasecmp((char*)ptr, "controlURL", 10) == 0)) break; // find the first 'c'; is this controlURL? if not, keep looking
245 ptr++;
246 }
247 if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find controlURL string"); return; }
248 ptr += 11; // skip over "controlURL>"
249 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
250
251 // find the end of the controlURL element
252 for (stop = ptr; stop < end; stop++) { if (*stop == '<') { end = stop; break; } }
253
254 // fill in default port
255 m->UPnPSOAPPort = m->UPnPRouterPort;
256
257 // free string pointers and set to NULL
258 if (m->UPnPSOAPAddressString != mDNSNULL)
259 {
260 mDNSPlatformMemFree(m->UPnPSOAPAddressString);
261 m->UPnPSOAPAddressString = mDNSNULL;
262 }
263 if (m->UPnPSOAPURL != mDNSNULL)
264 {
265 mDNSPlatformMemFree(m->UPnPSOAPURL);
266 m->UPnPSOAPURL = mDNSNULL;
267 }
268
269 if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, &m->UPnPSOAPURL) != mStatus_NoError) return;
270 // the SOAPURL should look something like "/uuid:0013-108c-4b3f0000f3dc"
271
272 if (m->UPnPSOAPAddressString == mDNSNULL)
273 {
274 ptr = tcpInfo->Reply;
275 while (ptr && ptr < end)
276 {
277 if ((*ptr & 0xDF) == 'U' && (strncasecmp((char*)ptr, "URLBase", 7) == 0)) break;
278 ptr++;
279 }
280
281 if (ptr < end) // found URLBase
282 {
283 LogInfo("handleLNTDeviceDescriptionResponse: found URLBase");
284 ptr += 8; // skip over "URLBase>"
285 // find the end of the URLBase element
286 for (stop = ptr; stop < end; stop++) { if (stop && *stop == '<') { end = stop; break; } }
287 if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, mDNSNULL) != mStatus_NoError)
288 {
289 LogInfo("handleLNTDeviceDescriptionResponse: failed to parse URLBase");
290 }
291 }
292
293 // if all else fails, use the router address string
294 if (m->UPnPSOAPAddressString == mDNSNULL) AllocAndCopy(&m->UPnPSOAPAddressString, m->UPnPRouterAddressString);
295 }
296 if (m->UPnPSOAPAddressString == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPAddressString is NULL");
297 else LogInfo("handleLNTDeviceDescriptionResponse: SOAP address string [%s]", m->UPnPSOAPAddressString);
298
299 if (m->UPnPSOAPURL == mDNSNULL) AllocAndCopy(&m->UPnPSOAPURL, m->UPnPRouterURL);
300 if (m->UPnPSOAPURL == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPURL is NULL");
301 else LogInfo("handleLNTDeviceDescriptionResponse: SOAP URL [%s]", m->UPnPSOAPURL);
302 }
303
304 mDNSlocal void handleLNTGetExternalAddressResponse(tcpLNTInfo *tcpInfo)
305 {
306 mDNS *m = tcpInfo->m;
307 mDNSu16 err = NATErr_None;
308 mDNSv4Addr ExtAddr;
309 const mDNSu8 *ptr = tcpInfo->Reply;
310 const mDNSu8 *end = tcpInfo->Reply + tcpInfo->nread;
311 mDNSu8 *addrend;
312 static char tagname[20] = { 'N','e','w','E','x','t','e','r','n','a','l','I','P','A','d','d','r','e','s','s' };
313 // Array NOT including a terminating nul
314
315 // LogInfo("handleLNTGetExternalAddressResponse: %s", ptr);
316
317 mDNSs16 http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr
318 if (http_result == HTTPCode_404) LNT_ClearState(m);
319 if (http_result != HTTPCode_200)
320 {
321 LogInfo("handleLNTGetExternalAddressResponse: HTTP Result code: %d", http_result);
322 return;
323 }
324
325 while (ptr < end && strncasecmp((char*)ptr, tagname, sizeof(tagname))) ptr++;
326 ptr += sizeof(tagname); // Skip over "NewExternalIPAddress"
327 while (ptr < end && *ptr != '>') ptr++;
328 ptr += 1; // Skip over ">"
329
330 // Find the end of the address and terminate the string so inet_pton() can convert it
331 // (Might be better to copy this to a local string here -- this is overwriting tcpInfo->Reply in-place
332 addrend = (mDNSu8*)ptr;
333 while (addrend < end && (mDNSIsDigit(*addrend) || *addrend == '.')) addrend++;
334 if (addrend >= end) return;
335 *addrend = 0;
336
337 if (inet_pton(AF_INET, (char*)ptr, &ExtAddr) <= 0)
338 {
339 LogMsg("handleLNTGetExternalAddressResponse: Router returned bad address %s", ptr);
340 err = NATErr_NetFail;
341 ExtAddr = zerov4Addr;
342 }
343 if (!err) LogInfo("handleLNTGetExternalAddressResponse: External IP address is %.4a", &ExtAddr);
344
345 natTraversalHandleAddressReply(m, err, ExtAddr);
346 }
347
348 mDNSlocal void handleLNTPortMappingResponse(tcpLNTInfo *tcpInfo)
349 {
350 mDNS *m = tcpInfo->m;
351 mDNSIPPort extport = zeroIPPort;
352 const mDNSu8 *ptr = tcpInfo->Reply;
353 const mDNSu8 *const end = tcpInfo->Reply + tcpInfo->nread;
354 NATTraversalInfo *natInfo;
355 mDNSs16 http_result;
356
357 for (natInfo = m->NATTraversals; natInfo; natInfo=natInfo->next) { if (natInfo == tcpInfo->parentNATInfo) break;}
358
359 if (!natInfo) { LogInfo("handleLNTPortMappingResponse: can't find matching tcpInfo in NATTraversals!"); return; }
360
361 http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr
362 if (http_result == HTTPCode_200)
363 {
364 LogInfo("handleLNTPortMappingResponse: got a valid response, sending reply to natTraversalHandlePortMapReply(internal %d external %d retries %d)",
365 mDNSVal16(natInfo->IntPort), RequestedPortNum(natInfo), tcpInfo->retries);
366
367 // Make sure to compute extport *before* we zero tcpInfo->retries
368 extport = mDNSOpaque16fromIntVal(RequestedPortNum(natInfo));
369 tcpInfo->retries = 0;
370 natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, mStatus_NoError, extport, NATMAP_DEFAULT_LEASE, NATTProtocolUPNPIGD);
371 }
372 else if (http_result == HTTPCode_500)
373 {
374 while (ptr && ptr != end)
375 {
376 if (((*ptr & 0xDF) == 'C' && end - ptr >= 8 && strncasecmp((char*)ptr, "Conflict", 8) == 0) ||
377 (*ptr == '>' && end - ptr >= 15 && strncasecmp((char*)ptr, ">718</errorCode", 15) == 0))
378 {
379 if (tcpInfo->retries < 100)
380 {
381 tcpInfo->retries++; SendPortMapRequest(tcpInfo->m, natInfo);
382 LogInfo("handleLNTPortMappingResponse: Conflict retry %d", tcpInfo->retries);
383 }
384 else
385 {
386 LogMsg("handleLNTPortMappingResponse too many conflict retries %d %d", mDNSVal16(natInfo->IntPort), mDNSVal16(natInfo->RequestedPort));
387 natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0, NATTProtocolUPNPIGD);
388 }
389 return;
390 }
391 ptr++;
392 }
393 }
394 else if (http_result == HTTPCode_Bad) LogMsg("handleLNTPortMappingResponse got data that was not a valid HTTP response");
395 else if (http_result == HTTPCode_Other) LogMsg("handleLNTPortMappingResponse got unexpected response code");
396 else if (http_result == HTTPCode_404) LNT_ClearState(m);
397 if (http_result != HTTPCode_200 && http_result != HTTPCode_500)
398 LogInfo("handleLNTPortMappingResponse: HTTP Result code: %d", http_result);
399 }
400
401 mDNSlocal void DisposeInfoFromUnmapList(mDNS *m, tcpLNTInfo *tcpInfo)
402 {
403 tcpLNTInfo **ptr = &m->tcpInfoUnmapList;
404 while (*ptr && *ptr != tcpInfo) ptr = &(*ptr)->next;
405 if (*ptr) { *ptr = (*ptr)->next; mDNSPlatformMemFree(tcpInfo); } // If we found it, cut it from our list and free the memory
406 }
407
408 mDNSlocal void tcpConnectionCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEstablished, mStatus err)
409 {
410 mStatus status = mStatus_NoError;
411 tcpLNTInfo *tcpInfo = (tcpLNTInfo *)context;
412 mDNSBool closed = mDNSfalse;
413 long n = 0;
414 long nsent = 0;
415 static mDNSu32 LNTERRORcount = 0;
416
417 if (tcpInfo->sock != sock)
418 {
419 LogMsg("tcpConnectionCallback: WARNING- tcpInfo->sock(%p) != sock(%p) !!! Printing tcpInfo struct", tcpInfo->sock, sock);
420 LogMsg("tcpConnectionCallback: tcpInfo->Address:Port [%#a:%d] tcpInfo->op[%d] tcpInfo->retries[%d] tcpInfo->Request[%s] tcpInfo->Reply[%s]",
421 &tcpInfo->Address, mDNSVal16(tcpInfo->Port), tcpInfo->op, tcpInfo->retries, tcpInfo->Request, tcpInfo->Reply);
422 }
423
424 // The handlers below expect to be called with the lock held
425 mDNS_Lock(tcpInfo->m);
426
427 if (err) { LogInfo("tcpConnectionCallback: received error"); goto exit; }
428
429 if (ConnectionEstablished) // connection is established - send the message
430 {
431 LogInfo("tcpConnectionCallback: connection established, sending message");
432 nsent = mDNSPlatformWriteTCP(sock, (char*)tcpInfo->Request, tcpInfo->requestLen);
433 if (nsent != (long)tcpInfo->requestLen) { LogMsg("tcpConnectionCallback: error writing"); status = mStatus_UnknownErr; goto exit; }
434 }
435 else
436 {
437 n = mDNSPlatformReadTCP(sock, (char*)tcpInfo->Reply + tcpInfo->nread, tcpInfo->replyLen - tcpInfo->nread, &closed);
438 LogInfo("tcpConnectionCallback: mDNSPlatformReadTCP read %d bytes", n);
439
440 if (n < 0) { LogInfo("tcpConnectionCallback - read returned %d", n); status = mStatus_ConnFailed; goto exit; }
441 else if (closed) { LogInfo("tcpConnectionCallback: socket closed by remote end %d", tcpInfo->nread); status = mStatus_ConnFailed; goto exit; }
442
443 tcpInfo->nread += n;
444 LogInfo("tcpConnectionCallback tcpInfo->nread %d", tcpInfo->nread);
445 if (tcpInfo->nread > LNT_MAXBUFSIZE)
446 {
447 LogInfo("result truncated...");
448 tcpInfo->nread = LNT_MAXBUFSIZE;
449 }
450
451 switch (tcpInfo->op)
452 {
453 case LNTDiscoveryOp: handleLNTDeviceDescriptionResponse (tcpInfo); break;
454 case LNTExternalAddrOp: handleLNTGetExternalAddressResponse(tcpInfo); break;
455 case LNTPortMapOp: handleLNTPortMappingResponse (tcpInfo); break;
456 case LNTPortMapDeleteOp: status = mStatus_ConfigChanged; break;
457 default: LogMsg("tcpConnectionCallback: bad tcp operation! %d", tcpInfo->op); status = mStatus_Invalid; break;
458 }
459 }
460 exit:
461 if (err || status)
462 {
463 mDNS *const m = tcpInfo->m;
464 static mDNSs32 lastErrorTime = 0;
465
466 if ((LNTERRORcount > 0) && (((mDNSu32)(m->timenow - lastErrorTime)) >= ((mDNSu32)mDNSPlatformOneSecond)))
467 {
468 LNTERRORcount = 0;
469 }
470 lastErrorTime = m->timenow;
471 if ((++LNTERRORcount % 1000) == 0)
472 {
473 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR,
474 "ERROR: tcpconnectioncallback -> got error status %u times", LNTERRORcount);
475 assert(LNTERRORcount < 1000);
476 // Recovery Mechanism to bail mDNSResponder out of trouble: It has been seen that we can get into
477 // this loop: [tcpKQSocketCallback()--> doTcpSocketCallback()-->tcpconnectionCallback()-->mDNSASLLog()],
478 // if mDNSPlatformTCPCloseConnection() does not close the TCPSocket. Instead of calling mDNSASLLog()
479 // repeatedly and logging the same error msg causing 100% CPU usage, we
480 // crash mDNSResponder using assert() and restart fresh. See advantages below:
481 // 1.Better User Experience
482 // 2.CrashLogs frequency can be monitored
483 // 3.StackTrace can be used for more info
484 }
485
486 switch (tcpInfo->op)
487 {
488 case LNTDiscoveryOp:
489 LogInfo("tcpConnectionCallback: DeviceDescription SOAP address %s SOAP path %s",
490 m->UPnPSOAPAddressString ? m->UPnPSOAPAddressString : "NULL", m->UPnPSOAPURL ? m->UPnPSOAPURL : "NULL");
491 break;
492 case LNTExternalAddrOp:
493 LogInfo("tcpConnectionCallback: AddressRequest %s", mDNSIPv4AddressIsZero(m->ExtAddress) ? "failure" : "success");
494 break;
495 case LNTPortMapOp:
496 if (tcpInfo->parentNATInfo)
497 LogInfo("tcpConnectionCallback: PortMapRequest %s result %d",
498 (tcpInfo->parentNATInfo->Result) ? "failure" : "success", tcpInfo->parentNATInfo->Result);
499 break;
500 case LNTPortMapDeleteOp: break;
501 default: break;
502 }
503
504 mDNSPlatformTCPCloseConnection(sock);
505 tcpInfo->sock = mDNSNULL;
506 if (tcpInfo->Request) { mDNSPlatformMemFree(tcpInfo->Request); tcpInfo->Request = mDNSNULL; }
507 if (tcpInfo->Reply ) { mDNSPlatformMemFree(tcpInfo->Reply); tcpInfo->Reply = mDNSNULL; }
508 }
509 else
510 {
511 LNTERRORcount = 0; // clear LNTERRORcount
512 }
513
514 if (tcpInfo) mDNS_Unlock(tcpInfo->m);
515
516 if (status == mStatus_ConfigChanged) DisposeInfoFromUnmapList(tcpInfo->m, tcpInfo);
517 }
518
519 mDNSlocal mStatus MakeTCPConnection(mDNS *const m, tcpLNTInfo *info, const mDNSAddr *const Addr, const mDNSIPPort Port, LNTOp_t op)
520 {
521 mStatus err = mStatus_NoError;
522 mDNSIPPort srcport = zeroIPPort;
523
524 if (mDNSIPv4AddressIsZero(Addr->ip.v4) || mDNSIPPortIsZero(Port))
525 { LogMsg("LNT MakeTCPConnection: bad address/port %#a:%d", Addr, mDNSVal16(Port)); return(mStatus_Invalid); }
526 info->m = m;
527 info->Address = *Addr;
528 info->Port = Port;
529 info->op = op;
530 info->nread = 0;
531 info->replyLen = LNT_MAXBUFSIZE;
532 if (info->Reply != mDNSNULL) mDNSPlatformMemZero(info->Reply, LNT_MAXBUFSIZE); // reuse previously allocated buffer
533 else if ((info->Reply = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate reply buffer"); return (mStatus_NoMemoryErr); }
534
535 if (info->sock) { LogInfo("MakeTCPConnection: closing previous open connection"); mDNSPlatformTCPCloseConnection(info->sock); info->sock = mDNSNULL; }
536 info->sock = mDNSPlatformTCPSocket(kTCPSocketFlags_Zero, Addr->type, &srcport, mDNSNULL, mDNSfalse);
537 if (!info->sock) { LogMsg("LNT MakeTCPConnection: unable to create TCP socket"); mDNSPlatformMemFree(info->Reply); info->Reply = mDNSNULL; return(mStatus_NoMemoryErr); }
538 LogInfo("MakeTCPConnection: connecting to %#a:%d", &info->Address, mDNSVal16(info->Port));
539 err = mDNSPlatformTCPConnect(info->sock, Addr, Port, 0, tcpConnectionCallback, info);
540
541 if (err == mStatus_ConnPending) err = mStatus_NoError;
542 else if (err == mStatus_ConnEstablished)
543 {
544 mDNS_DropLockBeforeCallback();
545 tcpConnectionCallback(info->sock, info, mDNStrue, mStatus_NoError);
546 mDNS_ReclaimLockAfterCallback();
547 err = mStatus_NoError;
548 }
549 else
550 {
551 // Don't need to log this in customer builds -- it happens quite often during sleep, wake, configuration changes, etc.
552 LogInfo("LNT MakeTCPConnection: connection failed");
553 mDNSPlatformTCPCloseConnection(info->sock); // Dispose the socket we created with mDNSPlatformTCPSocket() above
554 info->sock = mDNSNULL;
555 mDNSPlatformMemFree(info->Reply);
556 info->Reply = mDNSNULL;
557 }
558 return(err);
559 }
560
561 mDNSlocal unsigned int AddSOAPArguments(char *const buf, const unsigned int maxlen, const int numArgs, const Property *const a)
562 {
563 static const char f1[] = "<%s>%s</%s>";
564 static const char f2[] = "<%s xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"%s\">%s</%s>";
565 int i, len = 0;
566 *buf = 0;
567 for (i = 0; i < numArgs; i++)
568 {
569 if (a[i].type) len += mDNS_snprintf(buf + len, maxlen - len, f2, a[i].name, a[i].type, a[i].value, a[i].name);
570 else len += mDNS_snprintf(buf + len, maxlen - len, f1, a[i].name, a[i].value, a[i].name);
571 }
572 return(len);
573 }
574
575 mDNSlocal mStatus SendSOAPMsgControlAction(mDNS *m, tcpLNTInfo *info, const char *const Action, const int numArgs, const Property *const Arguments, const LNTOp_t op)
576 {
577 // SOAP message header format -
578 // - control URL
579 // - action (string)
580 // - router's host/port ("host:port")
581 // - content-length
582 static const char header[] =
583 "POST %s HTTP/1.1\r\n"
584 "Content-Type: text/xml; charset=\"utf-8\"\r\n"
585 "SOAPAction: \"urn:schemas-upnp-org:service:WAN%sConnection:1#%s\"\r\n"
586 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n"
587 "Host: %s\r\n"
588 "Content-Length: %d\r\n"
589 "Connection: close\r\n"
590 "Pragma: no-cache\r\n"
591 "\r\n"
592 "%s\r\n";
593
594 static const char body1[] =
595 "<?xml version=\"1.0\"?>\r\n"
596 "<SOAP-ENV:Envelope"
597 " xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\""
598 " SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
599 "<SOAP-ENV:Body>"
600 "<m:%s xmlns:m=\"urn:schemas-upnp-org:service:WAN%sConnection:1\">";
601
602 static const char body2[] =
603 "</m:%s>"
604 "</SOAP-ENV:Body>"
605 "</SOAP-ENV:Envelope>\r\n";
606
607 mStatus err;
608 char *body = (char*)&m->omsg; // Typically requires 1110-1122 bytes; m->omsg is 8952 bytes, which is plenty
609 int bodyLen;
610
611 if (mDNSIPPortIsZero(m->UPnPSOAPPort) || m->UPnPSOAPURL == mDNSNULL || m->UPnPSOAPAddressString == mDNSNULL) // if no SOAP URL or address exists get out here
612 { LogInfo("SendSOAPMsgControlAction: no SOAP port, URL or address string"); return mStatus_Invalid; }
613
614 // Create body
615 bodyLen = mDNS_snprintf (body, sizeof(m->omsg), body1, Action, m->UPnPWANPPPConnection ? "PPP" : "IP");
616 bodyLen += AddSOAPArguments(body + bodyLen, sizeof(m->omsg) - bodyLen, numArgs, Arguments);
617 bodyLen += mDNS_snprintf (body + bodyLen, sizeof(m->omsg) - bodyLen, body2, Action);
618
619 // Create info->Request; the header needs to contain the bodyLen in the "Content-Length" field
620 if (!info->Request) info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE);
621 if (!info->Request) { LogMsg("SendSOAPMsgControlAction: Can't allocate info->Request"); return mStatus_NoMemoryErr; }
622 info->requestLen = mDNS_snprintf((char*)info->Request, LNT_MAXBUFSIZE, header, m->UPnPSOAPURL, m->UPnPWANPPPConnection ? "PPP" : "IP", Action, m->UPnPSOAPAddressString, bodyLen, body);
623
624 err = MakeTCPConnection(m, info, &m->Router, m->UPnPSOAPPort, op);
625 if (err) { mDNSPlatformMemFree(info->Request); info->Request = mDNSNULL; }
626 return err;
627 }
628
629 // Build port mapping request with new port (up to max) and send it
630 mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n)
631 {
632 char externalPort[6];
633 char internalPort[6];
634 char localIPAddrString[30];
635 char publicPortString[40];
636 Property propArgs[8];
637 mDNSu16 ReqPortNum = RequestedPortNum(n);
638 NATTraversalInfo *n2 = m->NATTraversals;
639
640 // Scan our m->NATTraversals list to make sure the external port we're requesting is locally unique.
641 // UPnP gateways will report conflicts if different devices request the same external port, but if two
642 // clients on the same device request the same external port the second one just stomps over the first.
643 // One way this can happen is like this:
644 // 1. Client A binds local port 80
645 // 2. Client A requests external port 80 -> internal port 80
646 // 3. UPnP NAT gateway refuses external port 80 (some other client already has it)
647 // 4. Client A tries again, and successfully gets external port 80 -> internal port 81
648 // 5. Client B on same machine tries to bind local port 80, and fails
649 // 6. Client B tries again, and successfully binds local port 81
650 // 7. Client B now requests external port 81 -> internal port 81
651 // 8. UPnP NAT gateway allows this, stomping over Client A's existing mapping
652
653 while (n2)
654 {
655 if (n2 == n || RequestedPortNum(n2) != ReqPortNum) n2=n2->next;
656 else
657 {
658 if (n->tcpInfo.retries < 100)
659 {
660 n->tcpInfo.retries++;
661 ReqPortNum = RequestedPortNum(n); // Pick a new port number
662 n2 = m->NATTraversals; // And re-scan the list looking for conflicts
663 }
664 else
665 {
666 natTraversalHandlePortMapReply(m, n, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0, NATTProtocolUPNPIGD);
667 return mStatus_NoError;
668 }
669 }
670 }
671
672 // create strings to use in the message
673 mDNS_snprintf(externalPort, sizeof(externalPort), "%u", ReqPortNum);
674 mDNS_snprintf(internalPort, sizeof(internalPort), "%u", mDNSVal16(n->IntPort));
675 mDNS_snprintf(publicPortString, sizeof(publicPortString), "iC%u", ReqPortNum);
676 mDNS_snprintf(localIPAddrString, sizeof(localIPAddrString), "%u.%u.%u.%u",
677 m->AdvertisedV4.ip.v4.b[0], m->AdvertisedV4.ip.v4.b[1], m->AdvertisedV4.ip.v4.b[2], m->AdvertisedV4.ip.v4.b[3]);
678
679 // build the message
680 mDNSPlatformMemZero(propArgs, sizeof(propArgs));
681 propArgs[0].name = "NewRemoteHost";
682 propArgs[0].type = "string";
683 propArgs[0].value = "";
684 propArgs[1].name = "NewExternalPort";
685 propArgs[1].type = "ui2";
686 propArgs[1].value = externalPort;
687 propArgs[2].name = "NewProtocol";
688 propArgs[2].type = "string";
689 propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP";
690 propArgs[3].name = "NewInternalPort";
691 propArgs[3].type = "ui2";
692 propArgs[3].value = internalPort;
693 propArgs[4].name = "NewInternalClient";
694 propArgs[4].type = "string";
695 propArgs[4].value = localIPAddrString;
696 propArgs[5].name = "NewEnabled";
697 propArgs[5].type = "boolean";
698 propArgs[5].value = "1";
699 propArgs[6].name = "NewPortMappingDescription";
700 propArgs[6].type = "string";
701 propArgs[6].value = publicPortString;
702 propArgs[7].name = "NewLeaseDuration";
703 propArgs[7].type = "ui4";
704 propArgs[7].value = "0";
705
706 LogInfo("SendPortMapRequest: internal %u external %u", mDNSVal16(n->IntPort), ReqPortNum);
707 return SendSOAPMsgControlAction(m, &n->tcpInfo, "AddPortMapping", 8, propArgs, LNTPortMapOp);
708 }
709
710 mDNSexport mStatus LNT_MapPort(mDNS *m, NATTraversalInfo *const n)
711 {
712 LogInfo("LNT_MapPort");
713 if (n->tcpInfo.sock) return(mStatus_NoError); // If we already have a connection up don't make another request for the same thing
714 n->tcpInfo.parentNATInfo = n;
715 n->tcpInfo.retries = 0;
716 return SendPortMapRequest(m, n);
717 }
718
719 mDNSexport mStatus LNT_UnmapPort(mDNS *m, NATTraversalInfo *const n)
720 {
721 char externalPort[10];
722 Property propArgs[3];
723 tcpLNTInfo *info;
724 tcpLNTInfo **infoPtr = &m->tcpInfoUnmapList;
725 mStatus err;
726
727 // If no NAT gateway to talk to, no need to do all this work for nothing
728 if (mDNSIPPortIsZero(m->UPnPSOAPPort) || !m->UPnPSOAPURL || !m->UPnPSOAPAddressString) return mStatus_NoError;
729
730 mDNS_snprintf(externalPort, sizeof(externalPort), "%u", mDNSVal16(mDNSIPPortIsZero(n->RequestedPort) ? n->IntPort : n->RequestedPort));
731
732 mDNSPlatformMemZero(propArgs, sizeof(propArgs));
733 propArgs[0].name = "NewRemoteHost";
734 propArgs[0].type = "string";
735 propArgs[0].value = "";
736 propArgs[1].name = "NewExternalPort";
737 propArgs[1].type = "ui2";
738 propArgs[1].value = externalPort;
739 propArgs[2].name = "NewProtocol";
740 propArgs[2].type = "string";
741 propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP";
742
743 n->tcpInfo.parentNATInfo = n;
744
745 // clean up previous port mapping requests and allocations
746 if (n->tcpInfo.sock) LogInfo("LNT_UnmapPort: closing previous open connection");
747 if (n->tcpInfo.sock ) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; }
748 if (n->tcpInfo.Request) { mDNSPlatformMemFree(n->tcpInfo.Request); n->tcpInfo.Request = mDNSNULL; }
749 if (n->tcpInfo.Reply ) { mDNSPlatformMemFree(n->tcpInfo.Reply); n->tcpInfo.Reply = mDNSNULL; }
750
751 // 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)
752 if ((info = (tcpLNTInfo *) mDNSPlatformMemAllocate(sizeof(*info))) == mDNSNULL)
753 { LogInfo("LNT_UnmapPort: can't allocate tcpInfo"); return(mStatus_NoMemoryErr); }
754 *info = n->tcpInfo;
755
756 while (*infoPtr) infoPtr = &(*infoPtr)->next; // find the end of the list
757 *infoPtr = info; // append
758
759 err = SendSOAPMsgControlAction(m, info, "DeletePortMapping", 3, propArgs, LNTPortMapDeleteOp);
760 if (err) DisposeInfoFromUnmapList(m, info);
761 return err;
762 }
763
764 mDNSexport mStatus LNT_GetExternalAddress(mDNS *m)
765 {
766 return SendSOAPMsgControlAction(m, &m->tcpAddrInfo, "GetExternalIPAddress", 0, mDNSNULL, LNTExternalAddrOp);
767 }
768
769 mDNSlocal mStatus GetDeviceDescription(mDNS *m, tcpLNTInfo *info)
770 {
771 // Device description format -
772 // - device description URL
773 // - host/port
774 static const char szSSDPMsgDescribeDeviceFMT[] =
775 "GET %s HTTP/1.1\r\n"
776 "Accept: text/xml, application/xml\r\n"
777 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
778 "Host: %s\r\n"
779 "Connection: close\r\n"
780 "\r\n";
781
782 if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return mStatus_NoError; // already have the info we need
783
784 if (m->UPnPRouterURL == mDNSNULL || m->UPnPRouterAddressString == mDNSNULL) { LogInfo("GetDeviceDescription: no router URL or address string!"); return (mStatus_Invalid); }
785
786 // build message
787 if (info->Request != mDNSNULL) mDNSPlatformMemZero(info->Request, LNT_MAXBUFSIZE); // reuse previously allocated buffer
788 else if ((info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate send buffer for discovery"); return (mStatus_NoMemoryErr); }
789 info->requestLen = mDNS_snprintf((char*)info->Request, LNT_MAXBUFSIZE, szSSDPMsgDescribeDeviceFMT, m->UPnPRouterURL, m->UPnPRouterAddressString);
790 LogInfo("Describe Device: [%s]", info->Request);
791 return MakeTCPConnection(m, info, &m->Router, m->UPnPRouterPort, LNTDiscoveryOp);
792 }
793
794 // This function parses the response to our SSDP discovery message. Basically, we look to make sure this is a response
795 // referencing a service we care about (WANIPConnection or WANPPPConnection), then look for the "Location:" header and copy the addressing and
796 // URL info we need.
797 mDNSexport void LNT_ConfigureRouterInfo(mDNS *m, const mDNSInterfaceID InterfaceID, const mDNSu8 *const data, const mDNSu16 len)
798 {
799 const mDNSu8 *ptr = data;
800 const mDNSu8 *end = data + len;
801 const mDNSu8 *stop;
802
803 if (!mDNSIPPortIsZero(m->UPnPRouterPort)) return; // already have the info we need
804
805 // The formatting of the HTTP header is not always the same when it comes to the placement of
806 // the service and location strings, so we just look for each of them from the beginning for every response
807
808 // figure out if this is a message from a service we care about
809 while (ptr && ptr != end)
810 {
811 if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANIPConnection:1", 17) == 0)) break;
812 ptr++;
813 }
814 if (ptr == end)
815 {
816 ptr = data;
817 while (ptr && ptr != end)
818 {
819 if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANPPPConnection:1", 18) == 0)) break;
820 ptr++;
821 }
822 }
823 if (ptr == mDNSNULL || ptr == end) return; // not a message we care about
824
825 // find "Location:", starting from the beginning
826 ptr = data;
827 while (ptr && ptr != end)
828 {
829 if ((*ptr & 0xDF) == 'L' && (strncasecmp((char*)ptr, "Location:", 9) == 0)) break; // find the first 'L'; is this Location? if not, keep looking
830 ptr++;
831 }
832 if (ptr == mDNSNULL || ptr == end)
833 {
834 LogInfo("LNT_ConfigureRouterInfo: Location field not found");
835 return; // not a message we care about
836 }
837 ptr += 9; //Skip over 'Location:'
838 while (*ptr == ' ' && ptr < end) ptr++; // skip over spaces
839 if (ptr >= end) return;
840
841 // find the end of the line
842 for (stop = ptr; stop != end; stop++) { if (*stop == '\r') { end = stop; break; } }
843
844 // fill in default port
845 m->UPnPRouterPort = mDNSOpaque16fromIntVal(80);
846
847 // free string pointers and set to NULL
848 if (m->UPnPRouterAddressString != mDNSNULL)
849 {
850 mDNSPlatformMemFree(m->UPnPRouterAddressString);
851 m->UPnPRouterAddressString = mDNSNULL;
852 }
853 if (m->UPnPRouterURL != mDNSNULL)
854 {
855 mDNSPlatformMemFree(m->UPnPRouterURL);
856 m->UPnPRouterURL = mDNSNULL;
857 }
858
859 // the Router URL should look something like "/dyndev/uuid:0013-108c-4b3f0000f3dc"
860 if (ParseHttpUrl(ptr, end, &m->UPnPRouterAddressString, &m->UPnPRouterPort, &m->UPnPRouterURL) != mStatus_NoError)
861 {
862 LogInfo("LNT_ConfigureRouterInfo: Failed to parse URL");
863 return;
864 }
865
866 m->UPnPInterfaceID = InterfaceID;
867
868 if (m->UPnPRouterAddressString == mDNSNULL)
869 {
870 LogMsg("LNT_ConfigureRouterInfo: UPnPRouterAddressString is NULL");
871 }
872 else LogInfo("LNT_ConfigureRouterInfo: Router address string [%s]", m->UPnPRouterAddressString);
873
874 if (m->UPnPRouterURL == mDNSNULL)
875 {
876 LogMsg("LNT_ConfigureRouterInfo: UPnPRouterURL is NULL");
877 }
878 else LogInfo("LNT_ConfigureRouterInfo: Router URL [%s]", m->UPnPRouterURL);
879
880 LogInfo("LNT_ConfigureRouterInfo: Router port %d", mDNSVal16(m->UPnPRouterPort));
881 LogInfo("LNT_ConfigureRouterInfo: Router interface %d", m->UPnPInterfaceID);
882
883 // Don't need the SSDP socket anymore
884 if (m->SSDPSocket) { debugf("LNT_ConfigureRouterInfo destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
885
886 // now send message to get the device description
887 GetDeviceDescription(m, &m->tcpDeviceInfo);
888 }
889
890 mDNSexport void LNT_SendDiscoveryMsg(mDNS *m)
891 {
892 static const char msg[] =
893 "M-SEARCH * HTTP/1.1\r\n"
894 "Host:239.255.255.250:1900\r\n"
895 "ST:urn:schemas-upnp-org:service:WAN%sConnection:1\r\n"
896 "Man:\"ssdp:discover\"\r\n"
897 "MX:3\r\n\r\n";
898 static const mDNSAddr multicastDest = { mDNSAddrType_IPv4, { { { 239, 255, 255, 250 } } } };
899
900 mDNSu8 *const buf = (mDNSu8*)&m->omsg; //m->omsg is 8952 bytes, which is plenty
901 unsigned int bufLen;
902
903 if (m->SleepState != SleepState_Awake) return;
904 if (!mDNSIPPortIsZero(m->UPnPRouterPort))
905 {
906 if (m->SSDPSocket) { debugf("LNT_SendDiscoveryMsg destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
907 if (mDNSIPPortIsZero(m->UPnPSOAPPort) && !m->tcpDeviceInfo.sock) GetDeviceDescription(m, &m->tcpDeviceInfo);
908 return;
909 }
910
911 // Always query for WANIPConnection in the first SSDP packet
912 if (m->retryIntervalGetAddr <= NATMAP_INIT_RETRY) m->SSDPWANPPPConnection = mDNSfalse;
913
914 // Create message
915 bufLen = mDNS_snprintf((char*)buf, sizeof(m->omsg), msg, m->SSDPWANPPPConnection ? "PPP" : "IP");
916
917 debugf("LNT_SendDiscoveryMsg Router %.4a Current External Address %.4a", &m->Router.ip.v4, &m->ExtAddress);
918
919 if (!mDNSIPv4AddressIsZero(m->Router.ip.v4))
920 {
921 if (!m->SSDPSocket) { m->SSDPSocket = mDNSPlatformUDPSocket(zeroIPPort); debugf("LNT_SendDiscoveryMsg created SSDPSocket %p", &m->SSDPSocket); }
922 mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &m->Router, SSDPPort, mDNSfalse);
923 mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &multicastDest, SSDPPort, mDNSfalse);
924 }
925
926 m->SSDPWANPPPConnection = !m->SSDPWANPPPConnection;
927 }
928
929 mDNSexport void LNT_ClearState(mDNS *const m)
930 {
931 if (m->tcpAddrInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpAddrInfo.sock); m->tcpAddrInfo.sock = mDNSNULL; }
932 if (m->tcpDeviceInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpDeviceInfo.sock); m->tcpDeviceInfo.sock = mDNSNULL; }
933 m->UPnPSOAPPort = m->UPnPRouterPort = zeroIPPort; // Reset UPnP ports
934 }
935
936 #endif /* _LEGACY_NAT_TRAVERSAL_ */