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_ */ 934