Home | History | Annotate | Download | only in mDNSPosix
      1 /* -*- Mode: C; tab-width: 4 -*-
      2  *
      3  * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
      4  *
      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
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     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.
     16  */
     17 
     18 #include <stdio.h>				// For printf()
     19 #include <stdlib.h>				// For exit() etc.
     20 #include <string.h>				// For strlen() etc.
     21 #include <unistd.h>				// For select()
     22 #include <signal.h>				// For SIGINT, SIGTERM
     23 #include <errno.h>				// For errno, EINTR
     24 #include <netinet/in.h>			// For INADDR_NONE
     25 #include <arpa/inet.h>			// For inet_addr()
     26 #include <netdb.h>				// For gethostbyname()
     27 
     28 #include "mDNSEmbeddedAPI.h"	// Defines the interface to the client layer above
     29 #include "mDNSPosix.h"			// Defines the specific types needed to run mDNS on this platform
     30 #include "ExampleClientApp.h"
     31 
     32 // Compatibility workaround: Solaris 2.5 has no INADDR_NONE
     33 #ifndef	INADDR_NONE
     34 #define	INADDR_NONE	(mDNSu32)0xffffffff
     35 #endif
     36 
     37 //*************************************************************************************************************
     38 // Globals
     39 static mDNS mDNSStorage;       // mDNS core uses this to store its globals
     40 static mDNS_PlatformSupport PlatformStorage;  // Stores this platform's globals
     41 mDNSexport const char ProgramName[] = "mDNSProxyResponderPosix";
     42 
     43 //*************************************************************************************************************
     44 // Proxy Host Registration
     45 
     46 typedef struct
     47 	{
     48 	mDNSv4Addr ip;
     49 	domainlabel hostlabel;		// Conforms to standard DNS letter-digit-hyphen host name rules
     50 	AuthRecord RR_A;		// 'A' (address) record for our ".local" name
     51 	AuthRecord RR_PTR;		// PTR (reverse lookup) record
     52 	} ProxyHost;
     53 
     54 mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
     55 	{
     56 	ProxyHost *f = (ProxyHost*)rr->RecordContext;
     57 	if (result == mStatus_NoError)
     58 		debugf("Host name successfully registered: %##s", rr->resrec.name->c);
     59 	else
     60 		{
     61 		debugf("Host name conflict for %##s", rr->resrec.name->c);
     62 		mDNS_Deregister(m, &f->RR_A);
     63 		mDNS_Deregister(m, &f->RR_PTR);
     64 		exit(-1);
     65 		}
     66 	}
     67 
     68 mDNSlocal mStatus mDNS_RegisterProxyHost(mDNS *m, ProxyHost *p)
     69 	{
     70 	char buffer[32];
     71 
     72 	mDNS_SetupResourceRecord(&p->RR_A,   mDNSNULL, mDNSInterface_Any, kDNSType_A,   60, kDNSRecordTypeUnique,      AuthRecordAny, HostNameCallback, p);
     73 	mDNS_SetupResourceRecord(&p->RR_PTR, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, AuthRecordAny, HostNameCallback, p);
     74 
     75 	p->RR_A.namestorage.c[0] = 0;
     76 	AppendDomainLabel(&p->RR_A.namestorage, &p->hostlabel);
     77 	AppendLiteralLabelString(&p->RR_A.namestorage, "local");
     78 
     79 	// Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
     80 	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]);
     81 	MakeDomainNameFromDNSNameString(&p->RR_PTR.namestorage, buffer);
     82 	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
     83 
     84 	p->RR_A.  resrec.rdata->u.ipv4 = p->ip;
     85 	AssignDomainName(&p->RR_PTR.resrec.rdata->u.name, p->RR_A.resrec.name);
     86 
     87 	mDNS_Register(m, &p->RR_A);
     88 	mDNS_Register(m, &p->RR_PTR);
     89 
     90 	debugf("Made Proxy Host Records for %##s", p->RR_A.resrec.name->c);
     91 
     92 	return(mStatus_NoError);
     93 	}
     94 
     95 //*************************************************************************************************************
     96 // Service Registration
     97 
     98 // This sample ServiceCallback just calls mDNS_RenameAndReregisterService to automatically pick a new
     99 // unique name for the service. For a device such as a printer, this may be appropriate.
    100 // For a device with a user interface, and a screen, and a keyboard, the appropriate
    101 // response may be to prompt the user and ask them to choose a new name for the service.
    102 mDNSlocal void ServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result)
    103 	{
    104 	switch (result)
    105 		{
    106 		case mStatus_NoError:      debugf("Callback: %##s Name Registered",    sr->RR_SRV.resrec.name->c); break;
    107 		case mStatus_NameConflict: debugf("Callback: %##s Name Conflict",      sr->RR_SRV.resrec.name->c); break;
    108 		case mStatus_MemFree:      debugf("Callback: %##s Memory Free",        sr->RR_SRV.resrec.name->c); break;
    109 		default:                   debugf("Callback: %##s Unknown Result %ld", sr->RR_SRV.resrec.name->c, result); break;
    110 		}
    111 
    112 	if (result == mStatus_NoError)
    113 		{
    114 		char buffer[MAX_ESCAPED_DOMAIN_NAME];
    115 		ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer);
    116 		printf("Service %s now registered and active\n", buffer);
    117 		}
    118 
    119 	if (result == mStatus_NameConflict)
    120 		{
    121 		char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME];
    122 		ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer1);
    123 		mDNS_RenameAndReregisterService(m, sr, mDNSNULL);
    124 		ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer2);
    125 		printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2);
    126 		}
    127 	}
    128 
    129 // RegisterService() is a simple wrapper function which takes C string
    130 // parameters, converts them to domainname parameters, and calls mDNS_RegisterService()
    131 mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset,
    132 	const char name[], const char type[], const char domain[],
    133 	const domainname *host, mDNSu16 PortAsNumber, int argc, char **argv)
    134 	{
    135 	domainlabel n;
    136 	domainname t, d;
    137 	unsigned char txtbuffer[1024], *bptr = txtbuffer;
    138 	char buffer[MAX_ESCAPED_DOMAIN_NAME];
    139 
    140 	MakeDomainLabelFromLiteralString(&n, name);
    141 	MakeDomainNameFromDNSNameString(&t, type);
    142 	MakeDomainNameFromDNSNameString(&d, domain);
    143 	while (argc)
    144 		{
    145 		int len = strlen(argv[0]);
    146 		if (len > 255 || bptr + 1 + len >= txtbuffer + sizeof(txtbuffer)) break;
    147 		printf("STR: %s\n", argv[0]);
    148 		bptr[0] = len;
    149 		strcpy((char*)(bptr+1), argv[0]);
    150 		bptr += 1 + len;
    151 		argc--;
    152 		argv++;
    153 		}
    154 
    155 	mDNS_RegisterService(m, recordset,
    156 		&n, &t, &d,					// Name, type, domain
    157 		host, mDNSOpaque16fromIntVal(PortAsNumber),
    158 		txtbuffer, bptr-txtbuffer,	// TXT data, length
    159 		mDNSNULL, 0,				// Subtypes
    160 		mDNSInterface_Any,			// Interface ID
    161 		ServiceCallback, mDNSNULL, 0);	// Callback, context, flags
    162 
    163 	ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer);
    164 	printf("Made Service Records for %s\n", buffer);
    165 	}
    166 
    167 //*************************************************************************************************************
    168 // Service non-existence assertion
    169 // (claiming a service name without actually providing a service at that name, to prevent anyone else using that name)
    170 // This is useful to avoid confusion between similar services
    171 // e.g. A printer that implements IPP printing service using the name "My Printer", but doesn't implement LPR service,
    172 // should also claim the LPR service name "My Printer" to stop a different printer offering LPR service under the same name,
    173 // since it would be confusing to users to have two equivalent services with the same name.
    174 
    175 mDNSlocal void NoSuchServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
    176 	{
    177 	const domainname *proxyhostname = (const domainname *)rr->RecordContext;
    178 	switch (result)
    179 		{
    180 		case mStatus_NoError:      debugf("Callback: %##s Name Registered",    rr->resrec.name->c); break;
    181 		case mStatus_NameConflict: debugf("Callback: %##s Name Conflict",      rr->resrec.name->c); break;
    182 		case mStatus_MemFree:      debugf("Callback: %##s Memory Free",        rr->resrec.name->c); break;
    183 		default:                   debugf("Callback: %##s Unknown Result %ld", rr->resrec.name->c, result); break;
    184 		}
    185 
    186 	if (result == mStatus_NoError)
    187 		{
    188 		char buffer[MAX_ESCAPED_DOMAIN_NAME];
    189 		ConvertDomainNameToCString(rr->resrec.name, buffer);
    190 		printf("Non-existence assertion %s now registered and active\n", buffer);
    191 		}
    192 
    193 	if (result == mStatus_NameConflict)
    194 		{
    195 		domainlabel n;
    196 		domainname t, d;
    197 		char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME];
    198 		ConvertDomainNameToCString(rr->resrec.name, buffer1);
    199 		DeconstructServiceName(rr->resrec.name, &n, &t, &d);
    200 		IncrementLabelSuffix(&n, mDNStrue);
    201 		mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, mDNSNULL, mDNSfalse);
    202 		ConvertDomainNameToCString(rr->resrec.name, buffer2);
    203 		printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2);
    204 		}
    205 	}
    206 
    207 mDNSlocal void RegisterNoSuchService(mDNS *m, AuthRecord *const rr, domainname *proxyhostname,
    208 	const char name[], const char type[], const char domain[])
    209 	{
    210 	domainlabel n;
    211 	domainname t, d;
    212 	char buffer[MAX_ESCAPED_DOMAIN_NAME];
    213 	MakeDomainLabelFromLiteralString(&n, name);
    214 	MakeDomainNameFromDNSNameString(&t, type);
    215 	MakeDomainNameFromDNSNameString(&d, domain);
    216 	mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, proxyhostname, mDNSfalse);
    217 	ConvertDomainNameToCString(rr->resrec.name, buffer);
    218 	printf("Made Non-existence Record for %s\n", buffer);
    219 	}
    220 
    221 //*************************************************************************************************************
    222 // Main
    223 
    224 mDNSexport int main(int argc, char **argv)
    225 	{
    226 	mStatus			status;
    227 	sigset_t		signals;
    228 
    229 	if (argc < 3) goto usage;
    230 
    231 	status = mDNS_Init(&mDNSStorage, &PlatformStorage,
    232 		mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
    233 		mDNS_Init_DontAdvertiseLocalAddresses,
    234 		mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
    235 	if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %d\n", (int)status); return(status); }
    236 
    237 	mDNSPosixListenForSignalInEventLoop(SIGINT);
    238 	mDNSPosixListenForSignalInEventLoop(SIGTERM);
    239 
    240 	if (!strcmp(argv[1], "-"))
    241 		{
    242 		domainname proxyhostname;
    243 		AuthRecord proxyrecord;
    244 		if (argc < 5) goto usage;
    245 		proxyhostname.c[0] = 0;
    246 		AppendLiteralLabelString(&proxyhostname, argv[2]);
    247 		AppendLiteralLabelString(&proxyhostname, "local");
    248 		RegisterNoSuchService(&mDNSStorage, &proxyrecord, &proxyhostname, argv[3], argv[4], "local.");
    249 		}
    250 	else
    251 		{
    252 		ProxyHost proxyhost;
    253 		ServiceRecordSet proxyservice;
    254 
    255 		proxyhost.ip.NotAnInteger = inet_addr(argv[1]);
    256 		if (proxyhost.ip.NotAnInteger == INADDR_NONE)	// INADDR_NONE is 0xFFFFFFFF
    257 			{
    258 			struct hostent *h = gethostbyname(argv[1]);
    259 			if (h) proxyhost.ip.NotAnInteger = *(long*)h->h_addr;
    260 			}
    261 		if (proxyhost.ip.NotAnInteger == INADDR_NONE)	// INADDR_NONE is 0xFFFFFFFF
    262 			{
    263 			fprintf(stderr, "%s is not valid host address\n", argv[1]);
    264 			return(-1);
    265 			}
    266 
    267 		MakeDomainLabelFromLiteralString(&proxyhost.hostlabel, argv[2]);
    268 
    269 		mDNS_RegisterProxyHost(&mDNSStorage, &proxyhost);
    270 
    271 		if (argc >=6)
    272 			RegisterService(&mDNSStorage, &proxyservice, argv[3], argv[4], "local.",
    273 							proxyhost.RR_A.resrec.name, atoi(argv[5]), argc-6, &argv[6]);
    274 		}
    275 
    276 	do
    277 		{
    278 		struct timeval	timeout = { 0x3FFFFFFF, 0 };	// wait until SIGINT or SIGTERM
    279 		mDNSBool		gotSomething;
    280 		mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething);
    281 		}
    282 	while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM)));
    283 
    284 	mDNS_Close(&mDNSStorage);
    285 
    286 	return(0);
    287 
    288 usage:
    289 	fprintf(stderr, "%s ip hostlabel [srvname srvtype port txt [txt ...]]\n", argv[0]);
    290 	fprintf(stderr, "ip        Real IP address (or valid host name) of the host where the service actually resides\n");
    291 	fprintf(stderr, "hostlabel First label of the dot-local host name to create for this host, e.g. \"foo\" for \"foo.local.\"\n");
    292 	fprintf(stderr, "srvname   Descriptive name of service, e.g. \"Stuart's Ink Jet Printer\"\n");
    293 	fprintf(stderr, "srvtype   IANA service type, e.g. \"_ipp._tcp\" or \"_ssh._tcp\", etc.\n");
    294 	fprintf(stderr, "port      Port number where the service resides (1-65535)\n");
    295 	fprintf(stderr, "txt       Additional name/value pairs specified in service definition, e.g. \"pdl=application/postscript\"\n");
    296 	fprintf(stderr, "e.g. %s 169.254.12.34 thehost                                (just create a dot-local host name)\n", argv[0]);
    297 	fprintf(stderr, "or   %s 169.254.12.34 thehost \"My Printer\" _printer._tcp. 515 rp=lpt1 pdl=application/postscript\n", argv[0]);
    298 	fprintf(stderr, "or   %s -             thehost \"My Printer\" _printer._tcp.           (assertion of non-existence)\n", argv[0]);
    299 	return(-1);
    300 	}
    301