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
BrowseCallback(mDNS * const m,DNSQuestion * question,const ResourceRecord * const answer,QC_result AddRecord)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
CheckThatServiceTypeIsUsable(const char * serviceType,mDNSBool printExplanation)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
PrintUsage()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
ParseArguments(int argc,char ** argv)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
main(int argc,char ** argv)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