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