1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 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 <stdio.h>                      // For printf()
19#include <string.h>                     // For strcpy()
20
21#include <Events.h>                     // For WaitNextEvent()
22
23#include <OpenTransport.h>
24#include <OpenTptInternet.h>
25
26#include <SIOUX.h>                      // For SIOUXHandleOneEvent()
27
28#include "dns_sd.h"
29
30typedef union { UInt8 b[2]; UInt16 NotAnInteger; } mDNSOpaque16;
31static UInt16 mDNSVal16(mDNSOpaque16 x) { return((UInt16)(x.b[0]<<8 | x.b[1])); }
32static mDNSOpaque16 mDNSOpaque16fromIntVal(UInt16 v)
33{ mDNSOpaque16 x; x.b[0] = (UInt8)(v >> 8); x.b[1] = (UInt8)(v & 0xFF); return(x); }
34
35typedef struct RegisteredService_struct RegisteredService;
36struct RegisteredService_struct
37{
38    RegisteredService *next;
39    DNSServiceRef sdRef;
40    Boolean gotresult;
41    DNSServiceErrorType errorCode;
42    char namestr[64];
43    char typestr[kDNSServiceMaxDomainName];
44    char domstr [kDNSServiceMaxDomainName];
45};
46
47static RegisteredService p1, p2, afp, http, njp;
48static RegisteredService *services = NULL, **nextservice = &services;
49
50static void RegCallback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
51                        const char *name, const char *regtype, const char *domain, void *context)
52{
53    RegisteredService *rs = (RegisteredService *)context;
54    (void)sdRef;    // Unused
55    (void)flags;    // Unused
56    rs->gotresult = true;
57    rs->errorCode = errorCode;
58    strcpy(rs->namestr, name);
59    strcpy(rs->typestr, regtype);
60    strcpy(rs->domstr,  domain);
61}
62
63static DNSServiceErrorType RegisterService(RegisteredService *rs, mDNSOpaque16 OpaquePort,
64                                           const char name[], const char type[], const char domain[], const char txtinfo[])
65{
66    DNSServiceErrorType err;
67    unsigned char txtbuffer[257];
68    strncpy((char*)txtbuffer+1, txtinfo, 255);
69    txtbuffer[256] = 0;
70    txtbuffer[0] = (unsigned char)strlen((char*)txtbuffer);
71    rs->gotresult = 0;
72    rs->errorCode = kDNSServiceErr_NoError;
73    err = DNSServiceRegister(&rs->sdRef, /* kDNSServiceFlagsAutoRename*/ 0, 0,
74                             name, type, domain, NULL, OpaquePort.NotAnInteger, (unsigned short)(1+txtbuffer[0]), txtbuffer, RegCallback, rs);
75    if (err)
76        printf("RegisterService(%s %s %s) failed %d\n", name, type, domain, err);
77    else
78    { *nextservice = rs; nextservice = &rs->next; }
79    return(err);
80}
81
82// RegisterFakeServiceForTesting() simulates the effect of services being registered on
83// dynamically-allocated port numbers. No real service exists on that port -- this is just for testing.
84static DNSServiceErrorType RegisterFakeServiceForTesting(RegisteredService *rs,
85                                                         const char name[], const char type[], const char domain[], const char txtinfo[])
86{
87    static UInt16 NextPort = 0xF000;
88    return RegisterService(rs, mDNSOpaque16fromIntVal(NextPort++), name, type, domain, txtinfo);
89}
90
91// CreateProxyRegistrationForRealService() checks to see if the given port is currently
92// in use, and if so, advertises the specified service as present on that port.
93// This is useful for advertising existing real services (Personal Web Sharing, Personal
94// File Sharing, etc.) that currently don't register with mDNS Service Discovery themselves.
95static DNSServiceErrorType CreateProxyRegistrationForRealService(RegisteredService *rs,
96                                                                 const char *servicetype, UInt16 PortAsNumber, const char txtinfo[])
97{
98    mDNSOpaque16 OpaquePort = mDNSOpaque16fromIntVal(PortAsNumber);
99    InetAddress ia;
100    TBind bindReq;
101    OSStatus err;
102    TEndpointInfo endpointinfo;
103    EndpointRef ep = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, &endpointinfo, &err);
104    if (!ep || err) { printf("OTOpenEndpoint (CreateProxyRegistrationForRealService) failed %d", err); return(err); }
105
106    ia.fAddressType = AF_INET;
107    ia.fPort        = OpaquePort.NotAnInteger;
108    ia.fHost        = 0;
109    bindReq.addr.maxlen = sizeof(ia);
110    bindReq.addr.len    = sizeof(ia);
111    bindReq.addr.buf    = (UInt8*)&ia;
112    bindReq.qlen        = 0;
113    err = OTBind(ep, &bindReq, NULL);
114
115    if (err == kOTBadAddressErr)
116        err = RegisterService(rs, OpaquePort, "", servicetype, "local.", txtinfo);
117    else if (err)
118        printf("OTBind failed %d", err);
119
120    OTCloseProvider(ep);
121    return(err);
122}
123
124// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS
125static Boolean YieldSomeTime(UInt32 milliseconds)
126{
127    extern Boolean SIOUXQuitting;
128    EventRecord e;
129    WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL);
130    SIOUXHandleOneEvent(&e);
131    return(SIOUXQuitting);
132}
133
134int main()
135{
136    OSStatus err;
137    RegisteredService *s;
138
139    SIOUXSettings.asktosaveonclose = false;
140    SIOUXSettings.userwindowtitle = "\pMulticast DNS Responder";
141
142    printf("Multicast DNS Responder\n\n");
143    printf("This software reports errors using MacsBug breaks,\n");
144    printf("so if you don't have MacsBug installed your Mac may crash.\n\n");
145    printf("******************************************************************************\n\n");
146
147    err = InitOpenTransport();
148    if (err) { printf("InitOpenTransport failed %d", err); return(err); }
149
150    printf("Advertising Services...\n");
151
152#define SRSET 0
153#if SRSET==0
154    RegisterFakeServiceForTesting(&p1, "Web Server One", "_http._tcp.", "local.", "path=/index.html");
155    RegisterFakeServiceForTesting(&p2, "Web Server Two", "_http._tcp.", "local.", "path=/path.html");
156#elif SRSET==1
157    RegisterFakeServiceForTesting(&p1, "Epson Stylus 900N", "_printer._tcp.", "local.", "rn=lpq1");
158    RegisterFakeServiceForTesting(&p2, "HP LaserJet",       "_printer._tcp.", "local.", "rn=lpq2");
159#else
160    RegisterFakeServiceForTesting(&p1, "My Printer",        "_printer._tcp.", "local.", "rn=lpq3");
161    RegisterFakeServiceForTesting(&p2, "My Other Printer",  "_printer._tcp.", "local.", "lrn=pq4");
162#endif
163
164    // If AFP Server is running, register a record for it
165    CreateProxyRegistrationForRealService(&afp, "_afpovertcp._tcp.", 548, "");
166
167    // If Web Server is running, register a record for it
168    CreateProxyRegistrationForRealService(&http, "_http._tcp.",       80, "path=/index.html");
169
170    while (!YieldSomeTime(35))
171        for (s = services; s; s = s->next)
172            if (s->gotresult)
173            {
174                printf("%s %s %s registered\n", s->namestr, s->typestr, s->domstr);
175                s->gotresult = false;
176            }
177
178    for (s = services; s; s = s->next)
179        if (s->sdRef) DNSServiceRefDeallocate(s->sdRef);
180
181    CloseOpenTransport();
182    return(0);
183}
184