1da4059e080bad4f3b47af9811989d3611e2e2e2
[platform/upstream/mdnsresponder.git] / mDNSResponder-1096.40.7 / 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_ */