1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 Change History (most recent first):
19 $Log: LegacyNATTraversal.c,v $
20 Revision 1.65 2009/07/03 03:16:07 jessic2
21 <rdar://problem/7026146> BTMM: UPnP works in Leopard but doesn't work in SnowLeopard (URLBase is empty) Made changes to support the case where the URLBase tag exists but there isn't a valid URL
23 Revision 1.64 2009/06/25 21:07:44 herscher
24 <rdar://problem/4147784> B4W should support UPnP
26 Revision 1.63 2009/03/26 03:59:00 jessic2
27 Changes for <rdar://problem/6492552&6492593&6492609&6492613&6492628&6492640&6492699>
29 Revision 1.62 2009/02/13 06:31:09 cheshire
30 Converted LogOperation messages to LogInfo
32 Revision 1.61 2009/01/23 19:25:43 mcguire
33 <rdar://problem/6514439> UPnP: Should not use NATErr_Refused when too many conflict retries
35 Revision 1.60 2009/01/23 00:38:36 mcguire
36 <rdar://problem/5570906> BTMM: Doesn't work with Linksys WRT54GS firmware 4.71.1
38 Revision 1.59 2009/01/22 20:32:17 mcguire
39 <rdar://problem/6446934> BTMM: pref pane reports enabled but negotiation failed
40 Make sure we push the pointer out past the LF if we read it.
42 Revision 1.58 2009/01/22 01:15:58 mcguire
43 <rdar://problem/6446934> BTMM: pref pane reports enabled but negotiation failed
45 Revision 1.57 2008/12/19 21:09:22 mcguire
46 <rdar://problem/6431147> UPnP: error messages when canceling seemingly unrelated browse
48 Revision 1.56 2008/12/06 01:42:57 mcguire
49 <rdar://problem/6418958> Need to exponentially back-off after failure to get public address
51 Revision 1.55 2008/12/01 19:43:48 mcguire
52 <rdar://problem/6404766> UPnP: Handle errorCode 718 as a conflict when requesting a port mapping
54 Revision 1.54 2008/11/26 20:57:37 cheshire
55 For consistency with other similar macros, renamed mdnsIsDigit/mdnsIsLetter/mdnsValidHostChar
56 to mDNSIsDigit/mDNSIsLetter/mDNSValidHostChar
58 Revision 1.53 2008/11/26 20:34:04 cheshire
59 Changed "destroying SSDPSocket" LogOperation debugging messages to debugf
61 Revision 1.52 2008/11/26 19:54:03 cheshire
62 Changed some "LogOperation" debugging messages to "debugf"
64 Revision 1.51 2008/11/20 02:23:38 mcguire
65 <rdar://problem/6041208> need to handle URLBase
67 Revision 1.50 2008/09/20 00:34:22 mcguire
68 <rdar://problem/6129039> BTMM: Add support for WANPPPConnection
70 Revision 1.49 2008/08/07 21:51:13 mcguire
71 <rdar://problem/5904423> UPnP: Possible memory corruption bug
72 <rdar://problem/5930173> UPnP: Combine URL parsing code
74 Revision 1.48 2008/07/24 20:23:04 cheshire
75 <rdar://problem/3988320> Should use randomized source ports and transaction IDs to avoid DNS cache poisoning
77 Revision 1.47 2008/07/18 21:37:46 mcguire
78 <rdar://problem/5736845> BTMM: alternate SSDP queries between multicast & unicast
80 Revision 1.46 2008/05/13 01:51:12 mcguire
81 <rdar://problem/5839161> UPnP compatibility workaround for Netgear WGT624
83 Revision 1.45 2007/12/06 00:22:27 mcguire
84 <rdar://problem/5604567> BTMM: Doesn't work with Linksys WAG300N 1.01.06 (sending from 1026/udp)
86 Revision 1.44 2007/11/02 20:45:40 cheshire
87 Don't log "connection failed" in customer builds
89 Revision 1.43 2007/10/18 20:09:47 cheshire
90 <rdar://problem/5545930> BTMM: Back to My Mac not working with D-Link DGL-4100 NAT gateway
92 Revision 1.42 2007/10/16 17:37:18 cheshire
93 <rdar://problem/3557903> Performance: Core code will not work on platforms with small stacks
94 Cut SendSOAPMsgControlAction stack from 2144 to 96 bytes
96 Revision 1.41 2007/10/15 23:02:00 cheshire
97 Off-by-one error: Incorrect trailing zero byte on the end of the SSDP Discovery message
99 Revision 1.40 2007/09/20 21:41:49 cheshire
100 <rdar://problem/5495568> Legacy NAT Traversal - unmap request failed with error -65549
102 Revision 1.39 2007/09/20 20:41:40 cheshire
103 Reordered functions in file, in preparation for following fix
105 Revision 1.38 2007/09/18 21:42:30 cheshire
106 To reduce programming mistakes, renamed ExtPort to RequestedPort
108 Revision 1.37 2007/09/14 21:26:09 cheshire
109 <rdar://problem/5482627> BTMM: Need to manually avoid port conflicts when using UPnP gateways
111 Revision 1.36 2007/09/14 01:15:50 cheshire
112 Minor fixes for problems discovered in pre-submission testing
114 Revision 1.35 2007/09/13 00:16:42 cheshire
115 <rdar://problem/5468706> Miscellaneous NAT Traversal improvements
117 Revision 1.34 2007/09/12 23:03:08 cheshire
118 <rdar://problem/5476978> DNSServiceNATPortMappingCreate callback not giving correct interface index
120 Revision 1.33 2007/09/12 19:22:20 cheshire
121 Variable renaming in preparation for upcoming fixes e.g. priv/pub renamed to intport/extport
122 Made NAT Traversal packet handlers take typed data instead of anonymous "mDNSu8 *" byte pointers
124 Revision 1.32 2007/09/11 19:19:16 cheshire
125 Correct capitalization of "uPNP" to "UPnP"
127 Revision 1.31 2007/09/10 22:14:16 cheshire
128 When constructing fake NATAddrReply or NATPortMapReply packet, need to calculate
129 plausible upseconds value or core logic will think NAT engine has been rebooted
131 Revision 1.30 2007/09/05 20:46:17 cheshire
132 Tidied up alignment of code layout
134 Revision 1.29 2007/08/03 20:18:01 vazquez
135 <rdar://problem/5382177> LegacyNATTraversal: reading out of bounds can lead to DoS
137 Revision 1.28 2007/07/31 02:28:36 vazquez
138 <rdar://problem/3734269> NAT-PMP: Detect public IP address changes and base station reboot
140 Revision 1.27 2007/07/30 23:17:03 vazquez
141 Since lease times are meaningless in UPnP, return NATMAP_DEFAULT_LEASE in UPnP port mapping reply
143 Revision 1.26 2007/07/27 22:50:08 vazquez
144 Allocate memory for UPnP request and reply buffers instead of using arrays
146 Revision 1.25 2007/07/27 20:33:44 vazquez
147 Make sure we clean up previous port mapping requests before starting an unmap
149 Revision 1.24 2007/07/27 00:57:48 vazquez
150 If a tcp connection is already established for doing a port mapping, don't start it again
152 Revision 1.23 2007/07/26 21:19:26 vazquez
153 Retry port mapping with incremented port number (up to max) in order to handle
154 port mapping conflicts on UPnP gateways
156 Revision 1.22 2007/07/25 21:41:00 vazquez
157 Make sure we clean up opened sockets when there are network transitions and when changing
160 Revision 1.21 2007/07/25 03:05:03 vazquez
162 <rdar://problem/5338913> LegacyNATTraversal: UPnP heap overflow
163 <rdar://problem/5338933> LegacyNATTraversal: UPnP stack buffer overflow
164 and a myriad of other security problems
166 Revision 1.20 2007/07/16 20:15:10 vazquez
167 <rdar://problem/3867231> LegacyNATTraversal: Need complete rewrite
169 Revision 1.19 2007/06/21 16:37:43 jgraessley
171 Reviewed by: Stuart Cheshire
172 Additional changes to get this compiling on the embedded platform.
174 Revision 1.18 2007/05/09 01:43:32 cheshire
175 <rdar://problem/5187028> Change sprintf and strcpy to their safer snprintf and strlcpy equivalents
177 Revision 1.17 2007/02/27 02:48:25 cheshire
178 Parameter to LNT_GetPublicIP function is IPv4 address, not anonymous "mDNSOpaque32" object
180 Revision 1.16 2006/08/14 23:24:39 cheshire
181 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
183 Revision 1.15 2006/07/05 23:30:57 cheshire
184 Rename LegacyNATInit() -> LNT_Init()
186 Revision 1.14 2005/12/08 03:00:33 cheshire
187 <rdar://problem/4349971> Byte order bugs in Legacy NAT traversal code
189 Revision 1.13 2005/09/07 18:23:05 ksekar
190 <rdar://problem/4151514> Off-by-one overflow in LegacyNATTraversal
192 Revision 1.12 2005/07/22 21:36:16 ksekar
193 Fix GCC 4.0/Intel compiler warnings
195 Revision 1.11 2004/12/03 03:34:20 ksekar
196 <rdar://problem/3882674> LegacyNATTraversal.c leaks threads
198 Revision 1.10 2004/12/01 02:43:49 cheshire
199 Update copyright message
201 Revision 1.9 2004/10/27 02:25:05 cheshire
202 <rdar://problem/3816029> Random memory smashing bug
204 Revision 1.8 2004/10/27 02:17:21 cheshire
205 Turn off "safe_close: ERROR" error messages -- there are too many of them
207 Revision 1.7 2004/10/26 21:15:40 cheshire
208 <rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0
209 Additional fixes: Code should set fds to -1 after closing sockets.
211 Revision 1.6 2004/10/26 20:59:20 cheshire
212 <rdar://problem/3854314> Legacy NAT traversal code closes file descriptor 0
214 Revision 1.5 2004/10/26 01:01:35 cheshire
215 Use "#if 0" instead of commenting out code
217 Revision 1.4 2004/10/10 06:51:36 cheshire
218 Declared some strings "const" as appropriate
220 Revision 1.3 2004/09/21 23:40:12 ksekar
221 <rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure
223 Revision 1.2 2004/09/17 01:08:52 cheshire
224 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
225 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
226 declared in that file are ONLY appropriate to single-address-space embedded applications.
227 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
229 Revision 1.1 2004/08/18 17:35:41 ksekar
230 <rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways
233 #ifdef _LEGACY_NAT_TRAVERSAL_
235 #include "stdlib.h" // For strtol()
236 #include "string.h" // For strlcpy(), For strncpy(), strncasecmp()
239 # include <winsock2.h>
240 # include <ws2tcpip.h>
241 # define strcasecmp _stricmp
242 # define strncasecmp _strnicmp
243 # define mDNSASLLog( UUID, SUBDOMAIN, RESULT, SIGNATURE, FORMAT, ... ) ;
246 inet_pton( int family
, const char * addr
, void * dst
)
248 struct sockaddr_storage ss
;
249 int sslen
= sizeof( ss
);
251 ZeroMemory( &ss
, sizeof( ss
) );
252 ss
.ss_family
= family
;
254 if ( WSAStringToAddressA( addr
, family
, NULL
, ( struct sockaddr
* ) &ss
, &sslen
) == 0 )
256 if ( family
== AF_INET
) { memcpy( dst
, &( ( struct sockaddr_in
* ) &ss
)->sin_addr
, sizeof( IN_ADDR
) ); return 1; }
257 else if ( family
== AF_INET6
) { memcpy( dst
, &( ( struct sockaddr_in6
* ) &ss
)->sin6_addr
, sizeof( IN6_ADDR
) ); return 1; }
263 # include <arpa/inet.h> // For inet_pton()
266 #include "mDNSEmbeddedAPI.h"
267 #include "uDNS.h" // For natTraversalHandleAddressReply() etc.
269 // used to format SOAP port mapping arguments
270 typedef struct Property_struct
277 // All of the text parsing in this file is intentionally transparent so that we know exactly
278 // what's being done to the text, with an eye towards preventing security problems.
280 // This is an evolving list of useful acronyms to know. Please add to it at will.
282 // NT Notification Type
283 // USN Unique Service Name
284 // UDN Unique Device Name
285 // UUID Universally Unique Identifier
286 // URN/urn Universal Resource Name
288 // Forward declaration because of circular reference:
289 // SendPortMapRequest -> SendSOAPMsgControlAction -> MakeTCPConnection -> tcpConnectionCallback -> handleLNTPortMappingResponse
290 // In the event of a port conflict, handleLNTPortMappingResponse then increments tcpInfo->retries and calls back to SendPortMapRequest to try again
291 mDNSlocal mStatus
SendPortMapRequest(mDNS
*m
, NATTraversalInfo
*n
);
293 #define RequestedPortNum(n) (mDNSVal16(mDNSIPPortIsZero((n)->RequestedPort) ? (n)->IntPort : (n)->RequestedPort) + (n)->tcpInfo.retries)
295 // Note that this function assumes src is already NULL terminated
296 mDNSlocal
void AllocAndCopy(mDNSu8
** dst
, mDNSu8
* src
)
298 if (src
== mDNSNULL
) return;
299 if ((*dst
= (mDNSu8
*) mDNSPlatformMemAllocate(strlen((char*)src
) + 1)) == mDNSNULL
) { LogMsg("AllocAndCopy: can't allocate string"); return; }
300 strcpy((char *)*dst
, (char*)src
);
303 // This function does a simple parse of an HTTP URL that may include a hostname, port, and path
304 // 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)
305 mDNSlocal mStatus
ParseHttpUrl(char* ptr
, char* end
, mDNSu8
** addressAndPort
, mDNSIPPort
* port
, mDNSu8
** path
)
307 // if the data begins with "http://", we assume there is a hostname and possibly a port number
308 if (end
- ptr
>= 7 && strncasecmp(ptr
, "http://", 7) == 0)
312 char* addrPtr
= mDNSNULL
;
314 ptr
+= 7; //skip over "http://"
315 if (ptr
>= end
) { LogInfo("ParseHttpUrl: past end of buffer parsing host:port"); return mStatus_BadParamErr
; }
317 // find the end of the host:port
319 for (i
= 0; addrPtr
&& addrPtr
!= end
; i
++, addrPtr
++) if (*addrPtr
== '/') break;
321 // allocate the buffer (len i+1 so we have space to terminate the string)
322 if ((*addressAndPort
= (mDNSu8
*) mDNSPlatformMemAllocate(i
+1)) == mDNSNULL
) { LogMsg("ParseHttpUrl: can't allocate address string"); return mStatus_NoMemoryErr
; }
323 strncpy((char *)*addressAndPort
, ptr
, i
);
324 (*addressAndPort
)[i
] = '\0';
326 // find the port number in the string, by looking backwards for the ':'
327 stop
= ptr
; // can't go back farther than the original start
328 ptr
= addrPtr
; // move ptr to the path part
330 for (addrPtr
--;addrPtr
>stop
;addrPtr
--)
335 addrPtr
++; // skip over ':'
336 tmpport
= (int)strtol(addrPtr
, mDNSNULL
, 10);
337 *port
= mDNSOpaque16fromIntVal(tmpport
); // store it properly converted
343 // ptr should now point to the first character we haven't yet processed
344 // everything that remains is the path
345 if (path
&& ptr
< end
)
347 if ((*path
= (mDNSu8
*)mDNSPlatformMemAllocate(end
- ptr
+ 1)) == mDNSNULL
) { LogMsg("ParseHttpUrl: can't mDNSPlatformMemAllocate path"); return mStatus_NoMemoryErr
; }
348 strncpy((char *)*path
, ptr
, end
- ptr
);
349 (*path
)[end
- ptr
] = '\0';
352 return mStatus_NoError
;
357 HTTPCode_NeedMoreData
= -1, // No code found in stream
358 HTTPCode_Other
= -2, // Valid code other than those below found in stream
365 mDNSlocal mDNSs16
ParseHTTPResponseCode(mDNSu8
** data
, mDNSu8
* end
)
370 if (end
- ptr
< 5) return HTTPCode_NeedMoreData
;
371 if (strncasecmp((char*)ptr
, "HTTP/", 5) != 0) return HTTPCode_Bad
;
373 // should we care about the HTTP protocol version?
375 // look for first space, which must come before first LF
376 while (ptr
&& ptr
!= end
)
378 if (*ptr
== '\n') return HTTPCode_Bad
;
379 if (*ptr
== ' ') break;
382 if (ptr
== end
) return HTTPCode_NeedMoreData
;
385 if (end
- ptr
< 3) return HTTPCode_NeedMoreData
;
389 while (ptr
&& ptr
!= end
)
391 if (*ptr
== '\n') break;
394 if (ptr
== end
) return HTTPCode_NeedMoreData
;
397 if (memcmp(code
, "200", 3) == 0) return HTTPCode_200
;
398 if (memcmp(code
, "404", 3) == 0) return HTTPCode_404
;
399 if (memcmp(code
, "500", 3) == 0) return HTTPCode_500
;
401 LogInfo("ParseHTTPResponseCode found unexpected result code: %c%c%c", code
[0], code
[1], code
[2]);
402 return HTTPCode_Other
;
405 // This function parses the xml body of the device description response from the router. Basically, we look to make sure this is a response
406 // referencing a service we care about (WANIPConnection or WANPPPConnection), look for the "controlURL" header immediately following, and copy the addressing and URL info we need
407 mDNSlocal
void handleLNTDeviceDescriptionResponse(tcpLNTInfo
*tcpInfo
)
409 mDNS
*m
= tcpInfo
->m
;
410 char *ptr
= (char *)tcpInfo
->Reply
;
411 char *end
= (char *)tcpInfo
->Reply
+ tcpInfo
->nread
;
412 char *stop
= mDNSNULL
;
415 if (!mDNSIPPortIsZero(m
->UPnPSOAPPort
)) return; // already have the info we need
417 http_result
= ParseHTTPResponseCode((mDNSu8
**)&ptr
, (mDNSu8
*)end
); // Note: modifies ptr
418 if (http_result
== HTTPCode_404
) LNT_ClearState(m
);
419 if (http_result
!= HTTPCode_200
)
421 mDNSASLLog((uuid_t
*)&m
->asl_uuid
, "natt.legacy.DeviceDescription", "noop", "HTTP Result", "HTTP code: %d", http_result
);
425 // Always reset our flag to use WANIPConnection. We'll use WANPPPConnection if we find it and don't find WANIPConnection.
426 m
->UPnPWANPPPConnection
= mDNSfalse
;
428 // find either service we care about
429 while (ptr
&& ptr
< end
)
431 if (*ptr
== 'W' && (strncasecmp(ptr
, "WANIPConnection:1", 17) == 0)) break;
436 ptr
= (char *)tcpInfo
->Reply
;
437 while (ptr
&& ptr
< end
)
439 if (*ptr
== 'W' && (strncasecmp(ptr
, "WANPPPConnection:1", 18) == 0))
441 m
->UPnPWANPPPConnection
= mDNStrue
;
447 if (ptr
== mDNSNULL
|| ptr
== end
) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find WANIPConnection:1 or WANPPPConnection:1 string"); return; }
449 // find "controlURL", starting from where we left off
450 while (ptr
&& ptr
< end
)
452 if (*ptr
== 'c' && (strncasecmp(ptr
, "controlURL", 10) == 0)) break; // find the first 'c'; is this controlURL? if not, keep looking
455 if (ptr
== mDNSNULL
|| ptr
== end
) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find controlURL string"); return; }
456 ptr
+= 11; // skip over "controlURL>"
457 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
459 // find the end of the controlURL element
460 for (stop
= ptr
; stop
< end
; stop
++) { if (*stop
== '<') { end
= stop
; break; } }
462 // fill in default port
463 m
->UPnPSOAPPort
= m
->UPnPRouterPort
;
465 // free string pointers and set to NULL
466 if (m
->UPnPSOAPAddressString
!= mDNSNULL
)
468 mDNSPlatformMemFree(m
->UPnPSOAPAddressString
);
469 m
->UPnPSOAPAddressString
= mDNSNULL
;
471 if (m
->UPnPSOAPURL
!= mDNSNULL
)
473 mDNSPlatformMemFree(m
->UPnPSOAPURL
);
474 m
->UPnPSOAPURL
= mDNSNULL
;
477 if (ParseHttpUrl(ptr
, end
, &m
->UPnPSOAPAddressString
, &m
->UPnPSOAPPort
, &m
->UPnPSOAPURL
) != mStatus_NoError
) return;
478 // the SOAPURL should look something like "/uuid:0013-108c-4b3f0000f3dc"
480 if (m
->UPnPSOAPAddressString
== mDNSNULL
)
482 ptr
= (char *)tcpInfo
->Reply
;
483 while (ptr
&& ptr
< end
)
485 if (*ptr
== 'U' && (strncasecmp(ptr
, "URLBase", 7) == 0)) break;
489 if (ptr
< end
) // found URLBase
491 LogInfo("handleLNTDeviceDescriptionResponse: found URLBase");
492 ptr
+= 8; // skip over "URLBase>"
493 // find the end of the URLBase element
494 for (stop
= ptr
; stop
< end
; stop
++) { if (*stop
== '<') { end
= stop
; break; } }
495 if (ParseHttpUrl(ptr
, end
, &m
->UPnPSOAPAddressString
, &m
->UPnPSOAPPort
, mDNSNULL
) != mStatus_NoError
)
497 LogInfo("handleLNTDeviceDescriptionResponse: failed to parse URLBase");
501 // if all else fails, use the router address string
502 if (m
->UPnPSOAPAddressString
== mDNSNULL
) AllocAndCopy(&m
->UPnPSOAPAddressString
, m
->UPnPRouterAddressString
);
504 if (m
->UPnPSOAPAddressString
== mDNSNULL
) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPAddressString is NULL");
505 else LogInfo("handleLNTDeviceDescriptionResponse: SOAP address string [%s]", m
->UPnPSOAPAddressString
);
507 if (m
->UPnPSOAPURL
== mDNSNULL
) AllocAndCopy(&m
->UPnPSOAPURL
, m
->UPnPRouterURL
);
508 if (m
->UPnPSOAPURL
== mDNSNULL
) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPURL is NULL");
509 else LogInfo("handleLNTDeviceDescriptionResponse: SOAP URL [%s]", m
->UPnPSOAPURL
);
512 mDNSlocal
void handleLNTGetExternalAddressResponse(tcpLNTInfo
*tcpInfo
)
514 mDNS
*m
= tcpInfo
->m
;
515 mDNSu16 err
= NATErr_None
;
517 mDNSu8
*ptr
= (mDNSu8
*)tcpInfo
->Reply
;
518 mDNSu8
*end
= (mDNSu8
*)tcpInfo
->Reply
+ tcpInfo
->nread
;
520 static char tagname
[20] = "NewExternalIPAddress"; // Array NOT including a terminating nul
522 // LogInfo("handleLNTGetExternalAddressResponse: %s", ptr);
524 mDNSs16 http_result
= ParseHTTPResponseCode(&ptr
, end
); // Note: modifies ptr
525 if (http_result
== HTTPCode_404
) LNT_ClearState(m
);
526 if (http_result
!= HTTPCode_200
)
528 mDNSASLLog((uuid_t
*)&m
->asl_uuid
, "natt.legacy.AddressRequest", "noop", "HTTP Result", "HTTP code: %d", http_result
);
533 while (ptr
< end
&& strncasecmp((char*)ptr
, tagname
, sizeof(tagname
))) ptr
++;
534 ptr
+= sizeof(tagname
); // Skip over "NewExternalIPAddress"
535 while (ptr
< end
&& *ptr
!= '>') ptr
++;
536 ptr
+= 1; // Skip over ">"
537 // Find the end of the address and terminate the string so inet_pton() can convert it
539 while (addrend
< end
&& (mDNSIsDigit(*addrend
) || *addrend
== '.')) addrend
++;
540 if (addrend
>= end
) return;
543 if (inet_pton(AF_INET
, (char*)ptr
, &ExtAddr
) <= 0)
545 mDNSASLLog((uuid_t
*)&m
->asl_uuid
, "natt.legacy.AddressRequest", "noop", "inet_pton", "");
546 LogMsg("handleLNTGetExternalAddressResponse: Router returned bad address %s", ptr
);
547 err
= NATErr_NetFail
;
548 ExtAddr
= zerov4Addr
;
550 if (!err
) LogInfo("handleLNTGetExternalAddressResponse: External IP address is %.4a", &ExtAddr
);
552 natTraversalHandleAddressReply(m
, err
, ExtAddr
);
555 mDNSlocal
void handleLNTPortMappingResponse(tcpLNTInfo
*tcpInfo
)
557 mDNS
*m
= tcpInfo
->m
;
558 mDNSIPPort extport
= zeroIPPort
;
559 mDNSu8
*ptr
= (mDNSu8
*)tcpInfo
->Reply
;
560 mDNSu8
*end
= (mDNSu8
*)tcpInfo
->Reply
+ tcpInfo
->nread
;
561 NATTraversalInfo
*natInfo
;
564 for (natInfo
= m
->NATTraversals
; natInfo
; natInfo
=natInfo
->next
) { if (natInfo
== tcpInfo
->parentNATInfo
) break; }
566 if (!natInfo
) { LogInfo("handleLNTPortMappingResponse: can't find matching tcpInfo in NATTraversals!"); return; }
568 http_result
= ParseHTTPResponseCode(&ptr
, end
); // Note: modifies ptr
569 if (http_result
== HTTPCode_200
)
571 LogInfo("handleLNTPortMappingResponse: got a valid response, sending reply to natTraversalHandlePortMapReply(internal %d external %d retries %d)",
572 mDNSVal16(natInfo
->IntPort
), RequestedPortNum(natInfo
), tcpInfo
->retries
);
574 // Make sure to compute extport *before* we zero tcpInfo->retries
575 extport
= mDNSOpaque16fromIntVal(RequestedPortNum(natInfo
));
576 tcpInfo
->retries
= 0;
577 natTraversalHandlePortMapReply(m
, natInfo
, m
->UPnPInterfaceID
, mStatus_NoError
, extport
, NATMAP_DEFAULT_LEASE
);
579 else if (http_result
== HTTPCode_500
)
581 while (ptr
&& ptr
!= end
)
583 if (((*ptr
== 'c' || *ptr
== 'C') && end
- ptr
>= 8 && strncasecmp((char*)ptr
, "Conflict", 8) == 0) || (*ptr
== '>' && end
- ptr
>= 15 && strncasecmp((char*)ptr
, ">718</errorCode", 15) == 0))
585 if (tcpInfo
->retries
< 100)
587 tcpInfo
->retries
++; SendPortMapRequest(tcpInfo
->m
, natInfo
);
588 mDNSASLLog((uuid_t
*)&m
->asl_uuid
, "natt.legacy.PortMapRequest", "noop", "Conflict", "Retry %d", tcpInfo
->retries
);
592 LogMsg("handleLNTPortMappingResponse too many conflict retries %d %d", mDNSVal16(natInfo
->IntPort
), mDNSVal16(natInfo
->RequestedPort
));
593 mDNSASLLog((uuid_t
*)&m
->asl_uuid
, "natt.legacy.PortMapRequest", "noop", "Conflict - too many retries", "Retries: %d", tcpInfo
->retries
);
594 natTraversalHandlePortMapReply(m
, natInfo
, m
->UPnPInterfaceID
, NATErr_Res
, zeroIPPort
, 0);
601 else if (http_result
== HTTPCode_Bad
) LogMsg("handleLNTPortMappingResponse got data that was not a valid HTTP response");
602 else if (http_result
== HTTPCode_Other
) LogMsg("handleLNTPortMappingResponse got unexpected response code");
603 else if (http_result
== HTTPCode_404
) LNT_ClearState(m
);
604 if (http_result
!= HTTPCode_200
&& http_result
!= HTTPCode_500
)
605 mDNSASLLog((uuid_t
*)&m
->asl_uuid
, "natt.legacy.PortMapRequest", "noop", "HTTP Result", "HTTP code: %d", http_result
);
608 mDNSlocal
void DisposeInfoFromUnmapList(mDNS
*m
, tcpLNTInfo
*tcpInfo
)
610 tcpLNTInfo
**ptr
= &m
->tcpInfoUnmapList
;
611 while (*ptr
&& *ptr
!= tcpInfo
) ptr
= &(*ptr
)->next
;
612 if (*ptr
) { *ptr
= (*ptr
)->next
; mDNSPlatformMemFree(tcpInfo
); } // If we found it, cut it from our list and free the memory
615 mDNSlocal
void tcpConnectionCallback(TCPSocket
*sock
, void *context
, mDNSBool ConnectionEstablished
, mStatus err
)
617 mStatus status
= mStatus_NoError
;
618 tcpLNTInfo
*tcpInfo
= (tcpLNTInfo
*)context
;
619 mDNSBool closed
= mDNSfalse
;
623 if (tcpInfo
== mDNSNULL
) { LogInfo("tcpConnectionCallback: no tcpInfo context"); status
= mStatus_Invalid
; goto exit
; }
625 // The handlers below expect to be called with the lock held
626 mDNS_Lock(tcpInfo
->m
);
628 if (err
) { LogInfo("tcpConnectionCallback: received error"); goto exit
; }
630 if (ConnectionEstablished
) // connection is established - send the message
632 LogInfo("tcpConnectionCallback: connection established, sending message");
633 nsent
= mDNSPlatformWriteTCP(sock
, (char *)tcpInfo
->Request
, tcpInfo
->requestLen
);
634 if (nsent
!= (long)tcpInfo
->requestLen
) { LogMsg("tcpConnectionCallback: error writing"); status
= mStatus_UnknownErr
; goto exit
; }
638 n
= mDNSPlatformReadTCP(sock
, (char *)tcpInfo
->Reply
+ tcpInfo
->nread
, tcpInfo
->replyLen
- tcpInfo
->nread
, &closed
);
639 LogInfo("tcpConnectionCallback: mDNSPlatformReadTCP read %d bytes", n
);
641 if (n
< 0) { LogInfo("tcpConnectionCallback - read returned %d", n
); status
= mStatus_ConnFailed
; goto exit
; }
642 else if (closed
) { LogInfo("tcpConnectionCallback: socket closed by remote end %d", tcpInfo
->nread
); status
= mStatus_ConnFailed
; goto exit
; }
645 LogInfo("tcpConnectionCallback tcpInfo->nread %d", tcpInfo
->nread
);
646 if (tcpInfo
->nread
> LNT_MAXBUFSIZE
)
648 LogInfo("result truncated...");
649 tcpInfo
->nread
= LNT_MAXBUFSIZE
;
654 case LNTDiscoveryOp
: handleLNTDeviceDescriptionResponse (tcpInfo
); break;
655 case LNTExternalAddrOp
: handleLNTGetExternalAddressResponse(tcpInfo
); break;
656 case LNTPortMapOp
: handleLNTPortMappingResponse (tcpInfo
); break;
657 case LNTPortMapDeleteOp
: status
= mStatus_ConfigChanged
; break;
658 default: LogMsg("tcpConnectionCallback: bad tcp operation! %d", tcpInfo
->op
); status
= mStatus_Invalid
; break;
664 mDNS
*m
= tcpInfo
->m
;
667 case LNTDiscoveryOp
: if (m
->UPnPSOAPAddressString
== mDNSNULL
)
668 mDNSASLLog((uuid_t
*)&m
->asl_uuid
, "natt.legacy.DeviceDescription", "failure", "SOAP Address", "");
669 if (m
->UPnPSOAPURL
== mDNSNULL
)
670 mDNSASLLog((uuid_t
*)&m
->asl_uuid
, "natt.legacy.DeviceDescription", "failure", "SOAP path", "");
671 if (m
->UPnPSOAPAddressString
&& m
->UPnPSOAPURL
)
672 mDNSASLLog((uuid_t
*)&m
->asl_uuid
, "natt.legacy.DeviceDescription", "success", "success", "");
674 case LNTExternalAddrOp
: mDNSASLLog((uuid_t
*)&m
->asl_uuid
, "natt.legacy.AddressRequest", mDNSIPv4AddressIsZero(m
->ExternalAddress
) ? "failure" : "success", mDNSIPv4AddressIsZero(m
->ExternalAddress
) ? "failure" : "success", "");
676 case LNTPortMapOp
: if (tcpInfo
->parentNATInfo
)
677 mDNSASLLog((uuid_t
*)&m
->asl_uuid
, "natt.legacy.PortMapRequest", (tcpInfo
->parentNATInfo
->Result
) ? "failure" : "success",
678 (tcpInfo
->parentNATInfo
->Result
) ? "failure" : "success", "Result: %d", tcpInfo
->parentNATInfo
->Result
);
680 case LNTPortMapDeleteOp
: break;
684 mDNSPlatformTCPCloseConnection(tcpInfo
->sock
);
685 tcpInfo
->sock
= mDNSNULL
;
686 if (tcpInfo
->Request
) { mDNSPlatformMemFree(tcpInfo
->Request
); tcpInfo
->Request
= mDNSNULL
; }
687 if (tcpInfo
->Reply
) { mDNSPlatformMemFree(tcpInfo
->Reply
); tcpInfo
->Reply
= mDNSNULL
; }
690 if (tcpInfo
) mDNS_Unlock(tcpInfo
->m
);
692 if (status
== mStatus_ConfigChanged
) DisposeInfoFromUnmapList(tcpInfo
->m
, tcpInfo
);
695 mDNSlocal mStatus
MakeTCPConnection(mDNS
*const m
, tcpLNTInfo
*info
, const mDNSAddr
*const Addr
, const mDNSIPPort Port
, LNTOp_t op
)
697 mStatus err
= mStatus_NoError
;
698 mDNSIPPort srcport
= zeroIPPort
;
700 if (mDNSIPv4AddressIsZero(Addr
->ip
.v4
) || mDNSIPPortIsZero(Port
))
701 { LogMsg("LNT MakeTCPConnection: bad address/port %#a:%d", Addr
, mDNSVal16(Port
)); return(mStatus_Invalid
); }
703 info
->Address
= *Addr
;
707 info
->replyLen
= LNT_MAXBUFSIZE
;
708 if (info
->Reply
!= mDNSNULL
) mDNSPlatformMemZero(info
->Reply
, LNT_MAXBUFSIZE
); // reuse previously allocated buffer
709 else if ((info
->Reply
= (mDNSs8
*) mDNSPlatformMemAllocate(LNT_MAXBUFSIZE
)) == mDNSNULL
) { LogInfo("can't allocate reply buffer"); return (mStatus_NoMemoryErr
); }
711 if (info
->sock
) { LogInfo("MakeTCPConnection: closing previous open connection"); mDNSPlatformTCPCloseConnection(info
->sock
); info
->sock
= mDNSNULL
; }
712 info
->sock
= mDNSPlatformTCPSocket(m
, kTCPSocketFlags_Zero
, &srcport
);
713 if (!info
->sock
) { LogMsg("LNT MakeTCPConnection: unable to create TCP socket"); mDNSPlatformMemFree(info
->Reply
); info
->Reply
= mDNSNULL
; return(mStatus_NoMemoryErr
); }
714 LogInfo("MakeTCPConnection: connecting to %#a:%d", &info
->Address
, mDNSVal16(info
->Port
));
715 err
= mDNSPlatformTCPConnect(info
->sock
, Addr
, Port
, 0, tcpConnectionCallback
, info
);
717 if (err
== mStatus_ConnPending
) err
= mStatus_NoError
;
718 else if (err
== mStatus_ConnEstablished
)
720 mDNS_DropLockBeforeCallback();
721 tcpConnectionCallback(info
->sock
, info
, mDNStrue
, mStatus_NoError
);
722 mDNS_ReclaimLockAfterCallback();
723 err
= mStatus_NoError
;
727 // Don't need to log this in customer builds -- it happens quite often during sleep, wake, configuration changes, etc.
728 LogInfo("LNT MakeTCPConnection: connection failed");
729 mDNSPlatformTCPCloseConnection(info
->sock
); // Dispose the socket we created with mDNSPlatformTCPSocket() above
730 info
->sock
= mDNSNULL
;
731 mDNSPlatformMemFree(info
->Reply
);
732 info
->Reply
= mDNSNULL
;
737 mDNSlocal
unsigned int AddSOAPArguments(char *buf
, unsigned int maxlen
, int numArgs
, Property
*a
)
739 static const char f1
[] = "<%s>%s</%s>";
740 static const char f2
[] = "<%s xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"%s\">%s</%s>";
743 for (i
= 0; i
< numArgs
; i
++)
745 if (a
[i
].type
) len
+= mDNS_snprintf(buf
+ len
, maxlen
- len
, f2
, a
[i
].name
, a
[i
].type
, a
[i
].value
, a
[i
].name
);
746 else len
+= mDNS_snprintf(buf
+ len
, maxlen
- len
, f1
, a
[i
].name
, a
[i
].value
, a
[i
].name
);
751 mDNSlocal mStatus
SendSOAPMsgControlAction(mDNS
*m
, tcpLNTInfo
*info
, char *Action
, int numArgs
, Property
*Arguments
, LNTOp_t op
)
753 // SOAP message header format -
756 // - router's host/port ("host:port")
758 static const char header
[] =
759 "POST %s HTTP/1.1\r\n"
760 "Content-Type: text/xml; charset=\"utf-8\"\r\n"
761 "SOAPAction: \"urn:schemas-upnp-org:service:WAN%sConnection:1#%s\"\r\n"
762 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n"
764 "Content-Length: %d\r\n"
765 "Connection: close\r\n"
766 "Pragma: no-cache\r\n"
770 static const char body1
[] =
771 "<?xml version=\"1.0\"?>\r\n"
773 " xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\""
774 " SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
776 "<m:%s xmlns:m=\"urn:schemas-upnp-org:service:WAN%sConnection:1\">";
778 static const char body2
[] =
781 "</SOAP-ENV:Envelope>\r\n";
784 char *body
= (char *)&m
->omsg
; // Typically requires 1110-1122 bytes; m->omsg is 8952 bytes, which is plenty
787 if (mDNSIPPortIsZero(m
->UPnPSOAPPort
) || m
->UPnPSOAPURL
== mDNSNULL
|| m
->UPnPSOAPAddressString
== mDNSNULL
) // if no SOAP URL or address exists get out here
788 { LogInfo("SendSOAPMsgControlAction: no SOAP port, URL or address string"); return mStatus_Invalid
; }
791 bodyLen
= mDNS_snprintf (body
, sizeof(m
->omsg
), body1
, Action
, m
->UPnPWANPPPConnection
? "PPP" : "IP");
792 bodyLen
+= AddSOAPArguments(body
+ bodyLen
, sizeof(m
->omsg
) - bodyLen
, numArgs
, Arguments
);
793 bodyLen
+= mDNS_snprintf (body
+ bodyLen
, sizeof(m
->omsg
) - bodyLen
, body2
, Action
);
795 // Create info->Request; the header needs to contain the bodyLen in the "Content-Length" field
796 if (!info
->Request
) info
->Request
= mDNSPlatformMemAllocate(LNT_MAXBUFSIZE
);
797 if (!info
->Request
) { LogMsg("SendSOAPMsgControlAction: Can't allocate info->Request"); return mStatus_NoMemoryErr
; }
798 info
->requestLen
= mDNS_snprintf((char *)info
->Request
, LNT_MAXBUFSIZE
, header
, m
->UPnPSOAPURL
, m
->UPnPWANPPPConnection
? "PPP" : "IP", Action
, m
->UPnPSOAPAddressString
, bodyLen
, body
);
800 err
= MakeTCPConnection(m
, info
, &m
->Router
, m
->UPnPSOAPPort
, op
);
801 if (err
) { mDNSPlatformMemFree(info
->Request
); info
->Request
= mDNSNULL
; }
805 // Build port mapping request with new port (up to max) and send it
806 mDNSlocal mStatus
SendPortMapRequest(mDNS
*m
, NATTraversalInfo
*n
)
808 char externalPort
[6];
809 char internalPort
[6];
810 char localIPAddrString
[30];
811 char publicPortString
[40];
812 Property propArgs
[8];
813 mDNSu16 ReqPortNum
= RequestedPortNum(n
);
814 NATTraversalInfo
*n2
= m
->NATTraversals
;
816 // Scan our m->NATTraversals list to make sure the external port we're requesting is locally unique.
817 // UPnP gateways will report conflicts if different devices request the same external port, but if two
818 // clients on the same device request the same external port the second one just stomps over the first.
819 // One way this can happen is like this:
820 // 1. Client A binds local port 80
821 // 2. Client A requests external port 80 -> internal port 80
822 // 3. UPnP NAT gateway refuses external port 80 (some other client already has it)
823 // 4. Client A tries again, and successfully gets external port 80 -> internal port 81
824 // 5. Client B on same machine tries to bind local port 80, and fails
825 // 6. Client B tries again, and successfully binds local port 81
826 // 7. Client B now requests external port 81 -> internal port 81
827 // 8. UPnP NAT gateway allows this, stomping over Client A's existing mapping
831 if (n2
== n
|| RequestedPortNum(n2
) != ReqPortNum
) n2
=n2
->next
;
834 if (n
->tcpInfo
.retries
< 100)
836 n
->tcpInfo
.retries
++;
837 ReqPortNum
= RequestedPortNum(n
); // Pick a new port number
838 n2
= m
->NATTraversals
; // And re-scan the list looking for conflicts
842 natTraversalHandlePortMapReply(m
, n
, m
->UPnPInterfaceID
, NATErr_Res
, zeroIPPort
, 0);
843 return mStatus_NoError
;
848 // create strings to use in the message
849 mDNS_snprintf(externalPort
, sizeof(externalPort
), "%u", ReqPortNum
);
850 mDNS_snprintf(internalPort
, sizeof(internalPort
), "%u", mDNSVal16(n
->IntPort
));
851 mDNS_snprintf(publicPortString
, sizeof(publicPortString
), "iC%u", ReqPortNum
);
852 mDNS_snprintf(localIPAddrString
, sizeof(localIPAddrString
), "%u.%u.%u.%u",
853 m
->AdvertisedV4
.ip
.v4
.b
[0], m
->AdvertisedV4
.ip
.v4
.b
[1], m
->AdvertisedV4
.ip
.v4
.b
[2], m
->AdvertisedV4
.ip
.v4
.b
[3]);
856 mDNSPlatformMemZero(propArgs
, sizeof(propArgs
));
857 propArgs
[0].name
= "NewRemoteHost";
858 propArgs
[0].type
= "string";
859 propArgs
[0].value
= "";
860 propArgs
[1].name
= "NewExternalPort";
861 propArgs
[1].type
= "ui2";
862 propArgs
[1].value
= externalPort
;
863 propArgs
[2].name
= "NewProtocol";
864 propArgs
[2].type
= "string";
865 propArgs
[2].value
= (n
->Protocol
== NATOp_MapUDP
) ? "UDP" : "TCP";
866 propArgs
[3].name
= "NewInternalPort";
867 propArgs
[3].type
= "ui2";
868 propArgs
[3].value
= internalPort
;
869 propArgs
[4].name
= "NewInternalClient";
870 propArgs
[4].type
= "string";
871 propArgs
[4].value
= localIPAddrString
;
872 propArgs
[5].name
= "NewEnabled";
873 propArgs
[5].type
= "boolean";
874 propArgs
[5].value
= "1";
875 propArgs
[6].name
= "NewPortMappingDescription";
876 propArgs
[6].type
= "string";
877 propArgs
[6].value
= publicPortString
;
878 propArgs
[7].name
= "NewLeaseDuration";
879 propArgs
[7].type
= "ui4";
880 propArgs
[7].value
= "0";
882 LogInfo("SendPortMapRequest: internal %u external %u", mDNSVal16(n
->IntPort
), ReqPortNum
);
883 return SendSOAPMsgControlAction(m
, &n
->tcpInfo
, "AddPortMapping", 8, propArgs
, LNTPortMapOp
);
886 mDNSexport mStatus
LNT_MapPort(mDNS
*m
, NATTraversalInfo
*n
)
888 LogInfo("LNT_MapPort");
889 if (n
->tcpInfo
.sock
) return(mStatus_NoError
); // If we already have a connection up don't make another request for the same thing
890 n
->tcpInfo
.parentNATInfo
= n
;
891 n
->tcpInfo
.retries
= 0;
892 return SendPortMapRequest(m
, n
);
895 mDNSexport mStatus
LNT_UnmapPort(mDNS
*m
, NATTraversalInfo
*n
)
897 char externalPort
[10];
898 Property propArgs
[3];
900 tcpLNTInfo
**infoPtr
= &m
->tcpInfoUnmapList
;
903 // If no NAT gateway to talk to, no need to do all this work for nothing
904 if (mDNSIPPortIsZero(m
->UPnPSOAPPort
) || !m
->UPnPSOAPURL
|| !m
->UPnPSOAPAddressString
) return mStatus_NoError
;
906 mDNS_snprintf(externalPort
, sizeof(externalPort
), "%u", mDNSVal16(mDNSIPPortIsZero(n
->RequestedPort
) ? n
->IntPort
: n
->RequestedPort
));
908 mDNSPlatformMemZero(propArgs
, sizeof(propArgs
));
909 propArgs
[0].name
= "NewRemoteHost";
910 propArgs
[0].type
= "string";
911 propArgs
[0].value
= "";
912 propArgs
[1].name
= "NewExternalPort";
913 propArgs
[1].type
= "ui2";
914 propArgs
[1].value
= externalPort
;
915 propArgs
[2].name
= "NewProtocol";
916 propArgs
[2].type
= "string";
917 propArgs
[2].value
= (n
->Protocol
== NATOp_MapUDP
) ? "UDP" : "TCP";
919 n
->tcpInfo
.parentNATInfo
= n
;
921 // clean up previous port mapping requests and allocations
922 if (n
->tcpInfo
.sock
) LogInfo("LNT_UnmapPort: closing previous open connection");
923 if (n
->tcpInfo
.sock
) { mDNSPlatformTCPCloseConnection(n
->tcpInfo
.sock
); n
->tcpInfo
.sock
= mDNSNULL
; }
924 if (n
->tcpInfo
.Request
) { mDNSPlatformMemFree(n
->tcpInfo
.Request
); n
->tcpInfo
.Request
= mDNSNULL
; }
925 if (n
->tcpInfo
.Reply
) { mDNSPlatformMemFree(n
->tcpInfo
.Reply
); n
->tcpInfo
.Reply
= mDNSNULL
; }
927 // 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)
928 if ((info
= mDNSPlatformMemAllocate(sizeof(tcpLNTInfo
))) == mDNSNULL
)
929 { LogInfo("LNT_UnmapPort: can't allocate tcpInfo"); return(mStatus_NoMemoryErr
); }
932 while (*infoPtr
) infoPtr
= &(*infoPtr
)->next
; // find the end of the list
933 *infoPtr
= info
; // append
935 err
= SendSOAPMsgControlAction(m
, info
, "DeletePortMapping", 3, propArgs
, LNTPortMapDeleteOp
);
936 if (err
) DisposeInfoFromUnmapList(m
, info
);
940 mDNSexport mStatus
LNT_GetExternalAddress(mDNS
*m
)
942 return SendSOAPMsgControlAction(m
, &m
->tcpAddrInfo
, "GetExternalIPAddress", 0, mDNSNULL
, LNTExternalAddrOp
);
945 mDNSlocal mStatus
GetDeviceDescription(mDNS
*m
, tcpLNTInfo
*info
)
947 // Device description format -
948 // - device description URL
950 static const char szSSDPMsgDescribeDeviceFMT
[] =
951 "GET %s HTTP/1.1\r\n"
952 "Accept: text/xml, application/xml\r\n"
953 "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
955 "Connection: close\r\n"
958 if (!mDNSIPPortIsZero(m
->UPnPSOAPPort
)) return mStatus_NoError
; // already have the info we need
960 if (m
->UPnPRouterURL
== mDNSNULL
|| m
->UPnPRouterAddressString
== mDNSNULL
) { LogInfo("GetDeviceDescription: no router URL or address string!"); return (mStatus_Invalid
); }
963 if (info
->Request
!= mDNSNULL
) mDNSPlatformMemZero(info
->Request
, LNT_MAXBUFSIZE
); // reuse previously allocated buffer
964 else if ((info
->Request
= (mDNSs8
*) mDNSPlatformMemAllocate(LNT_MAXBUFSIZE
)) == mDNSNULL
) { LogInfo("can't allocate send buffer for discovery"); return (mStatus_NoMemoryErr
); }
965 info
->requestLen
= mDNS_snprintf((char *)info
->Request
, LNT_MAXBUFSIZE
, szSSDPMsgDescribeDeviceFMT
, m
->UPnPRouterURL
, m
->UPnPRouterAddressString
);
966 LogInfo("Describe Device: [%s]", info
->Request
);
967 return MakeTCPConnection(m
, info
, &m
->Router
, m
->UPnPRouterPort
, LNTDiscoveryOp
);
970 // This function parses the response to our SSDP discovery message. Basically, we look to make sure this is a response
971 // referencing a service we care about (WANIPConnection or WANPPPConnection), then look for the "Location:" header and copy the addressing and
973 mDNSexport
void LNT_ConfigureRouterInfo(mDNS
*m
, const mDNSInterfaceID InterfaceID
, mDNSu8
*data
, mDNSu16 len
)
975 char *ptr
= (char *)data
;
976 char *end
= (char *)data
+ len
;
979 if (!mDNSIPPortIsZero(m
->UPnPRouterPort
)) return; // already have the info we need
981 // The formatting of the HTTP header is not always the same when it comes to the placement of
982 // the service and location strings, so we just look for each of them from the beginning for every response
984 // figure out if this is a message from a service we care about
985 while (ptr
&& ptr
!= end
)
987 if (*ptr
== 'W' && (strncasecmp(ptr
, "WANIPConnection:1", 17) == 0)) break;
993 while (ptr
&& ptr
!= end
)
995 if (*ptr
== 'W' && (strncasecmp(ptr
, "WANPPPConnection:1", 18) == 0)) break;
999 if (ptr
== mDNSNULL
|| ptr
== end
) return; // not a message we care about
1001 // find "Location:", starting from the beginning
1003 while (ptr
&& ptr
!= end
)
1005 if (*ptr
== 'L' && (strncasecmp(ptr
, "Location:", 9) == 0)) break; // find the first 'L'; is this Location? if not, keep looking
1008 if (ptr
== mDNSNULL
|| ptr
== end
)
1010 mDNSASLLog((uuid_t
*)&m
->asl_uuid
, "natt.legacy.ssdp", "failure", "Location", "");
1011 return; // not a message we care about
1013 ptr
+= 9; //Skip over 'Location:'
1014 while (*ptr
== ' ' && ptr
< end
) ptr
++; // skip over spaces
1015 if (ptr
>= end
) return;
1017 // find the end of the line
1018 for (stop
= ptr
; stop
!= end
; stop
++) { if (*stop
== '\r') { end
= stop
; break; } }
1020 // fill in default port
1021 m
->UPnPRouterPort
= mDNSOpaque16fromIntVal(80);
1023 // free string pointers and set to NULL
1024 if (m
->UPnPRouterAddressString
!= mDNSNULL
)
1026 mDNSPlatformMemFree(m
->UPnPRouterAddressString
);
1027 m
->UPnPRouterAddressString
= mDNSNULL
;
1029 if (m
->UPnPRouterURL
!= mDNSNULL
)
1031 mDNSPlatformMemFree(m
->UPnPRouterURL
);
1032 m
->UPnPRouterURL
= mDNSNULL
;
1035 // the Router URL should look something like "/dyndev/uuid:0013-108c-4b3f0000f3dc"
1036 if (ParseHttpUrl(ptr
, end
, &m
->UPnPRouterAddressString
, &m
->UPnPRouterPort
, &m
->UPnPRouterURL
) != mStatus_NoError
)
1038 mDNSASLLog((uuid_t
*)&m
->asl_uuid
, "natt.legacy.ssdp", "failure", "Parse URL", "");
1042 m
->UPnPInterfaceID
= InterfaceID
;
1044 if (m
->UPnPRouterAddressString
== mDNSNULL
)
1046 mDNSASLLog((uuid_t
*)&m
->asl_uuid
, "natt.legacy.ssdp", "failure", "Router address", "");
1047 LogMsg("LNT_ConfigureRouterInfo: UPnPRouterAddressString is NULL");
1049 else LogInfo("LNT_ConfigureRouterInfo: Router address string [%s]", m
->UPnPRouterAddressString
);
1051 if (m
->UPnPRouterURL
== mDNSNULL
)
1053 mDNSASLLog((uuid_t
*)&m
->asl_uuid
, "natt.legacy.ssdp", "failure", "Router path", "");
1054 LogMsg("LNT_ConfigureRouterInfo: UPnPRouterURL is NULL");
1056 else LogInfo("LNT_ConfigureRouterInfo: Router URL [%s]", m
->UPnPRouterURL
);
1058 LogInfo("LNT_ConfigureRouterInfo: Router port %d", mDNSVal16(m
->UPnPRouterPort
));
1059 LogInfo("LNT_ConfigureRouterInfo: Router interface %d", m
->UPnPInterfaceID
);
1061 // Don't need the SSDP socket anymore
1062 if (m
->SSDPSocket
) { debugf("LNT_ConfigureRouterInfo destroying SSDPSocket %p", &m
->SSDPSocket
); mDNSPlatformUDPClose(m
->SSDPSocket
); m
->SSDPSocket
= mDNSNULL
; }
1064 mDNSASLLog((uuid_t
*)&m
->asl_uuid
, "natt.legacy.ssdp", "success", "success", "");
1065 // now send message to get the device description
1066 GetDeviceDescription(m
, &m
->tcpDeviceInfo
);
1069 mDNSexport
void LNT_SendDiscoveryMsg(mDNS
*m
)
1071 static const char msg
[] =
1072 "M-SEARCH * HTTP/1.1\r\n"
1073 "Host:239.255.255.250:1900\r\n"
1074 "ST:urn:schemas-upnp-org:service:WAN%sConnection:1\r\n"
1075 "Man:\"ssdp:discover\"\r\n"
1077 static const mDNSAddr multicastDest
= { mDNSAddrType_IPv4
, { { { 239, 255, 255, 250 } } } };
1079 mDNSu8
* buf
= (mDNSu8
*)&m
->omsg
; //m->omsg is 8952 bytes, which is plenty
1080 unsigned int bufLen
;
1082 if (!mDNSIPPortIsZero(m
->UPnPRouterPort
))
1084 if (m
->SSDPSocket
) { debugf("LNT_SendDiscoveryMsg destroying SSDPSocket %p", &m
->SSDPSocket
); mDNSPlatformUDPClose(m
->SSDPSocket
); m
->SSDPSocket
= mDNSNULL
; }
1085 if (mDNSIPPortIsZero(m
->UPnPSOAPPort
) && !m
->tcpDeviceInfo
.sock
) GetDeviceDescription(m
, &m
->tcpDeviceInfo
);
1089 // Always query for WANIPConnection in the first SSDP packet
1090 if (m
->retryIntervalGetAddr
<= NATMAP_INIT_RETRY
) m
->SSDPWANPPPConnection
= mDNSfalse
;
1093 bufLen
= mDNS_snprintf((char*)buf
, sizeof(m
->omsg
), msg
, m
->SSDPWANPPPConnection
? "PPP" : "IP");
1095 debugf("LNT_SendDiscoveryMsg Router %.4a Current External Address %.4a", &m
->Router
.ip
.v4
, &m
->ExternalAddress
);
1097 if (!mDNSIPv4AddressIsZero(m
->Router
.ip
.v4
))
1099 if (!m
->SSDPSocket
) { m
->SSDPSocket
= mDNSPlatformUDPSocket(m
, zeroIPPort
); debugf("LNT_SendDiscoveryMsg created SSDPSocket %p", &m
->SSDPSocket
); }
1100 mDNSPlatformSendUDP(m
, buf
, buf
+ bufLen
, 0, m
->SSDPSocket
, &m
->Router
, SSDPPort
);
1101 mDNSPlatformSendUDP(m
, buf
, buf
+ bufLen
, 0, m
->SSDPSocket
, &multicastDest
, SSDPPort
);
1104 m
->SSDPWANPPPConnection
= !m
->SSDPWANPPPConnection
;
1107 mDNSexport
void LNT_ClearState(mDNS
*const m
)
1109 if (m
->tcpAddrInfo
.sock
) { mDNSPlatformTCPCloseConnection(m
->tcpAddrInfo
.sock
); m
->tcpAddrInfo
.sock
= mDNSNULL
; }
1110 if (m
->tcpDeviceInfo
.sock
) { mDNSPlatformTCPCloseConnection(m
->tcpDeviceInfo
.sock
); m
->tcpDeviceInfo
.sock
= mDNSNULL
; }
1111 m
->UPnPSOAPPort
= m
->UPnPRouterPort
= zeroIPPort
; // Reset UPnP ports
1114 #endif /* _LEGACY_NAT_TRAVERSAL_ */