mDNSlocal mStatus SendSOAPMsgControlAction(mDNS *m, tcpLNTInfo *info, char *Action, int numArgs, Property *Arguments, LNTOp_t op) { // SOAP message header format - // - control URL // - action (string) // - router's host/port ("host:port") // - content-length 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; // Typically requires 1110-1122 bytes; m->omsg is 8952 bytes, which is plenty int bodyLen; if (mDNSIPPortIsZero(m->UPnPSOAPPort) || m->UPnPSOAPURL == mDNSNULL || m->UPnPSOAPAddressString == mDNSNULL) // if no SOAP URL or address exists get out here { LogInfo("SendSOAPMsgControlAction: no SOAP port, URL or address string"); return mStatus_Invalid; } // Create body 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); // Create info->Request; the header needs to contain the bodyLen in the "Content-Length" field 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 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 mDNS_RegisterProxyHost(mDNS *m, ProxyHost *p) { char buffer[32]; mDNS_SetupResourceRecord(&p->RR_A, mDNSNULL, mDNSInterface_Any, kDNSType_A, 60, kDNSRecordTypeUnique, AuthRecordAny, HostNameCallback, p); mDNS_SetupResourceRecord(&p->RR_PTR, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, AuthRecordAny, HostNameCallback, p); p->RR_A.namestorage.c[0] = 0; AppendDomainLabel(&p->RR_A.namestorage, &p->hostlabel); AppendLiteralLabelString(&p->RR_A.namestorage, "local"); // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p->ip.b[3], p->ip.b[2], p->ip.b[1], p->ip.b[0]); MakeDomainNameFromDNSNameString(&p->RR_PTR.namestorage, buffer); p->RR_PTR.ForceMCast = mDNStrue; // This PTR points to our dot-local name, so don't ever try to write it into a uDNS server p->RR_A.resrec.rdata->u.ipv4 = p->ip; AssignDomainName(&p->RR_PTR.resrec.rdata->u.name, p->RR_A.resrec.name); mDNS_Register(m, &p->RR_A); mDNS_Register(m, &p->RR_PTR); debugf("Made Proxy Host Records for %##s", p->RR_A.resrec.name->c); return(mStatus_NoError); }
mDNSlocal void PrintHash(mDNSu8 *digest, int digestlen, char *buffer, int buflen) { int length = 0; for (int j = 0; j < digestlen; j++) { length += mDNS_snprintf(buffer+length, buflen-length-1, "%x", digest[j]); } }
mDNSexport mStatus LNT_UnmapPort(mDNS *m, NATTraversalInfo *n) { char externalPort[10]; Property propArgs[3]; tcpLNTInfo *info; tcpLNTInfo **infoPtr = &m->tcpInfoUnmapList; mStatus err; // If no NAT gateway to talk to, no need to do all this work for nothing 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; // clean up previous port mapping requests and allocations 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; } // 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) if ((info = mDNSPlatformMemAllocate(sizeof(tcpLNTInfo))) == mDNSNULL) { LogInfo("LNT_UnmapPort: can't allocate tcpInfo"); return(mStatus_NoMemoryErr); } *info = n->tcpInfo; while (*infoPtr) infoPtr = &(*infoPtr)->next; // find the end of the list *infoPtr = info; // append err = SendSOAPMsgControlAction(m, info, "DeletePortMapping", 3, propArgs, LNTPortMapDeleteOp); if (err) DisposeInfoFromUnmapList(m, info); return err; }
mDNSlocal void LinkTrustAnchor(mDNS *const m, TrustAnchor *ta) { int length = 0; int i; mDNSu8 *p; TrustAnchor **t = &m->TrustAnchors; char buffer[256]; while (*t) t = &((*t)->next); *t = ta; buffer[0] = 0; p = ta->rds.digest; for (i = 0; i < ta->digestLen; i++) { length += mDNS_snprintf(buffer+length, sizeof(buffer)-1-length, "%x", p[i]); } LogInfo("LinkTrustAnchor: Zone %##s, keytag %d, alg %d, digestType %d, digestLen %d, digest %s", ta->zone.c, ta->rds.keyTag, ta->rds.alg, ta->rds.digestType, ta->digestLen, buffer); }
mDNSlocal int RegisterNotification(mDNS *const m, unsigned int interval) { int len = strlen("com.apple.system.notify.service.timer:+") + 21; // 21 bytes to accomodate the interval char buffer[len]; unsigned int blen; int status; // Starting "interval" second from now (+ below indicates relative) register for a notification blen = mDNS_snprintf(buffer, sizeof(buffer), "com.apple.system.notify.service.timer:+%us", interval); if (blen >= sizeof(buffer)) { LogMsg("RegisterNotification: Buffer too small blen %d, buffer size %d", blen, sizeof(buffer)); return -1; } LogInfo("RegisterNotification: buffer %s", buffer); if (m->notifyToken) { notify_cancel(m->notifyToken); m->notifyToken = 0; } status = notify_register_dispatch(buffer, &m->notifyToken, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(int t) { (void) t; FetchRootTA(m); });
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; //m->omsg is 8952 bytes, which is plenty 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; } // Always query for WANIPConnection in the first SSDP packet if (m->retryIntervalGetAddr <= NATMAP_INIT_RETRY) m->SSDPWANPPPConnection = mDNSfalse; // Create message 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; }
mDNSlocal mStatus GetDeviceDescription(mDNS *m, tcpLNTInfo *info) { // Device description format - // - device description URL // - host/port 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; // already have the info we need if (m->UPnPRouterURL == mDNSNULL || m->UPnPRouterAddressString == mDNSNULL) { LogInfo("GetDeviceDescription: no router URL or address string!"); return (mStatus_Invalid); } // build message if (info->Request != mDNSNULL) mDNSPlatformMemZero(info->Request, LNT_MAXBUFSIZE); // reuse previously allocated buffer 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 int main(int argc, char **argv) { const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; int this_arg = 1; mStatus status; struct in_addr s4; #if HAVE_IPV6 struct in6_addr s6; #endif char buffer[256]; DNSQuestion q; if (argc < 2) goto usage; // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog mDNS_DebugMode = mDNStrue; // Initialise the mDNS core. status = mDNS_Init(&mDNSStorage, &PlatformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_DontAdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %d\n", (int)status); return(status); } signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C signal(SIGTERM, HandleSIG); while (this_arg < argc) { char *arg = argv[this_arg++]; if (this_arg > 2) printf("\n"); lastid = id = zeroID; hostaddr = target = zeroAddr; hostname[0] = hardware[0] = software[0] = 0; NumAddr = NumAAAA = NumHINFO = 0; if (inet_pton(AF_INET, arg, &s4) == 1) { mDNSu8 *p = (mDNSu8 *)&s4; // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p[3], p[2], p[1], p[0]); printf("%s\n", buffer); target.type = mDNSAddrType_IPv4; target.ip.v4.NotAnInteger = s4.s_addr; DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback); if (StopNow == 2) break; } #if HAVE_IPV6 else if (inet_pton(AF_INET6, arg, &s6) == 1) { int i; mDNSu8 *p = (mDNSu8 *)&s6; for (i = 0; i < 16; i++) { static const char hexValues[] = "0123456789ABCDEF"; buffer[i * 4 ] = hexValues[p[15-i] & 0x0F]; buffer[i * 4 + 1] = '.'; buffer[i * 4 + 2] = hexValues[p[15-i] >> 4]; buffer[i * 4 + 3] = '.'; } mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); target.type = mDNSAddrType_IPv6; mDNSPlatformMemCopy(&target.ip.v6, &s6, sizeof(target.ip.v6)); DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback); if (StopNow == 2) break; } #endif else { if (strlen(arg) >= sizeof(hostname))
// Build port mapping request with new port (up to max) and send it 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; // Scan our m->NATTraversals list to make sure the external port we're requesting is locally unique. // UPnP gateways will report conflicts if different devices request the same external port, but if two // clients on the same device request the same external port the second one just stomps over the first. // One way this can happen is like this: // 1. Client A binds local port 80 // 2. Client A requests external port 80 -> internal port 80 // 3. UPnP NAT gateway refuses external port 80 (some other client already has it) // 4. Client A tries again, and successfully gets external port 80 -> internal port 81 // 5. Client B on same machine tries to bind local port 80, and fails // 6. Client B tries again, and successfully binds local port 81 // 7. Client B now requests external port 81 -> internal port 81 // 8. UPnP NAT gateway allows this, stomping over Client A's existing mapping while (n2) { if (n2 == n || RequestedPortNum(n2) != ReqPortNum) n2=n2->next; else { if (n->tcpInfo.retries < 100) { n->tcpInfo.retries++; ReqPortNum = RequestedPortNum(n); // Pick a new port number n2 = m->NATTraversals; // And re-scan the list looking for conflicts } else { natTraversalHandlePortMapReply(m, n, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0); return mStatus_NoError; } } } // create strings to use in the message 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]); // build the message 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); }