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