acfb3dcae2b03e1f479132dc5c2132266b13cb3f
[platform/upstream/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 #   define mDNSASLLog( UUID, SUBDOMAIN, RESULT, SIGNATURE, FORMAT, ... ) ;
31
32 static int
33 inet_pton( int family, const char * addr, void * dst )
34 {
35     struct sockaddr_storage ss;
36     int sslen = sizeof( ss );
37
38     ZeroMemory( &ss, sizeof( ss ) );
39     ss.ss_family = (ADDRESS_FAMILY)family;
40
41     if ( WSAStringToAddressA( (LPSTR)addr, family, NULL, ( struct sockaddr* ) &ss, &sslen ) == 0 )
42     {
43         if ( family == AF_INET ) { memcpy( dst, &( ( struct sockaddr_in* ) &ss)->sin_addr, sizeof( IN_ADDR ) ); return 1; }
44         else if ( family == AF_INET6 ) { memcpy( dst, &( ( struct sockaddr_in6* ) &ss)->sin6_addr, sizeof( IN6_ADDR ) ); return 1; }
45         else return 0;
46     }
47     else return 0;
48 }
49 #else
50 #   include <arpa/inet.h>       // For inet_pton()
51 #endif
52
53 #include "mDNSEmbeddedAPI.h"
54 #include "uDNS.h"           // For natTraversalHandleAddressReply() etc.
55
56 // used to format SOAP port mapping arguments
57 typedef struct Property_struct
58 {
59     char *name;
60     char *type;
61     char *value;
62 } Property;
63
64 // All of the text parsing in this file is intentionally transparent so that we know exactly
65 // what's being done to the text, with an eye towards preventing security problems.
66
67 // This is an evolving list of useful acronyms to know. Please add to it at will.
68 // ST      Service Type
69 // NT      Notification Type
70 // USN     Unique Service Name
71 // UDN     Unique Device Name
72 // UUID    Universally Unique Identifier
73 // URN/urn Universal Resource Name
74
75 // Forward declaration because of circular reference:
76 // SendPortMapRequest -> SendSOAPMsgControlAction -> MakeTCPConnection -> tcpConnectionCallback -> handleLNTPortMappingResponse
77 // In the event of a port conflict, handleLNTPortMappingResponse then increments tcpInfo->retries and calls back to SendPortMapRequest to try again
78 mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n);
79
80 #define RequestedPortNum(n) (mDNSVal16(mDNSIPPortIsZero((n)->RequestedPort) ? (n)->IntPort : (n)->RequestedPort) + (mDNSu16)(n)->tcpInfo.retries)
81
82 // Note that this function assumes src is already NULL terminated
83 mDNSlocal void AllocAndCopy(mDNSu8 **const dst, const mDNSu8 *const src)
84 {
85     if (src == mDNSNULL) return;
86     if ((strlen((char*)src)) >= UINT32_MAX || (*dst = mDNSPlatformMemAllocate((mDNSu32)strlen((char*)src) + 1)) == mDNSNULL)
87     {
88         LogMsg("AllocAndCopy: can't allocate string");
89         return;
90     }
91     strcpy((char*)*dst, (char*)src);
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, mDNSu8 **const addressAndPort, mDNSIPPort *const port, mDNSu8 **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         mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "noop", "HTTP Result", "HTTP 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         mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "HTTP Result", "HTTP 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         mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "inet_pton", "");
340         LogMsg("handleLNTGetExternalAddressResponse: Router returned bad address %s", ptr);
341         err = NATErr_NetFail;
342         ExtAddr = zerov4Addr;
343     }
344     if (!err) LogInfo("handleLNTGetExternalAddressResponse: External IP address is %.4a", &ExtAddr);
345
346     natTraversalHandleAddressReply(m, err, ExtAddr);
347 }
348
349 mDNSlocal void handleLNTPortMappingResponse(tcpLNTInfo *tcpInfo)
350 {
351     mDNS             *m         = tcpInfo->m;
352     mDNSIPPort extport   = zeroIPPort;
353     const mDNSu8     *ptr       = tcpInfo->Reply;
354     const mDNSu8     *const end = tcpInfo->Reply + tcpInfo->nread;
355     NATTraversalInfo *natInfo;
356     mDNSs16 http_result;
357
358     for (natInfo = m->NATTraversals; natInfo; natInfo=natInfo->next) { if (natInfo == tcpInfo->parentNATInfo) break;}
359
360     if (!natInfo) { LogInfo("handleLNTPortMappingResponse: can't find matching tcpInfo in NATTraversals!"); return; }
361
362     http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr
363     if (http_result == HTTPCode_200)
364     {
365         LogInfo("handleLNTPortMappingResponse: got a valid response, sending reply to natTraversalHandlePortMapReply(internal %d external %d retries %d)",
366                 mDNSVal16(natInfo->IntPort), RequestedPortNum(natInfo), tcpInfo->retries);
367
368         // Make sure to compute extport *before* we zero tcpInfo->retries
369         extport = mDNSOpaque16fromIntVal(RequestedPortNum(natInfo));
370         tcpInfo->retries = 0;
371         natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, mStatus_NoError, extport, NATMAP_DEFAULT_LEASE, NATTProtocolUPNPIGD);
372     }
373     else if (http_result == HTTPCode_500)
374     {
375         while (ptr && ptr != end)
376         {
377             if (((*ptr & 0xDF) == 'C' && end - ptr >= 8 && strncasecmp((char*)ptr, "Conflict", 8) == 0) ||
378                 (*ptr == '>' && end - ptr >= 15 && strncasecmp((char*)ptr, ">718</errorCode", 15) == 0))
379             {
380                 if (tcpInfo->retries < 100)
381                 {
382                     tcpInfo->retries++; SendPortMapRequest(tcpInfo->m, natInfo);
383                     mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict", "Retry %d", tcpInfo->retries);
384                 }
385                 else
386                 {
387                     LogMsg("handleLNTPortMappingResponse too many conflict retries %d %d", mDNSVal16(natInfo->IntPort), mDNSVal16(natInfo->RequestedPort));
388                     mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict - too many retries", "Retries: %d", tcpInfo->retries);
389                     natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0, NATTProtocolUPNPIGD);
390                 }
391                 return;
392             }
393             ptr++;
394         }
395     }
396     else if (http_result == HTTPCode_Bad) LogMsg("handleLNTPortMappingResponse got data that was not a valid HTTP response");
397     else if (http_result == HTTPCode_Other) LogMsg("handleLNTPortMappingResponse got unexpected response code");
398     else if (http_result == HTTPCode_404) LNT_ClearState(m);
399     if (http_result != HTTPCode_200 && http_result != HTTPCode_500)
400         mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "HTTP Result", "HTTP code: %d", http_result);
401 }
402
403 mDNSlocal void DisposeInfoFromUnmapList(mDNS *m, tcpLNTInfo *tcpInfo)
404 {
405     tcpLNTInfo **ptr = &m->tcpInfoUnmapList;
406     while (*ptr && *ptr != tcpInfo) ptr = &(*ptr)->next;
407     if (*ptr) { *ptr = (*ptr)->next; mDNSPlatformMemFree(tcpInfo); }    // If we found it, cut it from our list and free the memory
408 }
409
410 mDNSlocal void tcpConnectionCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEstablished, mStatus err)
411 {
412     mStatus status  = mStatus_NoError;
413     tcpLNTInfo *tcpInfo = (tcpLNTInfo *)context;
414     mDNSBool closed  = mDNSfalse;
415     long n       = 0;
416     long nsent   = 0;
417     static int LNTERRORcount = 0;
418
419     if (tcpInfo->sock != sock)
420     {
421         LogMsg("tcpConnectionCallback: WARNING- tcpInfo->sock(%p) != sock(%p) !!! Printing tcpInfo struct", tcpInfo->sock, sock);
422         LogMsg("tcpConnectionCallback: tcpInfo->Address:Port [%#a:%d] tcpInfo->op[%d] tcpInfo->retries[%d] tcpInfo->Request[%s] tcpInfo->Reply[%s]", 
423                 &tcpInfo->Address, mDNSVal16(tcpInfo->Port), tcpInfo->op, tcpInfo->retries, tcpInfo->Request, tcpInfo->Reply);  
424     }
425         
426     // The handlers below expect to be called with the lock held
427     mDNS_Lock(tcpInfo->m);
428
429     if (err) { LogInfo("tcpConnectionCallback: received error"); goto exit; }
430
431     if (ConnectionEstablished)      // connection is established - send the message
432     {
433         LogInfo("tcpConnectionCallback: connection established, sending message");
434         nsent = mDNSPlatformWriteTCP(sock, (char*)tcpInfo->Request, tcpInfo->requestLen);
435         if (nsent != (long)tcpInfo->requestLen) { LogMsg("tcpConnectionCallback: error writing"); status = mStatus_UnknownErr; goto exit; }
436     }
437     else
438     {
439         n = mDNSPlatformReadTCP(sock, (char*)tcpInfo->Reply + tcpInfo->nread, tcpInfo->replyLen - tcpInfo->nread, &closed);
440         LogInfo("tcpConnectionCallback: mDNSPlatformReadTCP read %d bytes", n);
441
442         if      (n < 0)  { LogInfo("tcpConnectionCallback - read returned %d", n);                           status = mStatus_ConnFailed; goto exit; }
443         else if (closed) { LogInfo("tcpConnectionCallback: socket closed by remote end %d", tcpInfo->nread); status = mStatus_ConnFailed; goto exit; }
444
445         tcpInfo->nread += n;
446         LogInfo("tcpConnectionCallback tcpInfo->nread %d", tcpInfo->nread);
447         if (tcpInfo->nread > LNT_MAXBUFSIZE)
448         {
449             LogInfo("result truncated...");
450             tcpInfo->nread = LNT_MAXBUFSIZE;
451         }
452
453         switch (tcpInfo->op)
454         {
455         case LNTDiscoveryOp:     handleLNTDeviceDescriptionResponse (tcpInfo); break;
456         case LNTExternalAddrOp:  handleLNTGetExternalAddressResponse(tcpInfo); break;
457         case LNTPortMapOp:       handleLNTPortMappingResponse       (tcpInfo); break;
458         case LNTPortMapDeleteOp: status = mStatus_ConfigChanged;               break;
459         default: LogMsg("tcpConnectionCallback: bad tcp operation! %d", tcpInfo->op); status = mStatus_Invalid; break;
460         }
461     }
462 exit:
463     if (err || status)
464     {
465         mDNS   *m = tcpInfo->m;
466         if ((++LNTERRORcount % 1000) == 0)
467         {   
468             LogMsg("ERROR: tcpconnectioncallback -> got error status %d times", LNTERRORcount);
469             assert(LNTERRORcount < 1000);
470             // Recovery Mechanism to bail mDNSResponder out of trouble: It has been seen that we can get into 
471             // this loop: [tcpKQSocketCallback()--> doTcpSocketCallback()-->tcpconnectionCallback()-->mDNSASLLog()],
472             // if mDNSPlatformTCPCloseConnection() does not close the TCPSocket. Instead of calling mDNSASLLog()
473             // repeatedly and logging the same error msg causing 100% CPU usage, we 
474             // crash mDNSResponder using assert() and restart fresh. See advantages below:
475             // 1.Better User Experience 
476             // 2.CrashLogs frequency can be monitored 
477             // 3.StackTrace can be used for more info 
478         }   
479
480         switch (tcpInfo->op)
481         {
482         case LNTDiscoveryOp:     if (m->UPnPSOAPAddressString == mDNSNULL)
483                 mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP Address", "");
484             if (m->UPnPSOAPURL == mDNSNULL)
485                 mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP path", "");
486             if (m->UPnPSOAPAddressString && m->UPnPSOAPURL)
487                 mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "success", "success", "");
488             break;
489         case LNTExternalAddrOp:  mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest",
490                                             mDNSIPv4AddressIsZero(m->ExtAddress) ? "failure" : "success",
491                                             mDNSIPv4AddressIsZero(m->ExtAddress) ? "failure" : "success", "");
492             break;
493         case LNTPortMapOp:       if (tcpInfo->parentNATInfo)
494                 mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", (tcpInfo->parentNATInfo->Result) ? "failure" : "success",
495                            (tcpInfo->parentNATInfo->Result) ? "failure" : "success", "Result: %d", tcpInfo->parentNATInfo->Result);
496             break;
497         case LNTPortMapDeleteOp: break;
498         default:                 break;
499         }
500
501         mDNSPlatformTCPCloseConnection(sock);
502         tcpInfo->sock = mDNSNULL;
503         if (tcpInfo->Request) { mDNSPlatformMemFree(tcpInfo->Request); tcpInfo->Request = mDNSNULL; }
504         if (tcpInfo->Reply  ) { mDNSPlatformMemFree(tcpInfo->Reply);   tcpInfo->Reply   = mDNSNULL; }
505     }
506     else
507     {
508         LNTERRORcount = 0;  // clear LNTERRORcount
509     }
510
511     if (tcpInfo) mDNS_Unlock(tcpInfo->m);
512
513     if (status == mStatus_ConfigChanged) DisposeInfoFromUnmapList(tcpInfo->m, tcpInfo);
514 }
515
516 mDNSlocal mStatus MakeTCPConnection(mDNS *const m, tcpLNTInfo *info, const mDNSAddr *const Addr, const mDNSIPPort Port, LNTOp_t op)
517 {
518     mStatus err = mStatus_NoError;
519     mDNSIPPort srcport = zeroIPPort;
520
521     if (mDNSIPv4AddressIsZero(Addr->ip.v4) || mDNSIPPortIsZero(Port))
522     { LogMsg("LNT MakeTCPConnection: bad address/port %#a:%d", Addr, mDNSVal16(Port)); return(mStatus_Invalid); }
523     info->m         = m;
524     info->Address   = *Addr;
525     info->Port      = Port;
526     info->op        = op;
527     info->nread     = 0;
528     info->replyLen  = LNT_MAXBUFSIZE;
529     if      (info->Reply != mDNSNULL) mDNSPlatformMemZero(info->Reply, LNT_MAXBUFSIZE);   // reuse previously allocated buffer
530     else if ((info->Reply = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate reply buffer"); return (mStatus_NoMemoryErr); }
531
532     if (info->sock) { LogInfo("MakeTCPConnection: closing previous open connection"); mDNSPlatformTCPCloseConnection(info->sock); info->sock = mDNSNULL; }
533     info->sock = mDNSPlatformTCPSocket(m, kTCPSocketFlags_Zero, &srcport, mDNSfalse);
534     if (!info->sock) { LogMsg("LNT MakeTCPConnection: unable to create TCP socket"); mDNSPlatformMemFree(info->Reply); info->Reply = mDNSNULL; return(mStatus_NoMemoryErr); }
535     LogInfo("MakeTCPConnection: connecting to %#a:%d", &info->Address, mDNSVal16(info->Port));
536     err = mDNSPlatformTCPConnect(info->sock, Addr, Port, mDNSNULL, 0, tcpConnectionCallback, info);
537
538     if      (err == mStatus_ConnPending) err = mStatus_NoError;
539     else if (err == mStatus_ConnEstablished)
540     {
541         mDNS_DropLockBeforeCallback();
542         tcpConnectionCallback(info->sock, info, mDNStrue, mStatus_NoError);
543         mDNS_ReclaimLockAfterCallback();
544         err = mStatus_NoError;
545     }
546     else
547     {
548         // Don't need to log this in customer builds -- it happens quite often during sleep, wake, configuration changes, etc.
549         LogInfo("LNT MakeTCPConnection: connection failed");
550         mDNSPlatformTCPCloseConnection(info->sock); // Dispose the socket we created with mDNSPlatformTCPSocket() above
551         info->sock = mDNSNULL;
552         mDNSPlatformMemFree(info->Reply);
553         info->Reply = mDNSNULL;
554     }
555     return(err);
556 }
557
558 mDNSlocal unsigned int AddSOAPArguments(char *const buf, const unsigned int maxlen, const int numArgs, const Property *const a)
559 {
560     static const char f1[] = "<%s>%s</%s>";
561     static const char f2[] = "<%s xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"%s\">%s</%s>";
562     int i, len = 0;
563     *buf = 0;
564     for (i = 0; i < numArgs; i++)
565     {
566         if (a[i].type) len += mDNS_snprintf(buf + len, maxlen - len, f2, a[i].name, a[i].type, a[i].value, a[i].name);
567         else len += mDNS_snprintf(buf + len, maxlen - len, f1, a[i].name,            a[i].value, a[i].name);
568     }
569     return(len);
570 }
571
572 mDNSlocal mStatus SendSOAPMsgControlAction(mDNS *m, tcpLNTInfo *info, const char *const Action, const int numArgs, const Property *const Arguments, const LNTOp_t op)
573 {
574     // SOAP message header format -
575     //  - control URL
576     //  - action (string)
577     //  - router's host/port ("host:port")
578     //  - content-length
579     static const char header[] =
580         "POST %s HTTP/1.1\r\n"
581         "Content-Type: text/xml; charset=\"utf-8\"\r\n"
582         "SOAPAction: \"urn:schemas-upnp-org:service:WAN%sConnection:1#%s\"\r\n"
583         "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n"
584         "Host: %s\r\n"
585         "Content-Length: %d\r\n"
586         "Connection: close\r\n"
587         "Pragma: no-cache\r\n"
588         "\r\n"
589         "%s\r\n";
590
591     static const char body1[] =
592         "<?xml version=\"1.0\"?>\r\n"
593         "<SOAP-ENV:Envelope"
594         " xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\""
595         " SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
596         "<SOAP-ENV:Body>"
597         "<m:%s xmlns:m=\"urn:schemas-upnp-org:service:WAN%sConnection:1\">";
598
599     static const char body2[] =
600         "</m:%s>"
601         "</SOAP-ENV:Body>"
602         "</SOAP-ENV:Envelope>\r\n";
603
604     mStatus err;
605     char   *body = (char*)&m->omsg;         // Typically requires 1110-1122 bytes; m->omsg is 8952 bytes, which is plenty
606     int bodyLen;
607
608     if (mDNSIPPortIsZero(m->UPnPSOAPPort) || m->UPnPSOAPURL == mDNSNULL || m->UPnPSOAPAddressString == mDNSNULL)    // if no SOAP URL or address exists get out here
609     { LogInfo("SendSOAPMsgControlAction: no SOAP port, URL or address string"); return mStatus_Invalid; }
610
611     // Create body
612     bodyLen  = mDNS_snprintf   (body,           sizeof(m->omsg),           body1,   Action,   m->UPnPWANPPPConnection ? "PPP" : "IP");
613     bodyLen += AddSOAPArguments(body + bodyLen, sizeof(m->omsg) - bodyLen, numArgs, Arguments);
614     bodyLen += mDNS_snprintf   (body + bodyLen, sizeof(m->omsg) - bodyLen, body2,   Action);
615
616     // Create info->Request; the header needs to contain the bodyLen in the "Content-Length" field
617     if (!info->Request) info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE);
618     if (!info->Request) { LogMsg("SendSOAPMsgControlAction: Can't allocate info->Request"); return mStatus_NoMemoryErr; }
619     info->requestLen = mDNS_snprintf((char*)info->Request, LNT_MAXBUFSIZE, header, m->UPnPSOAPURL, m->UPnPWANPPPConnection ? "PPP" : "IP", Action, m->UPnPSOAPAddressString, bodyLen, body);
620
621     err = MakeTCPConnection(m, info, &m->Router, m->UPnPSOAPPort, op);
622     if (err) { mDNSPlatformMemFree(info->Request); info->Request = mDNSNULL; }
623     return err;
624 }
625
626 // Build port mapping request with new port (up to max) and send it
627 mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n)
628 {
629     char externalPort[6];
630     char internalPort[6];
631     char localIPAddrString[30];
632     char publicPortString[40];
633     Property propArgs[8];
634     mDNSu16 ReqPortNum = RequestedPortNum(n);
635     NATTraversalInfo *n2 = m->NATTraversals;
636
637     // Scan our m->NATTraversals list to make sure the external port we're requesting is locally unique.
638     // UPnP gateways will report conflicts if different devices request the same external port, but if two
639     // clients on the same device request the same external port the second one just stomps over the first.
640     // One way this can happen is like this:
641     // 1. Client A binds local port 80
642     // 2. Client A requests external port 80 -> internal port 80
643     // 3. UPnP NAT gateway refuses external port 80 (some other client already has it)
644     // 4. Client A tries again, and successfully gets external port 80 -> internal port 81
645     // 5. Client B on same machine tries to bind local port 80, and fails
646     // 6. Client B tries again, and successfully binds local port 81
647     // 7. Client B now requests external port 81 -> internal port 81
648     // 8. UPnP NAT gateway allows this, stomping over Client A's existing mapping
649
650     while (n2)
651     {
652         if (n2 == n || RequestedPortNum(n2) != ReqPortNum) n2=n2->next;
653         else
654         {
655             if (n->tcpInfo.retries < 100)
656             {
657                 n->tcpInfo.retries++;
658                 ReqPortNum = RequestedPortNum(n);   // Pick a new port number
659                 n2 = m->NATTraversals;              // And re-scan the list looking for conflicts
660             }
661             else
662             {
663                 natTraversalHandlePortMapReply(m, n, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0, NATTProtocolUPNPIGD);
664                 return mStatus_NoError;
665             }
666         }
667     }
668
669     // create strings to use in the message
670     mDNS_snprintf(externalPort,      sizeof(externalPort),      "%u",   ReqPortNum);
671     mDNS_snprintf(internalPort,      sizeof(internalPort),      "%u",   mDNSVal16(n->IntPort));
672     mDNS_snprintf(publicPortString,  sizeof(publicPortString),  "iC%u", ReqPortNum);
673     mDNS_snprintf(localIPAddrString, sizeof(localIPAddrString), "%u.%u.%u.%u",
674                   m->AdvertisedV4.ip.v4.b[0], m->AdvertisedV4.ip.v4.b[1], m->AdvertisedV4.ip.v4.b[2], m->AdvertisedV4.ip.v4.b[3]);
675
676     // build the message
677     mDNSPlatformMemZero(propArgs, sizeof(propArgs));
678     propArgs[0].name  = "NewRemoteHost";
679     propArgs[0].type  = "string";
680     propArgs[0].value = "";
681     propArgs[1].name  = "NewExternalPort";
682     propArgs[1].type  = "ui2";
683     propArgs[1].value = externalPort;
684     propArgs[2].name  = "NewProtocol";
685     propArgs[2].type  = "string";
686     propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP";
687     propArgs[3].name  = "NewInternalPort";
688     propArgs[3].type  = "ui2";
689     propArgs[3].value = internalPort;
690     propArgs[4].name  = "NewInternalClient";
691     propArgs[4].type  = "string";
692     propArgs[4].value = localIPAddrString;
693     propArgs[5].name  = "NewEnabled";
694     propArgs[5].type  = "boolean";
695     propArgs[5].value = "1";
696     propArgs[6].name  = "NewPortMappingDescription";
697     propArgs[6].type  = "string";
698     propArgs[6].value = publicPortString;
699     propArgs[7].name  = "NewLeaseDuration";
700     propArgs[7].type  = "ui4";
701     propArgs[7].value = "0";
702
703     LogInfo("SendPortMapRequest: internal %u external %u", mDNSVal16(n->IntPort), ReqPortNum);
704     return SendSOAPMsgControlAction(m, &n->tcpInfo, "AddPortMapping", 8, propArgs, LNTPortMapOp);
705 }
706
707 mDNSexport mStatus LNT_MapPort(mDNS *m, NATTraversalInfo *const n)
708 {
709     LogInfo("LNT_MapPort");
710     if (n->tcpInfo.sock) return(mStatus_NoError);   // If we already have a connection up don't make another request for the same thing
711     n->tcpInfo.parentNATInfo = n;
712     n->tcpInfo.retries       = 0;
713     return SendPortMapRequest(m, n);
714 }
715
716 mDNSexport mStatus LNT_UnmapPort(mDNS *m, NATTraversalInfo *const n)
717 {
718     char externalPort[10];
719     Property propArgs[3];
720     tcpLNTInfo  *info;
721     tcpLNTInfo  **infoPtr = &m->tcpInfoUnmapList;
722     mStatus err;
723
724     // If no NAT gateway to talk to, no need to do all this work for nothing
725     if (mDNSIPPortIsZero(m->UPnPSOAPPort) || !m->UPnPSOAPURL || !m->UPnPSOAPAddressString) return mStatus_NoError;
726
727     mDNS_snprintf(externalPort, sizeof(externalPort), "%u", mDNSVal16(mDNSIPPortIsZero(n->RequestedPort) ? n->IntPort : n->RequestedPort));
728
729     mDNSPlatformMemZero(propArgs, sizeof(propArgs));
730     propArgs[0].name  = "NewRemoteHost";
731     propArgs[0].type  = "string";
732     propArgs[0].value = "";
733     propArgs[1].name  = "NewExternalPort";
734     propArgs[1].type  = "ui2";
735     propArgs[1].value = externalPort;
736     propArgs[2].name  = "NewProtocol";
737     propArgs[2].type  = "string";
738     propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP";
739
740     n->tcpInfo.parentNATInfo = n;
741
742     // clean up previous port mapping requests and allocations
743     if (n->tcpInfo.sock) LogInfo("LNT_UnmapPort: closing previous open connection");
744     if (n->tcpInfo.sock   ) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock    = mDNSNULL; }
745     if (n->tcpInfo.Request) { mDNSPlatformMemFree(n->tcpInfo.Request);         n->tcpInfo.Request = mDNSNULL; }
746     if (n->tcpInfo.Reply  ) { mDNSPlatformMemFree(n->tcpInfo.Reply);           n->tcpInfo.Reply   = mDNSNULL; }
747
748     // 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)
749     if ((info = mDNSPlatformMemAllocate(sizeof(tcpLNTInfo))) == mDNSNULL)
750     { LogInfo("LNT_UnmapPort: can't allocate tcpInfo"); return(mStatus_NoMemoryErr); }
751     *info = n->tcpInfo;
752
753     while (*infoPtr) infoPtr = &(*infoPtr)->next;   // find the end of the list
754     *infoPtr = info;    // append
755
756     err = SendSOAPMsgControlAction(m, info, "DeletePortMapping", 3, propArgs, LNTPortMapDeleteOp);
757     if (err) DisposeInfoFromUnmapList(m, info);
758     return err;
759 }
760
761 mDNSexport mStatus LNT_GetExternalAddress(mDNS *m)
762 {
763     return SendSOAPMsgControlAction(m, &m->tcpAddrInfo, "GetExternalIPAddress", 0, mDNSNULL, LNTExternalAddrOp);
764 }
765
766 mDNSlocal mStatus GetDeviceDescription(mDNS *m, tcpLNTInfo *info)
767 {
768     // Device description format -
769     //  - device description URL
770     //  - host/port
771     static const char szSSDPMsgDescribeDeviceFMT[] =
772         "GET %s HTTP/1.1\r\n"
773         "Accept: text/xml, application/xml\r\n"
774         "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
775         "Host: %s\r\n"
776         "Connection: close\r\n"
777         "\r\n";
778
779     if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return mStatus_NoError; // already have the info we need
780
781     if (m->UPnPRouterURL == mDNSNULL || m->UPnPRouterAddressString == mDNSNULL) { LogInfo("GetDeviceDescription: no router URL or address string!"); return (mStatus_Invalid); }
782
783     // build message
784     if      (info->Request != mDNSNULL) mDNSPlatformMemZero(info->Request, LNT_MAXBUFSIZE); // reuse previously allocated buffer
785     else if ((info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate send buffer for discovery"); return (mStatus_NoMemoryErr); }
786     info->requestLen = mDNS_snprintf((char*)info->Request, LNT_MAXBUFSIZE, szSSDPMsgDescribeDeviceFMT, m->UPnPRouterURL, m->UPnPRouterAddressString);
787     LogInfo("Describe Device: [%s]", info->Request);
788     return MakeTCPConnection(m, info, &m->Router, m->UPnPRouterPort, LNTDiscoveryOp);
789 }
790
791 // This function parses the response to our SSDP discovery message. Basically, we look to make sure this is a response
792 // referencing a service we care about (WANIPConnection or WANPPPConnection), then look for the "Location:" header and copy the addressing and
793 // URL info we need.
794 mDNSexport void LNT_ConfigureRouterInfo(mDNS *m, const mDNSInterfaceID InterfaceID, const mDNSu8 *const data, const mDNSu16 len)
795 {
796     const mDNSu8 *ptr = data;
797     const mDNSu8 *end = data + len;
798     const mDNSu8 *stop;
799
800     if (!mDNSIPPortIsZero(m->UPnPRouterPort)) return; // already have the info we need
801
802     // The formatting of the HTTP header is not always the same when it comes to the placement of
803     // the service and location strings, so we just look for each of them from the beginning for every response
804
805     // figure out if this is a message from a service we care about
806     while (ptr && ptr != end)
807     {
808         if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANIPConnection:1", 17) == 0)) break;
809         ptr++;
810     }
811     if (ptr == end)
812     {
813         ptr = data;
814         while (ptr && ptr != end)
815         {
816             if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANPPPConnection:1", 18) == 0)) break;
817             ptr++;
818         }
819     }
820     if (ptr == mDNSNULL || ptr == end) return;  // not a message we care about
821
822     // find "Location:", starting from the beginning
823     ptr = data;
824     while (ptr && ptr != end)
825     {
826         if ((*ptr & 0xDF) == 'L' && (strncasecmp((char*)ptr, "Location:", 9) == 0)) break;          // find the first 'L'; is this Location? if not, keep looking
827         ptr++;
828     }
829     if (ptr == mDNSNULL || ptr == end)
830     {
831         mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Location", "");
832         return; // not a message we care about
833     }
834     ptr += 9; //Skip over 'Location:'
835     while (*ptr == ' ' && ptr < end) ptr++; // skip over spaces
836     if (ptr >= end) return;
837
838     // find the end of the line
839     for (stop = ptr; stop != end; stop++) { if (*stop == '\r') { end = stop; break; } }
840
841     // fill in default port
842     m->UPnPRouterPort = mDNSOpaque16fromIntVal(80);
843
844     // free string pointers and set to NULL
845     if (m->UPnPRouterAddressString != mDNSNULL)
846     {
847         mDNSPlatformMemFree(m->UPnPRouterAddressString);
848         m->UPnPRouterAddressString = mDNSNULL;
849     }
850     if (m->UPnPRouterURL != mDNSNULL)
851     {
852         mDNSPlatformMemFree(m->UPnPRouterURL);
853         m->UPnPRouterURL = mDNSNULL;
854     }
855
856     // the Router URL should look something like "/dyndev/uuid:0013-108c-4b3f0000f3dc"
857     if (ParseHttpUrl(ptr, end, &m->UPnPRouterAddressString, &m->UPnPRouterPort, &m->UPnPRouterURL) != mStatus_NoError)
858     {
859         mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Parse URL", "");
860         return;
861     }
862
863     m->UPnPInterfaceID = InterfaceID;
864
865     if (m->UPnPRouterAddressString == mDNSNULL)
866     {
867         mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router address", "");
868         LogMsg("LNT_ConfigureRouterInfo: UPnPRouterAddressString is NULL");
869     }
870     else LogInfo("LNT_ConfigureRouterInfo: Router address string [%s]", m->UPnPRouterAddressString);
871
872     if (m->UPnPRouterURL == mDNSNULL)
873     {
874         mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router path", "");
875         LogMsg("LNT_ConfigureRouterInfo: UPnPRouterURL is NULL");
876     }
877     else LogInfo("LNT_ConfigureRouterInfo: Router URL [%s]", m->UPnPRouterURL);
878
879     LogInfo("LNT_ConfigureRouterInfo: Router port %d", mDNSVal16(m->UPnPRouterPort));
880     LogInfo("LNT_ConfigureRouterInfo: Router interface %d", m->UPnPInterfaceID);
881
882     // Don't need the SSDP socket anymore
883     if (m->SSDPSocket) { debugf("LNT_ConfigureRouterInfo destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
884
885     mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "success", "success", "");
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 *buf = (mDNSu8*)&m->omsg; //m->omsg is 8952 bytes, which is plenty
901     unsigned int bufLen;
902
903     if (!mDNSIPPortIsZero(m->UPnPRouterPort))
904     {
905         if (m->SSDPSocket) { debugf("LNT_SendDiscoveryMsg destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
906         if (mDNSIPPortIsZero(m->UPnPSOAPPort) && !m->tcpDeviceInfo.sock) GetDeviceDescription(m, &m->tcpDeviceInfo);
907         return;
908     }
909
910     // Always query for WANIPConnection in the first SSDP packet
911     if (m->retryIntervalGetAddr <= NATMAP_INIT_RETRY) m->SSDPWANPPPConnection = mDNSfalse;
912
913     // Create message
914     bufLen = mDNS_snprintf((char*)buf, sizeof(m->omsg), msg, m->SSDPWANPPPConnection ? "PPP" : "IP");
915
916     debugf("LNT_SendDiscoveryMsg Router %.4a Current External Address %.4a", &m->Router.ip.v4, &m->ExtAddress);
917
918     if (!mDNSIPv4AddressIsZero(m->Router.ip.v4))
919     {
920         if (!m->SSDPSocket) { m->SSDPSocket = mDNSPlatformUDPSocket(m, zeroIPPort); debugf("LNT_SendDiscoveryMsg created SSDPSocket %p", &m->SSDPSocket); }
921         mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &m->Router,     SSDPPort, mDNSfalse);
922         mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &multicastDest, SSDPPort, mDNSfalse);
923     }
924
925     m->SSDPWANPPPConnection = !m->SSDPWANPPPConnection;
926 }
927
928 mDNSexport void LNT_ClearState(mDNS *const m)
929 {
930     if (m->tcpAddrInfo.sock)   { mDNSPlatformTCPCloseConnection(m->tcpAddrInfo.sock);   m->tcpAddrInfo.sock   = mDNSNULL; }
931     if (m->tcpDeviceInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpDeviceInfo.sock); m->tcpDeviceInfo.sock = mDNSNULL; }
932     m->UPnPSOAPPort = m->UPnPRouterPort = zeroIPPort;   // Reset UPnP ports
933 }
934
935 #endif /* _LEGACY_NAT_TRAVERSAL_ */