1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2003 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 <stdio.h>                      // For printf()
19#include <string.h>                     // For strcpy()
20
21#include <Events.h>                     // For WaitNextEvent()
22#include <CodeFragments.h>              // For SIOkUnresolvedCFragSymbolAddress
23
24#include <SIOUX.h>                      // For SIOUXHandleOneEvent()
25
26#include <OpenTransport.h>
27#include <OpenTptInternet.h>
28
29#include "dns_sd.h"
30
31#define ns_c_in 1
32#define ns_t_a 1
33
34typedef union { UInt8 b[2]; UInt16 NotAnInteger; } mDNSOpaque16;
35static UInt16 mDNSVal16(mDNSOpaque16 x) { return((UInt16)(x.b[0]<<8 | x.b[1])); }
36static mDNSOpaque16 mDNSOpaque16fromIntVal(UInt16 v)
37{ mDNSOpaque16 x; x.b[0] = (UInt8)(v >> 8); x.b[1] = (UInt8)(v & 0xFF); return(x); }
38
39typedef struct
40{
41    OTLIFO serviceinfolist;
42    Boolean headerPrinted;
43    Boolean lostRecords;
44} SearcherServices;
45
46typedef struct
47{
48    SearcherServices *services;
49    char name[kDNSServiceMaxDomainName];
50    char type[kDNSServiceMaxDomainName];
51    char domn[kDNSServiceMaxDomainName];
52    char host[kDNSServiceMaxDomainName];
53    char text[kDNSServiceMaxDomainName];
54    InetHost address;
55    mDNSOpaque16 notAnIntPort;
56    DNSServiceRef sdRef;
57    Boolean add;
58    Boolean dom;
59    OTLink link;
60} linkedServiceInfo;
61
62static SearcherServices services;
63
64// PrintServiceInfo prints the service information to standard out
65// A real application might want to do something else with the information
66static void PrintServiceInfo(SearcherServices *services)
67{
68    OTLink *link = OTReverseList(OTLIFOStealList(&services->serviceinfolist));
69
70    while (link)
71    {
72        linkedServiceInfo *s = OTGetLinkObject(link, linkedServiceInfo, link);
73
74        if (!services->headerPrinted)
75        {
76            printf("%-55s Type             Domain         Target Host     IP Address      Port Info\n", "Name");
77            services->headerPrinted = true;
78        }
79
80        if (s->dom)
81        {
82            if (s->add) printf("%-55s available for browsing\n", s->domn);
83            else printf("%-55s no longer available for browsing\n", s->domn);
84        }
85        else
86        {
87            char ip[16];
88            unsigned char *p = (unsigned char *)&s->address;
89            sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
90            printf("%-55s %-16s %-14s ", s->name, s->type, s->domn);
91            if (s->add) printf("%-15s %-15s %5d %s\n", s->host, ip, mDNSVal16(s->notAnIntPort), s->text);
92            else printf("Removed\n");
93        }
94
95        link = link->fNext;
96        OTFreeMem(s);
97    }
98}
99
100static void FoundInstanceAddress(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode,
101                                 const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context)
102{
103    linkedServiceInfo *info = (linkedServiceInfo *)context;
104    SearcherServices *services = info->services;
105    (void)sdRef;            // Unused
106    (void)interfaceIndex;   // Unused
107    (void)fullname;         // Unused
108    (void)ttl;              // Unused
109    if (errorCode == kDNSServiceErr_NoError)
110        if (flags & kDNSServiceFlagsAdd)
111            if (rrclass == ns_c_in && rrtype == ns_t_a && rdlen == sizeof(info->address))
112            {
113                memcpy(&info->address, rdata, sizeof(info->address));
114                DNSServiceRefDeallocate(info->sdRef);
115                OTLIFOEnqueue(&services->serviceinfolist, &info->link);
116            }
117}
118
119static void FoundInstanceInfo(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
120                              DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t notAnIntPort,
121                              uint16_t txtLen, const unsigned char *txtRecord, void *context)
122{
123    linkedServiceInfo *info = (linkedServiceInfo *)context;
124    SearcherServices *services = info->services;
125    (void)sdRef;            // Unused
126    (void)flags;            // Unused
127    (void)interfaceIndex;   // Unused
128    (void)errorCode;        // Unused
129    (void)fullname;         // Unused
130    strcpy(info->host, hosttarget);
131    if (txtLen == 0) info->text[0] = 0;
132    else
133    {
134        strncpy(info->text, (char *)txtRecord+1, txtRecord[0]);
135        info->text[txtRecord[0]] = 0;
136    }
137    info->notAnIntPort.NotAnInteger = notAnIntPort;
138    DNSServiceRefDeallocate(info->sdRef);
139    DNSServiceQueryRecord(&info->sdRef, 0, 0, info->host, ns_t_a, ns_c_in, FoundInstanceAddress, info);
140}
141
142// When a new named instance of a service is found, FoundInstance() is called.
143// In this sample code we turn around and immediately to a DNSServiceResolve() to resolve that service name
144// to find its target host, port, and txtinfo, but a normal browing application would just display the name.
145// Resolving every single thing you find can be quite hard on the network, so you shouldn't do this
146// in a real application. Defer resolving until the client has picked which instance from the
147// long list of services is the one they want to use, and then resolve only that one.
148static void FoundInstance(DNSServiceRef client, DNSServiceFlags flags, uint32_t interface, DNSServiceErrorType errorCode,
149                          const char *replyName, const char *replyType, const char *replyDomain, void *context)
150{
151#pragma unused(client, interface, errorCode)
152    SearcherServices *services = (SearcherServices *)context;
153    linkedServiceInfo *info;
154
155    if (!services) { DebugStr("\pFoundInstance: services is NULL"); return; }
156
157    info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo));
158    if (!info) { services->lostRecords = true; return; }
159
160    info->services = services;
161    strcpy(info->name, replyName);
162    strcpy(info->type, replyType);
163    strcpy(info->domn, replyDomain);
164    info->text[0] = 0;
165    info->add = (flags & kDNSServiceFlagsAdd) ? true : false;
166    info->dom = false;
167
168    if (!info->add) // If TTL == 0 we're deleting a service,
169        OTLIFOEnqueue(&services->serviceinfolist, &info->link);
170    else                                // else we're adding a new service
171        DNSServiceResolve(&info->sdRef, 0, 0, info->name, info->type, info->domn, FoundInstanceInfo, info);
172}
173
174// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS
175static Boolean YieldSomeTime(UInt32 milliseconds)
176{
177    extern Boolean SIOUXQuitting;
178    EventRecord e;
179    WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL);
180    SIOUXHandleOneEvent(&e);
181    return(SIOUXQuitting);
182}
183
184int main()
185{
186    OSStatus err;
187    void *tempmem;
188    DNSServiceRef sdRef;
189    DNSServiceErrorType dse;
190
191    SIOUXSettings.asktosaveonclose = false;
192    SIOUXSettings.userwindowtitle  = "\pMulticast DNS Searcher";
193    SIOUXSettings.rows             = 40;
194    SIOUXSettings.columns          = 160;
195
196    printf("DNS-SD Search Client\n\n");
197    printf("This software reports errors using MacsBug breaks,\n");
198    printf("so if you don't have MacsBug installed your Mac may crash.\n\n");
199    printf("******************************************************************************\n\n");
200
201    if (DNSServiceBrowse == (void*)kUnresolvedCFragSymbolAddress)
202    {
203        printf("Before you can use mDNS/DNS-SD clients, you need to place the \n");
204        printf("\"Multicast DNS & DNS-SD\" Extension in the Extensions Folder and restart\n");
205        return(-1);
206    }
207
208    err = InitOpenTransport();
209    if (err) { printf("InitOpenTransport failed %d", err); return(err); }
210
211    // Make sure OT has a large enough memory pool for us to draw from at OTNotifier (interrupt) time
212    tempmem = OTAllocMem(0x10000);
213    if (tempmem) OTFreeMem(tempmem);
214    else printf("**** Warning: OTAllocMem couldn't pre-allocate 64K for us.\n");
215
216    services.serviceinfolist.fHead = NULL;
217    services.headerPrinted         = false;
218    services.lostRecords           = false;
219
220    printf("Sending mDNS service lookup queries and waiting for responses...\n\n");
221    dse = DNSServiceBrowse(&sdRef, 0, 0, "_http._tcp", "", FoundInstance, &services);
222    if (dse == kDNSServiceErr_NoError)
223    {
224        while (!YieldSomeTime(35))
225        {
226            if (services.serviceinfolist.fHead)
227                PrintServiceInfo(&services);
228
229            if (services.lostRecords)
230            {
231                services.lostRecords = false;
232                printf("**** Warning: Out of memory: Records have been missed.\n");
233            }
234        }
235    }
236
237    DNSServiceRefDeallocate(sdRef);
238    CloseOpenTransport();
239    return(0);
240}
241