// RegisterService() is a simple wrapper function which takes C string // parameters, converts them to domainname parameters, and calls mDNS_RegisterService() mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, UInt16 PortAsNumber, const char txtinfo[], const domainlabel *const n, const char type[], const char domain[]) { domainname t; domainname d; char buffer[MAX_ESCAPED_DOMAIN_NAME]; UInt8 txtbuffer[512]; MakeDomainNameFromDNSNameString(&t, type); MakeDomainNameFromDNSNameString(&d, domain); if (txtinfo) { strncpy((char*)txtbuffer+1, txtinfo, sizeof(txtbuffer)-1); txtbuffer[0] = (UInt8)strlen(txtinfo); } else txtbuffer[0] = 0; mDNS_RegisterService(m, recordset, n, &t, &d, // Name, type, domain mDNSNULL, mDNSOpaque16fromIntVal(PortAsNumber), txtbuffer, (mDNSu16)(1+txtbuffer[0]), // TXT data, length mDNSNULL, 0, // Subtypes (none) mDNSInterface_Any, // Interface ID Callback, mDNSNULL); // Callback and context ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer); printf("Made Service Records for %s\n", buffer); }
// RegisterService() is a simple wrapper function which takes C string // parameters, converts them to domainname parameters, and calls mDNS_RegisterService() mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, const char name[], const char type[], const char domain[], const domainname *host, mDNSu16 PortAsNumber, int argc, char **argv) { domainlabel n; domainname t, d; unsigned char txtbuffer[1024], *bptr = txtbuffer; char buffer[MAX_ESCAPED_DOMAIN_NAME]; MakeDomainLabelFromLiteralString(&n, name); MakeDomainNameFromDNSNameString(&t, type); MakeDomainNameFromDNSNameString(&d, domain); while (argc) { int len = strlen(argv[0]); if (len > 255 || bptr + 1 + len >= txtbuffer + sizeof(txtbuffer)) break; printf("STR: %s\n", argv[0]); bptr[0] = len; strcpy((char*)(bptr+1), argv[0]); bptr += 1 + len; argc--; argv++; } mDNS_RegisterService(m, recordset, &n, &t, &d, // Name, type, domain host, mDNSOpaque16fromIntVal(PortAsNumber), txtbuffer, bptr-txtbuffer, // TXT data, length mDNSNULL, 0, // Subtypes mDNSInterface_Any, // Interface ID ServiceCallback, mDNSNULL, 0); // Callback, context, flags ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer); printf("Made Service Records for %s\n", buffer); }
// CreateProxyRegistrationForRealService() checks to see if the given port is currently // in use, and if so, advertises the specified service as present on that port. // This is useful for advertising existing real services (Personal Web Sharing, Personal // File Sharing, etc.) that currently don't register with mDNS Service Discovery themselves. static DNSServiceErrorType CreateProxyRegistrationForRealService(RegisteredService *rs, const char *servicetype, UInt16 PortAsNumber, const char txtinfo[]) { mDNSOpaque16 OpaquePort = mDNSOpaque16fromIntVal(PortAsNumber); InetAddress ia; TBind bindReq; OSStatus err; TEndpointInfo endpointinfo; EndpointRef ep = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, &endpointinfo, &err); if (!ep || err) { printf("OTOpenEndpoint (CreateProxyRegistrationForRealService) failed %d", err); return(err); } ia.fAddressType = AF_INET; ia.fPort = OpaquePort.NotAnInteger; ia.fHost = 0; bindReq.addr.maxlen = sizeof(ia); bindReq.addr.len = sizeof(ia); bindReq.addr.buf = (UInt8*)&ia; bindReq.qlen = 0; err = OTBind(ep, &bindReq, NULL); if (err == kOTBadAddressErr) err = RegisterService(rs, OpaquePort, "", servicetype, "local.", txtinfo); else if (err) printf("OTBind failed %d", err); OTCloseProvider(ep); return(err); }
// CreateProxyRegistrationForRealService() checks to see if the given port is currently // in use, and if so, advertises the specified service as present on that port. // This is useful for advertising existing real services (Personal Web Sharing, Personal // File Sharing, etc.) that currently don't register with mDNS Service Discovery themselves. mDNSlocal OSStatus CreateProxyRegistrationForRealService(mDNS *m, UInt16 PortAsNumber, const char txtinfo[], const char *servicetype, ServiceRecordSet *recordset) { InetAddress ia; TBind bindReq; OSStatus err; TEndpointInfo endpointinfo; EndpointRef ep = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, &endpointinfo, &err); if (!ep || err) { printf("OTOpenEndpoint (CreateProxyRegistrationForRealService) failed %d", err); return(err); } ia.fAddressType = AF_INET; ia.fPort = mDNSOpaque16fromIntVal(PortAsNumber).NotAnInteger; ia.fHost = 0; bindReq.addr.maxlen = sizeof(ia); bindReq.addr.len = sizeof(ia); bindReq.addr.buf = (UInt8*)&ia; bindReq.qlen = 0; err = OTBind(ep, &bindReq, NULL); if (err == kOTBadAddressErr) RegisterService(m, recordset, PortAsNumber, txtinfo, &m->nicelabel, servicetype, "local."); else if (err) debugf("OTBind failed %d", err); OTCloseProvider(ep); return(noErr); }
static mStatus RegisterOneService(const char * richTextName, const char * serviceType, const char * serviceDomain, const mDNSu8 text[], mDNSu16 textLen, long portNumber) { mStatus status; PosixService * thisServ; domainlabel name; domainname type; domainname domain; status = mStatus_NoError; thisServ = (PosixService *) malloc(sizeof(*thisServ)); if (thisServ == NULL) { status = mStatus_NoMemoryErr; } if (status == mStatus_NoError) { MakeDomainLabelFromLiteralString(&name, richTextName); MakeDomainNameFromDNSNameString(&type, serviceType); MakeDomainNameFromDNSNameString(&domain, serviceDomain); status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ, &name, &type, &domain, // Name, type, domain NULL, mDNSOpaque16fromIntVal(portNumber), text, textLen, // TXT data, length NULL, 0, // Subtypes mDNSInterface_Any, // Interface ID RegistrationCallback, thisServ); // Callback and context } if (status == mStatus_NoError) { thisServ->serviceID = gServiceID; gServiceID += 1; thisServ->next = gServiceList; gServiceList = thisServ; if (gMDNSPlatformPosixVerboseLevel > 0) { fprintf(stderr, "%s: Registered service %d, name '%s', type '%s', port %ld\n", gProgramName, thisServ->serviceID, richTextName, serviceType, portNumber); } } else { if (thisServ != NULL) { free(thisServ); } } return status; }
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); // Note: modifies ptr 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); // Make sure to compute extport *before* we zero 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); }
mDNSexport void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain, mDNSBool *DomainDiscoveryDisabled) { char buf[MAX_ESCAPED_DOMAIN_NAME] = ""; char knn[MAX_ESCAPED_DOMAIN_NAME] = ""; domainname keyName; mDNSIPPort port; keyName.c[0] = knn[0] = 0; mStatus err; FILE *f = fopen(filename, "r"); if (hostname) hostname->c[0] = 0; if (domain) domain->c[0] = 0; if (DomainDiscoveryDisabled) *DomainDiscoveryDisabled = mDNSfalse; port = UnicastDNSPort; if (f) { if (DomainDiscoveryDisabled && GetConfigOption(buf, "DomainDiscoveryDisabled", f) && !strcasecmp(buf, "true")) *DomainDiscoveryDisabled = mDNStrue; if (hostname && GetConfigOption(buf, "hostname", f) && !MakeDomainNameFromDNSNameString(hostname, buf)) goto badf; if (domain && GetConfigOption(buf, "zone", f) && !MakeDomainNameFromDNSNameString(domain, buf)) goto badf; if (GetConfigOption(buf, "port", f)) { port = mDNSOpaque16fromIntVal(strtol(buf, (char **)NULL, 10)); } buf[0] = 0; GetConfigOption(buf, "secret-64", f); // failure means no authentication if (GetConfigOption(knn, "secret-name", f)) MakeDomainNameFromDNSNameString(&keyName, knn); fclose(f); f = NULL; } else { if (errno != ENOENT) LogMsg("ERROR: Config file exists, but cannot be opened."); return; } if (domain && domain->c[0] && buf[0]) { DomainAuthInfo *info = (DomainAuthInfo*)mDNSPlatformMemAllocate(sizeof(*info)); // for now we assume keyname = service reg domain and we use same key for service and hostname registration err = mDNS_SetSecretForDomain(m, info, domain, (keyName.c[0] ? &keyName : domain), buf, hostname, &port, NULL); if (err) LogMsg("ERROR: mDNS_SetSecretForDomain returned %d for domain %##s", err, domain->c); } return; badf: LogMsg("ERROR: malformatted config file"); if (f) fclose(f); }
// This function does a simple parse of an HTTP URL that may include a hostname, port, and path // 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) mDNSlocal mStatus ParseHttpUrl(char* ptr, char* end, mDNSu8** addressAndPort, mDNSIPPort* port, mDNSu8** path) { // if the data begins with "http://", we assume there is a hostname and possibly a port number if (end - ptr >= 7 && strncasecmp(ptr, "http://", 7) == 0) { int i; char* stop = end; char* addrPtr = mDNSNULL; ptr += 7; //skip over "http://" if (ptr >= end) { LogInfo("ParseHttpUrl: past end of buffer parsing host:port"); return mStatus_BadParamErr; } // find the end of the host:port addrPtr = ptr; for (i = 0; addrPtr && addrPtr != end; i++, addrPtr++) if (*addrPtr == '/') break; // allocate the buffer (len i+1 so we have space to terminate the string) 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'; // find the port number in the string, by looking backwards for the ':' stop = ptr; // can't go back farther than the original start ptr = addrPtr; // move ptr to the path part for (addrPtr--;addrPtr>stop;addrPtr--) { if (*addrPtr == ':') { int tmpport; addrPtr++; // skip over ':' tmpport = (int)strtol(addrPtr, mDNSNULL, 10); *port = mDNSOpaque16fromIntVal(tmpport); // store it properly converted break; } } } // ptr should now point to the first character we haven't yet processed // everything that remains is the path 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; }
static ServiceRecordSet * foobar_register(mDNSu16 port) { ServiceRecordSet *srs; mStatus status; domainlabel name; domainname type; domainname domain; srs = calloc(1, sizeof(*srs)); assert(srs != NULL); MakeDomainLabelFromLiteralString(&name, "foobar"); MakeDomainNameFromDNSNameString(&type, "_foobar._tcp"); MakeDomainNameFromDNSNameString(&domain, "local."); status = mDNS_RegisterService(&mDNSStorage, srs, &name, &type, &domain, NULL, mDNSOpaque16fromIntVal(port), NULL, 0, NULL, 0, mDNSInterface_Any, foobar_callback, srs, 0); assert(status == mStatus_NoError); return srs; }
// RegisterFakeServiceForTesting() simulates the effect of services being registered on // dynamically-allocated port numbers. No real service exists on that port -- this is just for testing. static DNSServiceErrorType RegisterFakeServiceForTesting(RegisteredService *rs, const char name[], const char type[], const char domain[], const char txtinfo[]) { static UInt16 NextPort = 0xF000; return RegisterService(rs, mDNSOpaque16fromIntVal(NextPort++), name, type, domain, txtinfo); }
// This function parses the response to our SSDP discovery message. Basically, we look to make sure this is a response // referencing a service we care about (WANIPConnection or WANPPPConnection), then look for the "Location:" header and copy the addressing and // URL info we need. 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; // already have the info we need // The formatting of the HTTP header is not always the same when it comes to the placement of // the service and location strings, so we just look for each of them from the beginning for every response // figure out if this is a message from a service we care about 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; // not a message we care about // find "Location:", starting from the beginning ptr = (char *)data; while (ptr && ptr != end) { if ((*ptr & 0xDF) == 'L' && (strncasecmp(ptr, "Location:", 9) == 0)) break; // find the first 'L'; is this Location? if not, keep looking ptr++; } if (ptr == mDNSNULL || ptr == end) { mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Location", ""); return; // not a message we care about } ptr += 9; //Skip over 'Location:' while (*ptr == ' ' && ptr < end) ptr++; // skip over spaces if (ptr >= end) return; // find the end of the line for (stop = ptr; stop != end; stop++) { if (*stop == '\r') { end = stop; break; } } // fill in default port m->UPnPRouterPort = mDNSOpaque16fromIntVal(80); // free string pointers and set to NULL if (m->UPnPRouterAddressString != mDNSNULL) { mDNSPlatformMemFree(m->UPnPRouterAddressString); m->UPnPRouterAddressString = mDNSNULL; } if (m->UPnPRouterURL != mDNSNULL) { mDNSPlatformMemFree(m->UPnPRouterURL); m->UPnPRouterURL = mDNSNULL; } // the Router URL should look something like "/dyndev/uuid:0013-108c-4b3f0000f3dc" 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); // Don't need the SSDP socket anymore 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", ""); // now send message to get the device description GetDeviceDescription(m, &m->tcpDeviceInfo); }