224 lines
7.6 KiB
C
Executable File
224 lines
7.6 KiB
C
Executable File
/* -*- Mode: C; tab-width: 4 -*-
|
|
*
|
|
* Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "mDNSEmbeddedAPI.h"// Defines the interface to the mDNS core code
|
|
#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
|
|
#include "ExampleClientApp.h"
|
|
|
|
// Globals
|
|
static mDNS mDNSStorage; // mDNS core uses this to store its globals
|
|
static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
|
|
#define RR_CACHE_SIZE 500
|
|
static CacheEntity gRRCache[RR_CACHE_SIZE];
|
|
|
|
mDNSexport const char ProgramName[] = "mDNSClientPosix";
|
|
|
|
static const char *gProgramName = ProgramName;
|
|
|
|
static void BrowseCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
|
|
// A callback from the core mDNS code that indicates that we've received a
|
|
// response to our query. Note that this code runs on the main thread
|
|
// (in fact, there is only one thread!), so we can safely printf the results.
|
|
{
|
|
domainlabel name;
|
|
domainname type;
|
|
domainname domain;
|
|
char nameC [MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
|
|
char typeC [MAX_ESCAPED_DOMAIN_NAME];
|
|
char domainC[MAX_ESCAPED_DOMAIN_NAME];
|
|
const char *state;
|
|
|
|
(void)m; // Unused
|
|
(void)question; // Unused
|
|
|
|
assert(answer->rrtype == kDNSType_PTR);
|
|
|
|
DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain);
|
|
|
|
ConvertDomainLabelToCString_unescaped(&name, nameC);
|
|
ConvertDomainNameToCString(&type, typeC);
|
|
ConvertDomainNameToCString(&domain, domainC);
|
|
|
|
// If the TTL has hit 0, the service is no longer available.
|
|
if (!AddRecord) {
|
|
state = "Lost ";
|
|
} else {
|
|
state = "Found";
|
|
}
|
|
fprintf(stderr, "*** %s name = '%s', type = '%s', domain = '%s'\n", state, nameC, typeC, domainC);
|
|
}
|
|
|
|
static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation)
|
|
// Checks that serviceType is a reasonable service type
|
|
// label and, if it isn't and printExplanation is true, prints
|
|
// an explanation of why not.
|
|
{
|
|
mDNSBool result;
|
|
|
|
result = mDNStrue;
|
|
if (result && strlen(serviceType) > 63) {
|
|
if (printExplanation) {
|
|
fprintf(stderr,
|
|
"%s: Service type specified by -t is too long (must be 63 characters or less)\n",
|
|
gProgramName);
|
|
}
|
|
result = mDNSfalse;
|
|
}
|
|
if (result && serviceType[0] == 0) {
|
|
if (printExplanation) {
|
|
fprintf(stderr,
|
|
"%s: Service type specified by -t can't be empty\n",
|
|
gProgramName);
|
|
}
|
|
result = mDNSfalse;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static const char kDefaultServiceType[] = "_afpovertcp._tcp";
|
|
static const char kDefaultDomain[] = "local.";
|
|
|
|
static void PrintUsage()
|
|
{
|
|
fprintf(stderr,
|
|
"Usage: %s [-v level] [-t type] [-d domain]\n",
|
|
gProgramName);
|
|
fprintf(stderr, " -v verbose mode, level is a number from 0 to 2\n");
|
|
fprintf(stderr, " 0 = no debugging info (default)\n");
|
|
fprintf(stderr, " 1 = standard debugging info\n");
|
|
fprintf(stderr, " 2 = intense debugging info\n");
|
|
fprintf(stderr, " -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType);
|
|
fprintf(stderr, " -d uses 'domain' as the domain to browse (default is '%s')\n", kDefaultDomain);
|
|
}
|
|
|
|
static const char *gServiceType = kDefaultServiceType;
|
|
static const char *gServiceDomain = kDefaultDomain;
|
|
|
|
static void ParseArguments(int argc, char **argv)
|
|
// Parses our command line arguments into the global variables
|
|
// listed above.
|
|
{
|
|
int ch;
|
|
|
|
// Set gProgramName to the last path component of argv[0]
|
|
|
|
gProgramName = strrchr(argv[0], '/');
|
|
if (gProgramName == NULL) {
|
|
gProgramName = argv[0];
|
|
} else {
|
|
gProgramName += 1;
|
|
}
|
|
|
|
// Parse command line options using getopt.
|
|
|
|
do {
|
|
ch = getopt(argc, argv, "v:t:d:");
|
|
if (ch != -1) {
|
|
switch (ch) {
|
|
case 'v':
|
|
gMDNSPlatformPosixVerboseLevel = atoi(optarg);
|
|
if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) {
|
|
fprintf(stderr,
|
|
"%s: Verbose mode must be in the range 0..2\n",
|
|
gProgramName);
|
|
exit(1);
|
|
}
|
|
break;
|
|
case 't':
|
|
gServiceType = optarg;
|
|
if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) {
|
|
exit(1);
|
|
}
|
|
break;
|
|
case 'd':
|
|
gServiceDomain = optarg;
|
|
break;
|
|
case '?':
|
|
default:
|
|
PrintUsage();
|
|
exit(1);
|
|
break;
|
|
}
|
|
}
|
|
} while (ch != -1);
|
|
|
|
// Check for any left over command line arguments.
|
|
|
|
if (optind != argc) {
|
|
fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
// The program's main entry point. The program does a trivial
|
|
// mDNS query, looking for all AFP servers in the local domain.
|
|
{
|
|
int result;
|
|
mStatus status;
|
|
DNSQuestion question;
|
|
domainname type;
|
|
domainname domain;
|
|
|
|
// Parse our command line arguments. This won't come back if there's an error.
|
|
ParseArguments(argc, argv);
|
|
|
|
// Initialise the mDNS core.
|
|
status = mDNS_Init(&mDNSStorage, &PlatformStorage,
|
|
gRRCache, RR_CACHE_SIZE,
|
|
mDNS_Init_DontAdvertiseLocalAddresses,
|
|
mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
|
|
if (status == mStatus_NoError) {
|
|
|
|
// Construct and start the query.
|
|
|
|
MakeDomainNameFromDNSNameString(&type, gServiceType);
|
|
MakeDomainNameFromDNSNameString(&domain, gServiceDomain);
|
|
|
|
status = mDNS_StartBrowse(&mDNSStorage, &question, &type, &domain, mDNSInterface_Any, mDNSfalse, BrowseCallback, NULL);
|
|
|
|
// Run the platform main event loop until the user types ^C.
|
|
// The BrowseCallback routine is responsible for printing
|
|
// any results that we find.
|
|
|
|
if (status == mStatus_NoError) {
|
|
fprintf(stderr, "Hit ^C when you're bored waiting for responses.\n");
|
|
ExampleClientEventLoop(&mDNSStorage);
|
|
mDNS_StopQuery(&mDNSStorage, &question);
|
|
mDNS_Close(&mDNSStorage);
|
|
}
|
|
}
|
|
|
|
if (status == mStatus_NoError) {
|
|
result = 0;
|
|
} else {
|
|
result = 2;
|
|
}
|
|
if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) {
|
|
fprintf(stderr, "%s: Finished with status %d, result %d\n", gProgramName, (int)status, result);
|
|
}
|
|
|
|
return 0;
|
|
}
|