source: rtems-libbsd/mDNSResponder/mDNSMacOSX/LegacyNATTraversal.c @ 9449f15

4.1155-freebsd-126-freebsd-12freebsd-9.3
Last change on this file since 9449f15 was 9449f15, checked in by Sebastian Huber <sebastian.huber@…>, on 01/30/14 at 12:52:13

mDNS: Import

The sources can be obtained via:

http://opensource.apple.com/tarballs/mDNSResponder/mDNSResponder-544.tar.gz

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