LegacyNATTraversal.c [plain text]
#ifdef _LEGACY_NAT_TRAVERSAL_
#include "stdlib.h" // For strtol()
#include "string.h" // For strlcpy(), For strncpy(), strncasecmp()
#if defined( WIN32 )
# include <winsock2.h>
# include <ws2tcpip.h>
# define strcasecmp _stricmp
# define strncasecmp _strnicmp
# define mDNSASLLog( UUID, SUBDOMAIN, RESULT, SIGNATURE, FORMAT, ... ) ;
static int
inet_pton( int family, const char * addr, void * dst )
{
struct sockaddr_storage ss;
int sslen = sizeof( ss );
ZeroMemory( &ss, sizeof( ss ) );
ss.ss_family = family;
if ( WSAStringToAddressA( addr, family, NULL, ( struct sockaddr* ) &ss, &sslen ) == 0 )
{
if ( family == AF_INET ) { memcpy( dst, &( ( struct sockaddr_in* ) &ss)->sin_addr, sizeof( IN_ADDR ) ); return 1; }
else if ( family == AF_INET6 ) { memcpy( dst, &( ( struct sockaddr_in6* ) &ss)->sin6_addr, sizeof( IN6_ADDR ) ); return 1; }
else return 0;
}
else return 0;
}
#else
# include <arpa/inet.h> // For inet_pton()
#endif
#include "mDNSEmbeddedAPI.h"
#include "uDNS.h" // For natTraversalHandleAddressReply() etc.
typedef struct Property_struct
{
char *name;
char *type;
char *value;
} Property;
mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n);
#define RequestedPortNum(n) (mDNSVal16(mDNSIPPortIsZero((n)->RequestedPort) ? (n)->IntPort : (n)->RequestedPort) + (n)->tcpInfo.retries)
mDNSlocal void AllocAndCopy(mDNSu8** dst, mDNSu8* src)
{
if (src == mDNSNULL) return;
if ((*dst = (mDNSu8 *) mDNSPlatformMemAllocate(strlen((char*)src) + 1)) == mDNSNULL) { LogMsg("AllocAndCopy: can't allocate string"); return; }
strcpy((char *)*dst, (char*)src);
}
mDNSlocal mStatus ParseHttpUrl(char* ptr, char* end, mDNSu8** addressAndPort, mDNSIPPort* port, mDNSu8** path)
{
if (end - ptr >= 7 && strncasecmp(ptr, "http://", 7) == 0)
{
int i;
char* stop = end;
char* addrPtr = mDNSNULL;
ptr += 7; if (ptr >= end) { LogInfo("ParseHttpUrl: past end of buffer parsing host:port"); return mStatus_BadParamErr; }
addrPtr = ptr;
for (i = 0; addrPtr && addrPtr != end; i++, addrPtr++) if (*addrPtr == '/') break;
if ((*addressAndPort = (mDNSu8 *) mDNSPlatformMemAllocate(i+1)) == mDNSNULL) { LogMsg("ParseHttpUrl: can't allocate address string"); return mStatus_NoMemoryErr; }
strncpy((char *)*addressAndPort, ptr, i);
(*addressAndPort)[i] = '\0';
stop = ptr; ptr = addrPtr;
for (addrPtr--;addrPtr>stop;addrPtr--)
{
if (*addrPtr == ':')
{
int tmpport;
addrPtr++; tmpport = (int)strtol(addrPtr, mDNSNULL, 10);
*port = mDNSOpaque16fromIntVal(tmpport); break;
}
}
}
if (path && ptr < end)
{
if ((*path = (mDNSu8 *)mDNSPlatformMemAllocate(end - ptr + 1)) == mDNSNULL) { LogMsg("ParseHttpUrl: can't mDNSPlatformMemAllocate path"); return mStatus_NoMemoryErr; }
strncpy((char *)*path, ptr, end - ptr);
(*path)[end - ptr] = '\0';
}
return mStatus_NoError;
}
enum
{
HTTPCode_NeedMoreData = -1, HTTPCode_Other = -2, HTTPCode_Bad = -3,
HTTPCode_200 = 200,
HTTPCode_404 = 404,
HTTPCode_500 = 500,
};
mDNSlocal mDNSs16 ParseHTTPResponseCode(mDNSu8** data, mDNSu8* end)
{
mDNSu8* ptr = *data;
char * code;
if (end - ptr < 5) return HTTPCode_NeedMoreData;
if (strncasecmp((char*)ptr, "HTTP/", 5) != 0) return HTTPCode_Bad;
ptr += 5;
while (ptr && ptr != end)
{
if (*ptr == '\n') return HTTPCode_Bad;
if (*ptr == ' ') break;
ptr++;
}
if (ptr == end) return HTTPCode_NeedMoreData;
ptr++;
if (end - ptr < 3) return HTTPCode_NeedMoreData;
code = (char*)ptr;
ptr += 3;
while (ptr && ptr != end)
{
if (*ptr == '\n') break;
ptr++;
}
if (ptr == end) return HTTPCode_NeedMoreData;
*data = ++ptr;
if (memcmp(code, "200", 3) == 0) return HTTPCode_200;
if (memcmp(code, "404", 3) == 0) return HTTPCode_404;
if (memcmp(code, "500", 3) == 0) return HTTPCode_500;
LogInfo("ParseHTTPResponseCode found unexpected result code: %c%c%c", code[0], code[1], code[2]);
return HTTPCode_Other;
}
mDNSlocal void handleLNTDeviceDescriptionResponse(tcpLNTInfo *tcpInfo)
{
mDNS *m = tcpInfo->m;
char *ptr = (char *)tcpInfo->Reply;
char *end = (char *)tcpInfo->Reply + tcpInfo->nread;
char *stop = mDNSNULL;
mDNSs16 http_result;
if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return;
http_result = ParseHTTPResponseCode((mDNSu8**)&ptr, (mDNSu8*)end); if (http_result == HTTPCode_404) LNT_ClearState(m);
if (http_result != HTTPCode_200)
{
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "noop", "HTTP Result", "HTTP code: %d", http_result);
return;
}
m->UPnPWANPPPConnection = mDNSfalse;
while (ptr && ptr < end)
{
if (*ptr == 'W' && (strncasecmp(ptr, "WANIPConnection:1", 17) == 0)) break;
ptr++;
}
if (ptr == end)
{
ptr = (char *)tcpInfo->Reply;
while (ptr && ptr < end)
{
if (*ptr == 'W' && (strncasecmp(ptr, "WANPPPConnection:1", 18) == 0))
{
m->UPnPWANPPPConnection = mDNStrue;
break;
}
ptr++;
}
}
if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find WANIPConnection:1 or WANPPPConnection:1 string"); return; }
while (ptr && ptr < end)
{
if (*ptr == 'c' && (strncasecmp(ptr, "controlURL", 10) == 0)) break; ptr++;
}
if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find controlURL string"); return; }
ptr += 11; if (ptr >= end) { LogInfo("handleLNTDeviceDescriptionResponse: past end of buffer and no body!"); return; }
for (stop = ptr; stop < end; stop++) { if (*stop == '<') { end = stop; break; } }
m->UPnPSOAPPort = m->UPnPRouterPort;
if (m->UPnPSOAPAddressString != mDNSNULL)
{
mDNSPlatformMemFree(m->UPnPSOAPAddressString);
m->UPnPSOAPAddressString = mDNSNULL;
}
if (m->UPnPSOAPURL != mDNSNULL)
{
mDNSPlatformMemFree(m->UPnPSOAPURL);
m->UPnPSOAPURL = mDNSNULL;
}
if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, &m->UPnPSOAPURL) != mStatus_NoError) return;
if (m->UPnPSOAPAddressString == mDNSNULL)
{
ptr = (char *)tcpInfo->Reply;
while (ptr && ptr < end)
{
if (*ptr == 'U' && (strncasecmp(ptr, "URLBase", 7) == 0)) break;
ptr++;
}
if (ptr < end) {
LogInfo("handleLNTDeviceDescriptionResponse: found URLBase");
ptr += 8; for (stop = ptr; stop < end; stop++) { if (*stop == '<') { end = stop; break; } }
if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, mDNSNULL) != mStatus_NoError)
{
LogInfo("handleLNTDeviceDescriptionResponse: failed to parse URLBase");
}
}
if (m->UPnPSOAPAddressString == mDNSNULL) AllocAndCopy(&m->UPnPSOAPAddressString, m->UPnPRouterAddressString);
}
if (m->UPnPSOAPAddressString == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPAddressString is NULL");
else LogInfo("handleLNTDeviceDescriptionResponse: SOAP address string [%s]", m->UPnPSOAPAddressString);
if (m->UPnPSOAPURL == mDNSNULL) AllocAndCopy(&m->UPnPSOAPURL, m->UPnPRouterURL);
if (m->UPnPSOAPURL == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPURL is NULL");
else LogInfo("handleLNTDeviceDescriptionResponse: SOAP URL [%s]", m->UPnPSOAPURL);
}
mDNSlocal void handleLNTGetExternalAddressResponse(tcpLNTInfo *tcpInfo)
{
mDNS *m = tcpInfo->m;
mDNSu16 err = NATErr_None;
mDNSv4Addr ExtAddr;
mDNSu8 *ptr = (mDNSu8*)tcpInfo->Reply;
mDNSu8 *end = (mDNSu8*)tcpInfo->Reply + tcpInfo->nread;
mDNSu8 *addrend;
static char tagname[20] = "NewExternalIPAddress";
mDNSs16 http_result = ParseHTTPResponseCode(&ptr, end); if (http_result == HTTPCode_404) LNT_ClearState(m);
if (http_result != HTTPCode_200)
{
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "HTTP Result", "HTTP code: %d", http_result);
return;
}
while (ptr < end && strncasecmp((char*)ptr, tagname, sizeof(tagname))) ptr++;
ptr += sizeof(tagname); while (ptr < end && *ptr != '>') ptr++;
ptr += 1; addrend = ptr;
while (addrend < end && (mDNSIsDigit(*addrend) || *addrend == '.')) addrend++;
if (addrend >= end) return;
*addrend = 0;
if (inet_pton(AF_INET, (char*)ptr, &ExtAddr) <= 0)
{
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "inet_pton", "");
LogMsg("handleLNTGetExternalAddressResponse: Router returned bad address %s", ptr);
err = NATErr_NetFail;
ExtAddr = zerov4Addr;
}
if (!err) LogInfo("handleLNTGetExternalAddressResponse: External IP address is %.4a", &ExtAddr);
natTraversalHandleAddressReply(m, err, ExtAddr);
}
mDNSlocal void handleLNTPortMappingResponse(tcpLNTInfo *tcpInfo)
{
mDNS *m = tcpInfo->m;
mDNSIPPort extport = zeroIPPort;
mDNSu8 *ptr = (mDNSu8*)tcpInfo->Reply;
mDNSu8 *end = (mDNSu8*)tcpInfo->Reply + tcpInfo->nread;
NATTraversalInfo *natInfo;
mDNSs16 http_result;
for (natInfo = m->NATTraversals; natInfo; natInfo=natInfo->next) { if (natInfo == tcpInfo->parentNATInfo) break; }
if (!natInfo) { LogInfo("handleLNTPortMappingResponse: can't find matching tcpInfo in NATTraversals!"); return; }
http_result = ParseHTTPResponseCode(&ptr, end); if (http_result == HTTPCode_200)
{
LogInfo("handleLNTPortMappingResponse: got a valid response, sending reply to natTraversalHandlePortMapReply(internal %d external %d retries %d)",
mDNSVal16(natInfo->IntPort), RequestedPortNum(natInfo), tcpInfo->retries);
extport = mDNSOpaque16fromIntVal(RequestedPortNum(natInfo));
tcpInfo->retries = 0;
natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, mStatus_NoError, extport, NATMAP_DEFAULT_LEASE);
}
else if (http_result == HTTPCode_500)
{
while (ptr && ptr != end)
{
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))
{
if (tcpInfo->retries < 100)
{
tcpInfo->retries++; SendPortMapRequest(tcpInfo->m, natInfo);
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict", "Retry %d", tcpInfo->retries);
}
else
{
LogMsg("handleLNTPortMappingResponse too many conflict retries %d %d", mDNSVal16(natInfo->IntPort), mDNSVal16(natInfo->RequestedPort));
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict - too many retries", "Retries: %d", tcpInfo->retries);
natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0);
}
return;
}
ptr++;
}
}
else if (http_result == HTTPCode_Bad) LogMsg("handleLNTPortMappingResponse got data that was not a valid HTTP response");
else if (http_result == HTTPCode_Other) LogMsg("handleLNTPortMappingResponse got unexpected response code");
else if (http_result == HTTPCode_404) LNT_ClearState(m);
if (http_result != HTTPCode_200 && http_result != HTTPCode_500)
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "HTTP Result", "HTTP code: %d", http_result);
}
mDNSlocal void DisposeInfoFromUnmapList(mDNS *m, tcpLNTInfo *tcpInfo)
{
tcpLNTInfo **ptr = &m->tcpInfoUnmapList;
while (*ptr && *ptr != tcpInfo) ptr = &(*ptr)->next;
if (*ptr) { *ptr = (*ptr)->next; mDNSPlatformMemFree(tcpInfo); } }
mDNSlocal void tcpConnectionCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEstablished, mStatus err)
{
mStatus status = mStatus_NoError;
tcpLNTInfo *tcpInfo = (tcpLNTInfo *)context;
mDNSBool closed = mDNSfalse;
long n = 0;
long nsent = 0;
if (tcpInfo == mDNSNULL) { LogInfo("tcpConnectionCallback: no tcpInfo context"); status = mStatus_Invalid; goto exit; }
mDNS_Lock(tcpInfo->m);
if (err) { LogInfo("tcpConnectionCallback: received error"); goto exit; }
if (ConnectionEstablished) {
LogInfo("tcpConnectionCallback: connection established, sending message");
nsent = mDNSPlatformWriteTCP(sock, (char *)tcpInfo->Request, tcpInfo->requestLen);
if (nsent != (long)tcpInfo->requestLen) { LogMsg("tcpConnectionCallback: error writing"); status = mStatus_UnknownErr; goto exit; }
}
else
{
n = mDNSPlatformReadTCP(sock, (char *)tcpInfo->Reply + tcpInfo->nread, tcpInfo->replyLen - tcpInfo->nread, &closed);
LogInfo("tcpConnectionCallback: mDNSPlatformReadTCP read %d bytes", n);
if (n < 0) { LogInfo("tcpConnectionCallback - read returned %d", n); status = mStatus_ConnFailed; goto exit; }
else if (closed) { LogInfo("tcpConnectionCallback: socket closed by remote end %d", tcpInfo->nread); status = mStatus_ConnFailed; goto exit; }
tcpInfo->nread += n;
LogInfo("tcpConnectionCallback tcpInfo->nread %d", tcpInfo->nread);
if (tcpInfo->nread > LNT_MAXBUFSIZE)
{
LogInfo("result truncated...");
tcpInfo->nread = LNT_MAXBUFSIZE;
}
switch (tcpInfo->op)
{
case LNTDiscoveryOp: handleLNTDeviceDescriptionResponse (tcpInfo); break;
case LNTExternalAddrOp: handleLNTGetExternalAddressResponse(tcpInfo); break;
case LNTPortMapOp: handleLNTPortMappingResponse (tcpInfo); break;
case LNTPortMapDeleteOp: status = mStatus_ConfigChanged; break;
default: LogMsg("tcpConnectionCallback: bad tcp operation! %d", tcpInfo->op); status = mStatus_Invalid; break;
}
}
exit:
if (err || status)
{
mDNS *m = tcpInfo->m;
switch (tcpInfo->op)
{
case LNTDiscoveryOp: if (m->UPnPSOAPAddressString == mDNSNULL)
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP Address", "");
if (m->UPnPSOAPURL == mDNSNULL)
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP path", "");
if (m->UPnPSOAPAddressString && m->UPnPSOAPURL)
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "success", "success", "");
break;
case LNTExternalAddrOp: mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success", mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success", "");
break;
case LNTPortMapOp: if (tcpInfo->parentNATInfo)
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", (tcpInfo->parentNATInfo->Result) ? "failure" : "success",
(tcpInfo->parentNATInfo->Result) ? "failure" : "success", "Result: %d", tcpInfo->parentNATInfo->Result);
break;
case LNTPortMapDeleteOp: break;
default: break;
}
mDNSPlatformTCPCloseConnection(tcpInfo->sock);
tcpInfo->sock = mDNSNULL;
if (tcpInfo->Request) { mDNSPlatformMemFree(tcpInfo->Request); tcpInfo->Request = mDNSNULL; }
if (tcpInfo->Reply ) { mDNSPlatformMemFree(tcpInfo->Reply); tcpInfo->Reply = mDNSNULL; }
}
if (tcpInfo) mDNS_Unlock(tcpInfo->m);
if (status == mStatus_ConfigChanged) DisposeInfoFromUnmapList(tcpInfo->m, tcpInfo);
}
mDNSlocal mStatus MakeTCPConnection(mDNS *const m, tcpLNTInfo *info, const mDNSAddr *const Addr, const mDNSIPPort Port, LNTOp_t op)
{
mStatus err = mStatus_NoError;
mDNSIPPort srcport = zeroIPPort;
if (mDNSIPv4AddressIsZero(Addr->ip.v4) || mDNSIPPortIsZero(Port))
{ LogMsg("LNT MakeTCPConnection: bad address/port %#a:%d", Addr, mDNSVal16(Port)); return(mStatus_Invalid); }
info->m = m;
info->Address = *Addr;
info->Port = Port;
info->op = op;
info->nread = 0;
info->replyLen = LNT_MAXBUFSIZE;
if (info->Reply != mDNSNULL) mDNSPlatformMemZero(info->Reply, LNT_MAXBUFSIZE); else if ((info->Reply = (mDNSs8 *) mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate reply buffer"); return (mStatus_NoMemoryErr); }
if (info->sock) { LogInfo("MakeTCPConnection: closing previous open connection"); mDNSPlatformTCPCloseConnection(info->sock); info->sock = mDNSNULL; }
info->sock = mDNSPlatformTCPSocket(m, kTCPSocketFlags_Zero, &srcport);
if (!info->sock) { LogMsg("LNT MakeTCPConnection: unable to create TCP socket"); mDNSPlatformMemFree(info->Reply); info->Reply = mDNSNULL; return(mStatus_NoMemoryErr); }
LogInfo("MakeTCPConnection: connecting to %#a:%d", &info->Address, mDNSVal16(info->Port));
err = mDNSPlatformTCPConnect(info->sock, Addr, Port, 0, tcpConnectionCallback, info);
if (err == mStatus_ConnPending) err = mStatus_NoError;
else if (err == mStatus_ConnEstablished)
{
mDNS_DropLockBeforeCallback();
tcpConnectionCallback(info->sock, info, mDNStrue, mStatus_NoError);
mDNS_ReclaimLockAfterCallback();
err = mStatus_NoError;
}
else
{
LogInfo("LNT MakeTCPConnection: connection failed");
mDNSPlatformTCPCloseConnection(info->sock); info->sock = mDNSNULL;
mDNSPlatformMemFree(info->Reply);
info->Reply = mDNSNULL;
}
return(err);
}
mDNSlocal unsigned int AddSOAPArguments(char *buf, unsigned int maxlen, int numArgs, Property *a)
{
static const char f1[] = "<%s>%s</%s>";
static const char f2[] = "<%s xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"%s\">%s</%s>";
int i, len = 0;
*buf = 0;
for (i = 0; i < numArgs; i++)
{
if (a[i].type) len += mDNS_snprintf(buf + len, maxlen - len, f2, a[i].name, a[i].type, a[i].value, a[i].name);
else len += mDNS_snprintf(buf + len, maxlen - len, f1, a[i].name, a[i].value, a[i].name);
}
return(len);
}
mDNSlocal mStatus SendSOAPMsgControlAction(mDNS *m, tcpLNTInfo *info, char *Action, int numArgs, Property *Arguments, LNTOp_t op)
{
static const char header[] =
"POST %s HTTP/1.1\r\n"
"Content-Type: text/xml; charset=\"utf-8\"\r\n"
"SOAPAction: \"urn:schemas-upnp-org:service:WAN%sConnection:1#%s\"\r\n"
"User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n"
"Host: %s\r\n"
"Content-Length: %d\r\n"
"Connection: close\r\n"
"Pragma: no-cache\r\n"
"\r\n"
"%s\r\n";
static const char body1[] =
"<?xml version=\"1.0\"?>\r\n"
"<SOAP-ENV:Envelope"
" xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\""
" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
"<SOAP-ENV:Body>"
"<m:%s xmlns:m=\"urn:schemas-upnp-org:service:WAN%sConnection:1\">";
static const char body2[] =
"</m:%s>"
"</SOAP-ENV:Body>"
"</SOAP-ENV:Envelope>\r\n";
mStatus err;
char *body = (char *)&m->omsg; int bodyLen;
if (mDNSIPPortIsZero(m->UPnPSOAPPort) || m->UPnPSOAPURL == mDNSNULL || m->UPnPSOAPAddressString == mDNSNULL) { LogInfo("SendSOAPMsgControlAction: no SOAP port, URL or address string"); return mStatus_Invalid; }
bodyLen = mDNS_snprintf (body, sizeof(m->omsg), body1, Action, m->UPnPWANPPPConnection ? "PPP" : "IP");
bodyLen += AddSOAPArguments(body + bodyLen, sizeof(m->omsg) - bodyLen, numArgs, Arguments);
bodyLen += mDNS_snprintf (body + bodyLen, sizeof(m->omsg) - bodyLen, body2, Action);
if (!info->Request) info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE);
if (!info->Request) { LogMsg("SendSOAPMsgControlAction: Can't allocate info->Request"); return mStatus_NoMemoryErr; }
info->requestLen = mDNS_snprintf((char *)info->Request, LNT_MAXBUFSIZE, header, m->UPnPSOAPURL, m->UPnPWANPPPConnection ? "PPP" : "IP", Action, m->UPnPSOAPAddressString, bodyLen, body);
err = MakeTCPConnection(m, info, &m->Router, m->UPnPSOAPPort, op);
if (err) { mDNSPlatformMemFree(info->Request); info->Request = mDNSNULL; }
return err;
}
mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n)
{
char externalPort[6];
char internalPort[6];
char localIPAddrString[30];
char publicPortString[40];
Property propArgs[8];
mDNSu16 ReqPortNum = RequestedPortNum(n);
NATTraversalInfo *n2 = m->NATTraversals;
while (n2)
{
if (n2 == n || RequestedPortNum(n2) != ReqPortNum) n2=n2->next;
else
{
if (n->tcpInfo.retries < 100)
{
n->tcpInfo.retries++;
ReqPortNum = RequestedPortNum(n); n2 = m->NATTraversals; }
else
{
natTraversalHandlePortMapReply(m, n, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0);
return mStatus_NoError;
}
}
}
mDNS_snprintf(externalPort, sizeof(externalPort), "%u", ReqPortNum);
mDNS_snprintf(internalPort, sizeof(internalPort), "%u", mDNSVal16(n->IntPort));
mDNS_snprintf(publicPortString, sizeof(publicPortString), "iC%u", ReqPortNum);
mDNS_snprintf(localIPAddrString, sizeof(localIPAddrString), "%u.%u.%u.%u",
m->AdvertisedV4.ip.v4.b[0], m->AdvertisedV4.ip.v4.b[1], m->AdvertisedV4.ip.v4.b[2], m->AdvertisedV4.ip.v4.b[3]);
mDNSPlatformMemZero(propArgs, sizeof(propArgs));
propArgs[0].name = "NewRemoteHost";
propArgs[0].type = "string";
propArgs[0].value = "";
propArgs[1].name = "NewExternalPort";
propArgs[1].type = "ui2";
propArgs[1].value = externalPort;
propArgs[2].name = "NewProtocol";
propArgs[2].type = "string";
propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP";
propArgs[3].name = "NewInternalPort";
propArgs[3].type = "ui2";
propArgs[3].value = internalPort;
propArgs[4].name = "NewInternalClient";
propArgs[4].type = "string";
propArgs[4].value = localIPAddrString;
propArgs[5].name = "NewEnabled";
propArgs[5].type = "boolean";
propArgs[5].value = "1";
propArgs[6].name = "NewPortMappingDescription";
propArgs[6].type = "string";
propArgs[6].value = publicPortString;
propArgs[7].name = "NewLeaseDuration";
propArgs[7].type = "ui4";
propArgs[7].value = "0";
LogInfo("SendPortMapRequest: internal %u external %u", mDNSVal16(n->IntPort), ReqPortNum);
return SendSOAPMsgControlAction(m, &n->tcpInfo, "AddPortMapping", 8, propArgs, LNTPortMapOp);
}
mDNSexport mStatus LNT_MapPort(mDNS *m, NATTraversalInfo *n)
{
LogInfo("LNT_MapPort");
if (n->tcpInfo.sock) return(mStatus_NoError); n->tcpInfo.parentNATInfo = n;
n->tcpInfo.retries = 0;
return SendPortMapRequest(m, n);
}
mDNSexport mStatus LNT_UnmapPort(mDNS *m, NATTraversalInfo *n)
{
char externalPort[10];
Property propArgs[3];
tcpLNTInfo *info;
tcpLNTInfo **infoPtr = &m->tcpInfoUnmapList;
mStatus err;
if (mDNSIPPortIsZero(m->UPnPSOAPPort) || !m->UPnPSOAPURL || !m->UPnPSOAPAddressString) return mStatus_NoError;
mDNS_snprintf(externalPort, sizeof(externalPort), "%u", mDNSVal16(mDNSIPPortIsZero(n->RequestedPort) ? n->IntPort : n->RequestedPort));
mDNSPlatformMemZero(propArgs, sizeof(propArgs));
propArgs[0].name = "NewRemoteHost";
propArgs[0].type = "string";
propArgs[0].value = "";
propArgs[1].name = "NewExternalPort";
propArgs[1].type = "ui2";
propArgs[1].value = externalPort;
propArgs[2].name = "NewProtocol";
propArgs[2].type = "string";
propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP";
n->tcpInfo.parentNATInfo = n;
if (n->tcpInfo.sock) LogInfo("LNT_UnmapPort: closing previous open connection");
if (n->tcpInfo.sock ) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; }
if (n->tcpInfo.Request) { mDNSPlatformMemFree(n->tcpInfo.Request); n->tcpInfo.Request = mDNSNULL; }
if (n->tcpInfo.Reply ) { mDNSPlatformMemFree(n->tcpInfo.Reply); n->tcpInfo.Reply = mDNSNULL; }
if ((info = mDNSPlatformMemAllocate(sizeof(tcpLNTInfo))) == mDNSNULL)
{ LogInfo("LNT_UnmapPort: can't allocate tcpInfo"); return(mStatus_NoMemoryErr); }
*info = n->tcpInfo;
while (*infoPtr) infoPtr = &(*infoPtr)->next; *infoPtr = info;
err = SendSOAPMsgControlAction(m, info, "DeletePortMapping", 3, propArgs, LNTPortMapDeleteOp);
if (err) DisposeInfoFromUnmapList(m, info);
return err;
}
mDNSexport mStatus LNT_GetExternalAddress(mDNS *m)
{
return SendSOAPMsgControlAction(m, &m->tcpAddrInfo, "GetExternalIPAddress", 0, mDNSNULL, LNTExternalAddrOp);
}
mDNSlocal mStatus GetDeviceDescription(mDNS *m, tcpLNTInfo *info)
{
static const char szSSDPMsgDescribeDeviceFMT[] =
"GET %s HTTP/1.1\r\n"
"Accept: text/xml, application/xml\r\n"
"User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
"Host: %s\r\n"
"Connection: close\r\n"
"\r\n";
if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return mStatus_NoError;
if (m->UPnPRouterURL == mDNSNULL || m->UPnPRouterAddressString == mDNSNULL) { LogInfo("GetDeviceDescription: no router URL or address string!"); return (mStatus_Invalid); }
if (info->Request != mDNSNULL) mDNSPlatformMemZero(info->Request, LNT_MAXBUFSIZE); else if ((info->Request = (mDNSs8 *) mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate send buffer for discovery"); return (mStatus_NoMemoryErr); }
info->requestLen = mDNS_snprintf((char *)info->Request, LNT_MAXBUFSIZE, szSSDPMsgDescribeDeviceFMT, m->UPnPRouterURL, m->UPnPRouterAddressString);
LogInfo("Describe Device: [%s]", info->Request);
return MakeTCPConnection(m, info, &m->Router, m->UPnPRouterPort, LNTDiscoveryOp);
}
mDNSexport void LNT_ConfigureRouterInfo(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *data, mDNSu16 len)
{
char *ptr = (char *)data;
char *end = (char *)data + len;
char *stop = ptr;
if (!mDNSIPPortIsZero(m->UPnPRouterPort)) return;
while (ptr && ptr != end)
{
if (*ptr == 'W' && (strncasecmp(ptr, "WANIPConnection:1", 17) == 0)) break;
ptr++;
}
if (ptr == end)
{
ptr = (char *)data;
while (ptr && ptr != end)
{
if (*ptr == 'W' && (strncasecmp(ptr, "WANPPPConnection:1", 18) == 0)) break;
ptr++;
}
}
if (ptr == mDNSNULL || ptr == end) return;
ptr = (char *)data;
while (ptr && ptr != end)
{
if (*ptr == 'L' && (strncasecmp(ptr, "Location:", 9) == 0)) break; ptr++;
}
if (ptr == mDNSNULL || ptr == end)
{
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Location", "");
return; }
ptr += 9; while (*ptr == ' ' && ptr < end) ptr++; if (ptr >= end) return;
for (stop = ptr; stop != end; stop++) { if (*stop == '\r') { end = stop; break; } }
m->UPnPRouterPort = mDNSOpaque16fromIntVal(80);
if (m->UPnPRouterAddressString != mDNSNULL)
{
mDNSPlatformMemFree(m->UPnPRouterAddressString);
m->UPnPRouterAddressString = mDNSNULL;
}
if (m->UPnPRouterURL != mDNSNULL)
{
mDNSPlatformMemFree(m->UPnPRouterURL);
m->UPnPRouterURL = mDNSNULL;
}
if (ParseHttpUrl(ptr, end, &m->UPnPRouterAddressString, &m->UPnPRouterPort, &m->UPnPRouterURL) != mStatus_NoError)
{
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Parse URL", "");
return;
}
m->UPnPInterfaceID = InterfaceID;
if (m->UPnPRouterAddressString == mDNSNULL)
{
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router address", "");
LogMsg("LNT_ConfigureRouterInfo: UPnPRouterAddressString is NULL");
}
else LogInfo("LNT_ConfigureRouterInfo: Router address string [%s]", m->UPnPRouterAddressString);
if (m->UPnPRouterURL == mDNSNULL)
{
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router path", "");
LogMsg("LNT_ConfigureRouterInfo: UPnPRouterURL is NULL");
}
else LogInfo("LNT_ConfigureRouterInfo: Router URL [%s]", m->UPnPRouterURL);
LogInfo("LNT_ConfigureRouterInfo: Router port %d", mDNSVal16(m->UPnPRouterPort));
LogInfo("LNT_ConfigureRouterInfo: Router interface %d", m->UPnPInterfaceID);
if (m->SSDPSocket) { debugf("LNT_ConfigureRouterInfo destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "success", "success", "");
GetDeviceDescription(m, &m->tcpDeviceInfo);
}
mDNSexport void LNT_SendDiscoveryMsg(mDNS *m)
{
static const char msg[] =
"M-SEARCH * HTTP/1.1\r\n"
"Host:239.255.255.250:1900\r\n"
"ST:urn:schemas-upnp-org:service:WAN%sConnection:1\r\n"
"Man:\"ssdp:discover\"\r\n"
"MX:3\r\n\r\n";
static const mDNSAddr multicastDest = { mDNSAddrType_IPv4, { { { 239, 255, 255, 250 } } } };
mDNSu8* buf = (mDNSu8*)&m->omsg; unsigned int bufLen;
if (!mDNSIPPortIsZero(m->UPnPRouterPort))
{
if (m->SSDPSocket) { debugf("LNT_SendDiscoveryMsg destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
if (mDNSIPPortIsZero(m->UPnPSOAPPort) && !m->tcpDeviceInfo.sock) GetDeviceDescription(m, &m->tcpDeviceInfo);
return;
}
if (m->retryIntervalGetAddr <= NATMAP_INIT_RETRY) m->SSDPWANPPPConnection = mDNSfalse;
bufLen = mDNS_snprintf((char*)buf, sizeof(m->omsg), msg, m->SSDPWANPPPConnection ? "PPP" : "IP");
debugf("LNT_SendDiscoveryMsg Router %.4a Current External Address %.4a", &m->Router.ip.v4, &m->ExternalAddress);
if (!mDNSIPv4AddressIsZero(m->Router.ip.v4))
{
if (!m->SSDPSocket) { m->SSDPSocket = mDNSPlatformUDPSocket(m, zeroIPPort); debugf("LNT_SendDiscoveryMsg created SSDPSocket %p", &m->SSDPSocket); }
mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &m->Router, SSDPPort);
mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &multicastDest, SSDPPort);
}
m->SSDPWANPPPConnection = !m->SSDPWANPPPConnection;
}
mDNSexport void LNT_ClearState(mDNS *const m)
{
if (m->tcpAddrInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpAddrInfo.sock); m->tcpAddrInfo.sock = mDNSNULL; }
if (m->tcpDeviceInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpDeviceInfo.sock); m->tcpDeviceInfo.sock = mDNSNULL; }
m->UPnPSOAPPort = m->UPnPRouterPort = zeroIPPort; }
#endif