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
30static mDNS mDNSStorage;       // mDNS core uses this to store its globals
31static mDNS_PlatformSupport PlatformStorage;  // Stores this platform's globals
32#define RR_CACHE_SIZE 500
33static CacheEntity gRRCache[RR_CACHE_SIZE];
34
35mDNSexport const char ProgramName[] = "mDNSClientPosix";
36
37static const char *gProgramName = ProgramName;
38
39static 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
72static 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
99static const char kDefaultServiceType[] = "_afpovertcp._tcp";
100static const char kDefaultDomain[]      = "local.";
101
102static 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
115static const char *gServiceType      = kDefaultServiceType;
116static const char *gServiceDomain    = kDefaultDomain;
117
118static 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
174int 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