diff options
Diffstat (limited to 'mDNSResponder/mDNSPosix/ProxyResponder.c')
-rw-r--r-- | mDNSResponder/mDNSPosix/ProxyResponder.c | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/mDNSResponder/mDNSPosix/ProxyResponder.c b/mDNSResponder/mDNSPosix/ProxyResponder.c new file mode 100644 index 00000000..3982161d --- /dev/null +++ b/mDNSResponder/mDNSPosix/ProxyResponder.c @@ -0,0 +1,300 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> // For printf() +#include <stdlib.h> // For exit() etc. +#include <string.h> // For strlen() etc. +#include <unistd.h> // For select() +#include <signal.h> // For SIGINT, SIGTERM +#include <errno.h> // For errno, EINTR +#include <netinet/in.h> // For INADDR_NONE +#include <arpa/inet.h> // For inet_addr() +#include <netdb.h> // For gethostbyname() + +#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above +#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform +#include "ExampleClientApp.h" + +// Compatibility workaround: Solaris 2.5 has no INADDR_NONE +#ifndef INADDR_NONE +#define INADDR_NONE (mDNSu32)0xffffffff +#endif + +//************************************************************************************************************* +// Globals +static mDNS mDNSStorage; // mDNS core uses this to store its globals +static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals +mDNSexport const char ProgramName[] = "mDNSProxyResponderPosix"; + +//************************************************************************************************************* +// Proxy Host Registration + +typedef struct +{ + mDNSv4Addr ip; + domainlabel hostlabel; // Conforms to standard DNS letter-digit-hyphen host name rules + AuthRecord RR_A; // 'A' (address) record for our ".local" name + AuthRecord RR_PTR; // PTR (reverse lookup) record +} ProxyHost; + +mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) +{ + ProxyHost *f = (ProxyHost*)rr->RecordContext; + if (result == mStatus_NoError) + debugf("Host name successfully registered: %##s", rr->resrec.name->c); + else + { + debugf("Host name conflict for %##s", rr->resrec.name->c); + mDNS_Deregister(m, &f->RR_A); + mDNS_Deregister(m, &f->RR_PTR); + exit(-1); + } +} + +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); +} + +//************************************************************************************************************* +// Service Registration + +// This sample ServiceCallback just calls mDNS_RenameAndReregisterService to automatically pick a new +// unique name for the service. For a device such as a printer, this may be appropriate. +// For a device with a user interface, and a screen, and a keyboard, the appropriate +// response may be to prompt the user and ask them to choose a new name for the service. +mDNSlocal void ServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) +{ + switch (result) + { + case mStatus_NoError: debugf("Callback: %##s Name Registered", sr->RR_SRV.resrec.name->c); break; + case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", sr->RR_SRV.resrec.name->c); break; + case mStatus_MemFree: debugf("Callback: %##s Memory Free", sr->RR_SRV.resrec.name->c); break; + default: debugf("Callback: %##s Unknown Result %ld", sr->RR_SRV.resrec.name->c, result); break; + } + + if (result == mStatus_NoError) + { + char buffer[MAX_ESCAPED_DOMAIN_NAME]; + ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer); + printf("Service %s now registered and active\n", buffer); + } + + if (result == mStatus_NameConflict) + { + char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME]; + ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer1); + mDNS_RenameAndReregisterService(m, sr, mDNSNULL); + ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer2); + printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2); + } +} + +// 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); +} + +//************************************************************************************************************* +// Service non-existence assertion +// (claiming a service name without actually providing a service at that name, to prevent anyone else using that name) +// This is useful to avoid confusion between similar services +// e.g. A printer that implements IPP printing service using the name "My Printer", but doesn't implement LPR service, +// should also claim the LPR service name "My Printer" to stop a different printer offering LPR service under the same name, +// since it would be confusing to users to have two equivalent services with the same name. + +mDNSlocal void NoSuchServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result) +{ + const domainname *proxyhostname = (const domainname *)rr->RecordContext; + switch (result) + { + case mStatus_NoError: debugf("Callback: %##s Name Registered", rr->resrec.name->c); break; + case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", rr->resrec.name->c); break; + case mStatus_MemFree: debugf("Callback: %##s Memory Free", rr->resrec.name->c); break; + default: debugf("Callback: %##s Unknown Result %ld", rr->resrec.name->c, result); break; + } + + if (result == mStatus_NoError) + { + char buffer[MAX_ESCAPED_DOMAIN_NAME]; + ConvertDomainNameToCString(rr->resrec.name, buffer); + printf("Non-existence assertion %s now registered and active\n", buffer); + } + + if (result == mStatus_NameConflict) + { + domainlabel n; + domainname t, d; + char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME]; + ConvertDomainNameToCString(rr->resrec.name, buffer1); + DeconstructServiceName(rr->resrec.name, &n, &t, &d); + IncrementLabelSuffix(&n, mDNStrue); + mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, mDNSNULL, 0); + ConvertDomainNameToCString(rr->resrec.name, buffer2); + printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2); + } +} + +mDNSlocal void RegisterNoSuchService(mDNS *m, AuthRecord *const rr, domainname *proxyhostname, + const char name[], const char type[], const char domain[]) +{ + domainlabel n; + domainname t, d; + char buffer[MAX_ESCAPED_DOMAIN_NAME]; + MakeDomainLabelFromLiteralString(&n, name); + MakeDomainNameFromDNSNameString(&t, type); + MakeDomainNameFromDNSNameString(&d, domain); + mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, proxyhostname, 0); + ConvertDomainNameToCString(rr->resrec.name, buffer); + printf("Made Non-existence Record for %s\n", buffer); +} + +//************************************************************************************************************* +// Main + +mDNSexport int main(int argc, char **argv) +{ + mStatus status; + sigset_t signals; + + if (argc < 3) goto usage; + + status = mDNS_Init(&mDNSStorage, &PlatformStorage, + mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, + mDNS_Init_DontAdvertiseLocalAddresses, + mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %d\n", (int)status); return(status); } + + mDNSPosixListenForSignalInEventLoop(SIGINT); + mDNSPosixListenForSignalInEventLoop(SIGTERM); + + if (!strcmp(argv[1], "-")) + { + domainname proxyhostname; + AuthRecord proxyrecord; + if (argc < 5) goto usage; + proxyhostname.c[0] = 0; + AppendLiteralLabelString(&proxyhostname, argv[2]); + AppendLiteralLabelString(&proxyhostname, "local"); + RegisterNoSuchService(&mDNSStorage, &proxyrecord, &proxyhostname, argv[3], argv[4], "local."); + } + else + { + ProxyHost proxyhost; + ServiceRecordSet proxyservice; + + proxyhost.ip.NotAnInteger = inet_addr(argv[1]); + if (proxyhost.ip.NotAnInteger == INADDR_NONE) // INADDR_NONE is 0xFFFFFFFF + { + struct hostent *h = gethostbyname(argv[1]); + if (h) proxyhost.ip.NotAnInteger = *(long*)h->h_addr; + } + if (proxyhost.ip.NotAnInteger == INADDR_NONE) // INADDR_NONE is 0xFFFFFFFF + { + fprintf(stderr, "%s is not valid host address\n", argv[1]); + return(-1); + } + + MakeDomainLabelFromLiteralString(&proxyhost.hostlabel, argv[2]); + + mDNS_RegisterProxyHost(&mDNSStorage, &proxyhost); + + if (argc >=6) + RegisterService(&mDNSStorage, &proxyservice, argv[3], argv[4], "local.", + proxyhost.RR_A.resrec.name, atoi(argv[5]), argc-6, &argv[6]); + } + + do + { + struct timeval timeout = { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM + mDNSBool gotSomething; + mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething); + } + while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM))); + + mDNS_Close(&mDNSStorage); + + return(0); + +usage: + fprintf(stderr, "%s ip hostlabel [srvname srvtype port txt [txt ...]]\n", argv[0]); + fprintf(stderr, "ip Real IP address (or valid host name) of the host where the service actually resides\n"); + fprintf(stderr, "hostlabel First label of the dot-local host name to create for this host, e.g. \"foo\" for \"foo.local.\"\n"); + fprintf(stderr, "srvname Descriptive name of service, e.g. \"Stuart's Ink Jet Printer\"\n"); + fprintf(stderr, "srvtype IANA service type, e.g. \"_ipp._tcp\" or \"_ssh._tcp\", etc.\n"); + fprintf(stderr, "port Port number where the service resides (1-65535)\n"); + fprintf(stderr, "txt Additional name/value pairs specified in service definition, e.g. \"pdl=application/postscript\"\n"); + fprintf(stderr, "e.g. %s 169.254.12.34 thehost (just create a dot-local host name)\n", argv[0]); + fprintf(stderr, "or %s 169.254.12.34 thehost \"My Printer\" _printer._tcp. 515 rp=lpt1 pdl=application/postscript\n", argv[0]); + fprintf(stderr, "or %s - thehost \"My Printer\" _printer._tcp. (assertion of non-existence)\n", argv[0]); + return(-1); +} |