Home | History | Annotate | Download | only in mDNSPosix
      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