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