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 <assert.h> 19 #include <signal.h> 20 #include <stdio.h> 21 #include <string.h> 22 #include <unistd.h> 23 #include <stdlib.h> 24 25 #include "mDNSEmbeddedAPI.h"// Defines the interface to the mDNS core code 26 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform 27 #include "ExampleClientApp.h" 28 29 // Globals 30 static mDNS mDNSStorage; // mDNS core uses this to store its globals 31 static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals 32 #define RR_CACHE_SIZE 500 33 static CacheEntity gRRCache[RR_CACHE_SIZE]; 34 35 mDNSexport const char ProgramName[] = "mDNSClientPosix"; 36 37 static const char *gProgramName = ProgramName; 38 39 static void BrowseCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) 40 // A callback from the core mDNS code that indicates that we've received a 41 // response to our query. Note that this code runs on the main thread 42 // (in fact, there is only one thread!), so we can safely printf the results. 43 { 44 domainlabel name; 45 domainname type; 46 domainname domain; 47 char nameC [MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL. 48 char typeC [MAX_ESCAPED_DOMAIN_NAME]; 49 char domainC[MAX_ESCAPED_DOMAIN_NAME]; 50 const char *state; 51 52 (void)m; // Unused 53 (void)question; // Unused 54 55 assert(answer->rrtype == kDNSType_PTR); 56 57 DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain); 58 59 ConvertDomainLabelToCString_unescaped(&name, nameC); 60 ConvertDomainNameToCString(&type, typeC); 61 ConvertDomainNameToCString(&domain, domainC); 62 63 // If the TTL has hit 0, the service is no longer available. 64 if (!AddRecord) { 65 state = "Lost "; 66 } else { 67 state = "Found"; 68 } 69 fprintf(stderr, "*** %s name = '%s', type = '%s', domain = '%s'\n", state, nameC, typeC, domainC); 70 } 71 72 static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation) 73 // Checks that serviceType is a reasonable service type 74 // label and, if it isn't and printExplanation is true, prints 75 // an explanation of why not. 76 { 77 mDNSBool result; 78 79 result = mDNStrue; 80 if (result && strlen(serviceType) > 63) { 81 if (printExplanation) { 82 fprintf(stderr, 83 "%s: Service type specified by -t is too long (must be 63 characters or less)\n", 84 gProgramName); 85 } 86 result = mDNSfalse; 87 } 88 if (result && serviceType[0] == 0) { 89 if (printExplanation) { 90 fprintf(stderr, 91 "%s: Service type specified by -t can't be empty\n", 92 gProgramName); 93 } 94 result = mDNSfalse; 95 } 96 return result; 97 } 98 99 static const char kDefaultServiceType[] = "_afpovertcp._tcp"; 100 static const char kDefaultDomain[] = "local."; 101 102 static void PrintUsage() 103 { 104 fprintf(stderr, 105 "Usage: %s [-v level] [-t type] [-d domain]\n", 106 gProgramName); 107 fprintf(stderr, " -v verbose mode, level is a number from 0 to 2\n"); 108 fprintf(stderr, " 0 = no debugging info (default)\n"); 109 fprintf(stderr, " 1 = standard debugging info\n"); 110 fprintf(stderr, " 2 = intense debugging info\n"); 111 fprintf(stderr, " -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType); 112 fprintf(stderr, " -d uses 'domain' as the domain to browse (default is '%s')\n", kDefaultDomain); 113 } 114 115 static const char *gServiceType = kDefaultServiceType; 116 static const char *gServiceDomain = kDefaultDomain; 117 118 static void ParseArguments(int argc, char **argv) 119 // Parses our command line arguments into the global variables 120 // listed above. 121 { 122 int ch; 123 124 // Set gProgramName to the last path component of argv[0] 125 126 gProgramName = strrchr(argv[0], '/'); 127 if (gProgramName == NULL) { 128 gProgramName = argv[0]; 129 } else { 130 gProgramName += 1; 131 } 132 133 // Parse command line options using getopt. 134 135 do { 136 ch = getopt(argc, argv, "v:t:d:"); 137 if (ch != -1) { 138 switch (ch) { 139 case 'v': 140 gMDNSPlatformPosixVerboseLevel = atoi(optarg); 141 if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) { 142 fprintf(stderr, 143 "%s: Verbose mode must be in the range 0..2\n", 144 gProgramName); 145 exit(1); 146 } 147 break; 148 case 't': 149 gServiceType = optarg; 150 if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) { 151 exit(1); 152 } 153 break; 154 case 'd': 155 gServiceDomain = optarg; 156 break; 157 case '?': 158 default: 159 PrintUsage(); 160 exit(1); 161 break; 162 } 163 } 164 } while (ch != -1); 165 166 // Check for any left over command line arguments. 167 168 if (optind != argc) { 169 fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]); 170 exit(1); 171 } 172 } 173 174 int main(int argc, char **argv) 175 // The program's main entry point. The program does a trivial 176 // mDNS query, looking for all AFP servers in the local domain. 177 { 178 int result; 179 mStatus status; 180 DNSQuestion question; 181 domainname type; 182 domainname domain; 183 184 // Parse our command line arguments. This won't come back if there's an error. 185 ParseArguments(argc, argv); 186 187 // Initialise the mDNS core. 188 status = mDNS_Init(&mDNSStorage, &PlatformStorage, 189 gRRCache, RR_CACHE_SIZE, 190 mDNS_Init_DontAdvertiseLocalAddresses, 191 mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); 192 if (status == mStatus_NoError) { 193 194 // Construct and start the query. 195 196 MakeDomainNameFromDNSNameString(&type, gServiceType); 197 MakeDomainNameFromDNSNameString(&domain, gServiceDomain); 198 199 status = mDNS_StartBrowse(&mDNSStorage, &question, &type, &domain, mDNSInterface_Any, mDNSfalse, BrowseCallback, NULL); 200 201 // Run the platform main event loop until the user types ^C. 202 // The BrowseCallback routine is responsible for printing 203 // any results that we find. 204 205 if (status == mStatus_NoError) { 206 fprintf(stderr, "Hit ^C when you're bored waiting for responses.\n"); 207 ExampleClientEventLoop(&mDNSStorage); 208 mDNS_StopQuery(&mDNSStorage, &question); 209 mDNS_Close(&mDNSStorage); 210 } 211 } 212 213 if (status == mStatus_NoError) { 214 result = 0; 215 } else { 216 result = 2; 217 } 218 if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) { 219 fprintf(stderr, "%s: Finished with status %d, result %d\n", gProgramName, (int)status, result); 220 } 221 222 return 0; 223 } 224