blob: ca370516c7a48305c283c39562778d2c0175a29b [file] [log] [blame]
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2002-2003 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.
Change History (most recent first):
$Log: Mac\040OS\040Test\040Searcher.c,v $
Revision 1.24 2008/11/04 19:43:35 cheshire
Updated comment about MAX_ESCAPED_DOMAIN_NAME size (should be 1009, not 1005)
Revision 1.23 2007/07/27 19:30:40 cheshire
Changed mDNSQuestionCallback parameter from mDNSBool to QC_result,
to properly reflect tri-state nature of the possible responses
Revision 1.22 2006/08/14 23:24:29 cheshire
Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
Revision 1.21 2004/12/16 20:49:34 cheshire
<rdar://problem/3324626> Cache memory management improvements
Revision 1.20 2004/10/19 21:33:18 cheshire
<rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
Revision 1.19 2004/09/17 01:08:50 cheshire
Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
declared in that file are ONLY appropriate to single-address-space embedded applications.
For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
Revision 1.18 2004/09/16 21:59:16 cheshire
For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr
Revision 1.17 2004/06/10 04:37:27 cheshire
Add new parameter in mDNS_GetDomains()
Revision 1.16 2004/03/12 21:30:25 cheshire
Build a System-Context Shared Library from mDNSCore, for the benefit of developers
like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code.
Revision 1.15 2004/01/24 23:55:15 cheshire
Change to use mDNSOpaque16fromIntVal/mDNSVal16 instead of shifting and masking
Revision 1.14 2003/11/14 21:27:09 cheshire
<rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1009) instead of 256-byte buffers.
Revision 1.13 2003/08/14 02:19:54 cheshire
<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
Revision 1.12 2003/08/12 19:56:24 cheshire
Update to APSL 2.0
*/
#include <stdio.h> // For printf()
#include <Events.h> // For WaitNextEvent()
#include <SIOUX.h> // For SIOUXHandleOneEvent()
#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above
#include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform
typedef struct
{
OTLIFO serviceinfolist;
Boolean headerPrinted;
Boolean lostRecords;
} SearcherServices;
typedef struct { ServiceInfo i; mDNSBool add; mDNSBool dom; OTLink link; } linkedServiceInfo;
// These don't have to be globals, but their memory does need to remain valid for as
// long as the search is going on. They are declared as globals here for simplicity.
#define RR_CACHE_SIZE 1000
static CacheEntity rrcachestorage[RR_CACHE_SIZE];
static mDNS mDNSStorage;
static mDNS_PlatformSupport PlatformSupportStorage;
static SearcherServices services;
static DNSQuestion browsequestion, domainquestion;
// PrintServiceInfo prints the service information to standard out
// A real application might want to do something else with the information
static void PrintServiceInfo(SearcherServices *services)
{
OTLink *link = OTReverseList(OTLIFOStealList(&services->serviceinfolist));
while (link)
{
linkedServiceInfo *ls = OTGetLinkObject(link, linkedServiceInfo, link);
ServiceInfo *s = &ls->i;
if (!services->headerPrinted)
{
printf("%-55s Type Domain IP Address Port Info\n", "Name");
services->headerPrinted = true;
}
if (ls->dom)
{
char c_dom[MAX_ESCAPED_DOMAIN_NAME];
ConvertDomainNameToCString(&s->name, c_dom);
if (ls->add) printf("%-55s available for browsing\n", c_dom);
else printf("%-55s no longer available for browsing\n", c_dom);
}
else
{
domainlabel name;
domainname type, domain;
char c_name[MAX_DOMAIN_LABEL+1], c_type[MAX_ESCAPED_DOMAIN_NAME], c_dom[MAX_ESCAPED_DOMAIN_NAME], c_ip[20];
DeconstructServiceName(&s->name, &name, &type, &domain);
ConvertDomainLabelToCString_unescaped(&name, c_name);
ConvertDomainNameToCString(&type, c_type);
ConvertDomainNameToCString(&domain, c_dom);
sprintf(c_ip, "%d.%d.%d.%d", s->ip.ip.v4.b[0], s->ip.ip.v4.b[1], s->ip.ip.v4.b[2], s->ip.ip.v4.b[3]);
printf("%-55s %-16s %-14s ", c_name, c_type, c_dom);
if (ls->add) printf("%-15s %5d %#s\n", c_ip, mDNSVal16(s->port), s->TXTinfo);
else printf("Removed\n");
}
link = link->fNext;
OTFreeMem(ls);
}
}
// When the name, address, port, and txtinfo for a service is found, FoundInstanceInfo()
// enqueues a record for PrintServiceInfo() to print.
// Note, a browsing application would *not* normally need to get all this information --
// all it needs is the name, to display to the user.
// Finding out the address, port, and txtinfo should be deferred to the time that the user
// actually needs to contact the service to use it.
static void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query)
{
SearcherServices *services = (SearcherServices *)query->ServiceInfoQueryContext;
linkedServiceInfo *info = (linkedServiceInfo *)(query->info);
if (query->info->ip.type == mDNSAddrType_IPv4)
{
mDNS_StopResolveService(m, query); // For this test code, one answer is sufficient
OTLIFOEnqueue(&services->serviceinfolist, &info->link);
OTFreeMem(query);
}
}
// When a new named instance of a service is found, FoundInstance() is called.
// In this sample code we turn around and immediately issue a query to resolve that service name to
// find its address, port, and txtinfo, but a normal browing application would just display the name.
static void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
{
#pragma unused (question)
SearcherServices *services = (SearcherServices *)question->QuestionContext;
linkedServiceInfo *info;
debugf("FoundInstance %##s PTR %##s", answer->name->c, answer->rdata->u.name.c);
if (answer->rrtype != kDNSType_PTR) return;
if (!services) { debugf("FoundInstance: services is NULL"); return; }
info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo));
if (!info) { services->lostRecords = true; return; }
info->i.name = answer->rdata->u.name;
info->i.InterfaceID = answer->InterfaceID;
info->i.ip.type = mDNSAddrType_IPv4;
info->i.ip.ip.v4 = zerov4Addr;
info->i.port = zeroIPPort;
info->add = AddRecord;
info->dom = mDNSfalse;
if (!AddRecord) // If TTL == 0 we're deleting a service,
OTLIFOEnqueue(&services->serviceinfolist, &info->link);
else // else we're adding a new service
{
ServiceInfoQuery *q = (ServiceInfoQuery *)OTAllocMem(sizeof(ServiceInfoQuery));
if (!q) { OTFreeMem(info); services->lostRecords = true; return; }
mDNS_StartResolveService(m, q, &info->i, FoundInstanceInfo, services);
}
}
static void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
{
#pragma unused (m)
#pragma unused (question)
SearcherServices *services = (SearcherServices *)question->QuestionContext;
linkedServiceInfo *info;
debugf("FoundDomain %##s PTR %##s", answer->name->c, answer->rdata->u.name.c);
if (answer->rrtype != kDNSType_PTR) return;
if (!services) { debugf("FoundDomain: services is NULL"); return; }
info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo));
if (!info) { services->lostRecords = true; return; }
info->i.name = answer->rdata->u.name;
info->i.InterfaceID = answer->InterfaceID;
info->i.ip.type = mDNSAddrType_IPv4;
info->i.ip.ip.v4 = zerov4Addr;
info->i.port = zeroIPPort;
info->add = AddRecord;
info->dom = mDNStrue;
OTLIFOEnqueue(&services->serviceinfolist, &info->link);
}
// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS
static Boolean YieldSomeTime(UInt32 milliseconds)
{
extern Boolean SIOUXQuitting;
EventRecord e;
WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL);
SIOUXHandleOneEvent(&e);
return(SIOUXQuitting);
}
int main()
{
mStatus err;
Boolean DoneSetup = false;
void *tempmem;
SIOUXSettings.asktosaveonclose = false;
SIOUXSettings.userwindowtitle = "\pMulticast DNS Searcher";
SIOUXSettings.rows = 40;
SIOUXSettings.columns = 132;
printf("Multicast DNS Searcher\n\n");
printf("This software reports errors using MacsBug breaks,\n");
printf("so if you don't have MacsBug installed your Mac may crash.\n\n");
printf("******************************************************************************\n");
err = InitOpenTransport();
if (err) { debugf("InitOpenTransport failed %d", err); return(err); }
err = mDNS_Init(&mDNSStorage, &PlatformSupportStorage, rrcachestorage, RR_CACHE_SIZE,
mDNS_Init_DontAdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
if (err) return(err);
// Make sure OT has a large enough memory pool for us to draw from at OTNotifier (interrupt) time
tempmem = OTAllocMem(0x10000);
if (tempmem) OTFreeMem(tempmem);
else printf("**** Warning: OTAllocMem couldn't pre-allocate 64K for us.\n");
services.serviceinfolist.fHead = NULL;
services.headerPrinted = false;
services.lostRecords = false;
while (!YieldSomeTime(35))
{
#if MDNS_ONLYSYSTEMTASK
// For debugging, use "#define MDNS_ONLYSYSTEMTASK 1" and call mDNSPlatformIdle() periodically.
// For shipping code, don't define MDNS_ONLYSYSTEMTASK, and you don't need to call mDNSPlatformIdle()
extern void mDNSPlatformIdle(mDNS *const m);
mDNSPlatformIdle(&mDNSStorage); // Only needed for debugging version
#endif
if (mDNSStorage.mDNSPlatformStatus == mStatus_NoError && !DoneSetup)
{
domainname srvtype, srvdom;
DoneSetup = true;
printf("\nSending mDNS service lookup queries and waiting for responses...\n\n");
MakeDomainNameFromDNSNameString(&srvtype, "_http._tcp.");
MakeDomainNameFromDNSNameString(&srvdom, "local.");
err = mDNS_StartBrowse(&mDNSStorage, &browsequestion, &srvtype, &srvdom, mDNSInterface_Any, mDNSfalse, FoundInstance, &services);
if (err) break;
err = mDNS_GetDomains(&mDNSStorage, &domainquestion, mDNS_DomainTypeBrowse, NULL, mDNSInterface_Any, FoundDomain, &services);
if (err) break;
}
if (services.serviceinfolist.fHead)
PrintServiceInfo(&services);
if (services.lostRecords)
{
services.lostRecords = false;
printf("**** Warning: Out of memory: Records have been missed.\n");
}
}
mDNS_StopBrowse(&mDNSStorage, &browsequestion);
mDNS_Close(&mDNSStorage);
return(0);
}