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