]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/LegacyNATTraversal.c
mDNSResponder-522.92.1.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / LegacyNATTraversal.c
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
31 static int
32 inet_pton( int family, const char * addr, void * dst )
33 {
34 struct sockaddr_storage ss;
35 int sslen = sizeof( ss );
36
37 ZeroMemory( &ss, sizeof( ss ) );
38 ss.ss_family = (ADDRESS_FAMILY)family;
39
40 if ( WSAStringToAddressA( (LPSTR)addr, family, NULL, ( struct sockaddr* ) &ss, &sslen ) == 0 )
41 {
42 if ( family == AF_INET ) { memcpy( dst, &( ( struct sockaddr_in* ) &ss)->sin_addr, sizeof( IN_ADDR ) ); return 1; }
43 else if ( family == AF_INET6 ) { memcpy( dst, &( ( struct sockaddr_in6* ) &ss)->sin6_addr, sizeof( IN6_ADDR ) ); return 1; }
44 else return 0;
45 }
46 else return 0;
47 }
48 #else
49 # include <arpa/inet.h> // For inet_pton()
50 #endif
51
52 #include "mDNSEmbeddedAPI.h"
53 #include "uDNS.h" // For natTraversalHandleAddressReply() etc.
54
55 // used to format SOAP port mapping arguments
56 typedef struct Property_struct
57 {
58 char *name;
59 char *type;
60 char *value;
61 } Property;
62
63 // All of the text parsing in this file is intentionally transparent so that we know exactly
64 // what's being done to the text, with an eye towards preventing security problems.
65
66 // This is an evolving list of useful acronyms to know. Please add to it at will.
67 // ST Service Type
68 // NT Notification Type
69 // USN Unique Service Name
70 // UDN Unique Device Name
71 // UUID Universally Unique Identifier
72 // URN/urn Universal Resource Name
73
74 // Forward declaration because of circular reference:
75 // SendPortMapRequest -> SendSOAPMsgControlAction -> MakeTCPConnection -> tcpConnectionCallback -> handleLNTPortMappingResponse
76 // In the event of a port conflict, handleLNTPortMappingResponse then increments tcpInfo->retries and calls back to SendPortMapRequest to try again
77 mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n);
78
79 #define RequestedPortNum(n) (mDNSVal16(mDNSIPPortIsZero((n)->RequestedPort) ? (n)->IntPort : (n)->RequestedPort) + (mDNSu16)(n)->tcpInfo.retries)
80
81 // Note that this function assumes src is already NULL terminated
82 mDNSlocal void AllocAndCopy(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)
92 mDNSlocal 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
142 enum
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
152 mDNSlocal 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
195 mDNSlocal 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
300 mDNSlocal 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
345 mDNSlocal 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
399 mDNSlocal 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
406 mDNSlocal 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 }
460 exit:
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
514 mDNSlocal 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
556 mDNSlocal 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
570 mDNSlocal 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
625 mDNSlocal 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
705 mDNSexport 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
714 mDNSexport 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
759 mDNSexport mStatus LNT_GetExternalAddress(mDNS *m)
760 {
761 return SendSOAPMsgControlAction(m, &m->tcpAddrInfo, "GetExternalIPAddress", 0, mDNSNULL, LNTExternalAddrOp);
762 }
763
764 mDNSlocal 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.
792 mDNSexport 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
888 mDNSexport 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
926 mDNSexport 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_ */