summaryrefslogtreecommitdiffstats
path: root/mDNSResponder/mDNSMacOS9/mDNSMacOS9.c
blob: 24b03f229811bf74f9eaf2a34d8a091e36b72154 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
/* -*- 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.
 */

#include <stdio.h>
#include <stdarg.h>                     // For va_list support

#include <LowMem.h>                     // For LMGetCurApName()
#include <TextUtils.h>                  // For smSystemScript
#include <UnicodeConverter.h>           // For ConvertFromPStringToUnicode()

#include "mDNSEmbeddedAPI.h"                // Defines the interface provided to the client layer above

#include "mDNSMacOS9.h"                 // Defines the specific types needed to run mDNS on this platform

// ***************************************************************************
// Constants

static const TSetBooleanOption kReusePortOption =
{ kOTBooleanOptionSize,          INET_IP, IP_REUSEPORT,      0, true };

// IP_RCVDSTADDR with TSetByteOption/kOTOneByteOptionSize works on OS 9, OS X Classic, and OS 9 Carbon,
// but gives error #-3151 (kOTBadOptionErr) on OS X Carbon.
// If we instead use TSetBooleanOption/kOTBooleanOptionSize then OTOptionManagement on OS X Carbon
// no longer returns -3151 but it still doesn't actually work -- no destination addresses
// are delivered by OTRcvUData. I think it's just a bug in OS X Carbon.
static const TSetByteOption kRcvDestAddrOption =
{ kOTOneByteOptionSize,          INET_IP, IP_RCVDSTADDR,     0, true };
//static const TSetBooleanOption kRcvDestAddrOption =
//	{ kOTBooleanOptionSize,          INET_IP, IP_RCVDSTADDR,     0, true };

static const TSetByteOption kSetUnicastTTLOption =
{ kOTOneByteOptionSize,          INET_IP, IP_TTL,            0, 255 };

static const TSetByteOption kSetMulticastTTLOption =
{ kOTOneByteOptionSize,          INET_IP, IP_MULTICAST_TTL,  0, 255 };

static const TIPAddMulticastOption kAddLinkMulticastOption  =
{ sizeof(TIPAddMulticastOption), INET_IP, IP_ADD_MEMBERSHIP, 0, { 224,  0,  0,251 }, { 0,0,0,0 } };

//static const TIPAddMulticastOption kAddAdminMulticastOption =
//	{ sizeof(TIPAddMulticastOption), INET_IP, IP_ADD_MEMBERSHIP, 0, { 239,255,255,251 }, { 0,0,0,0 } };

// Bind endpoint to port number. Don't specify any specific IP address --
// we want to receive unicasts on all interfaces, as well as multicasts.
typedef struct { OTAddressType fAddressType; mDNSIPPort fPort; mDNSv4Addr fHost; UInt8 fUnused[8]; } mDNSInetAddress;
//static const mDNSInetAddress mDNSPortInetAddress = { AF_INET, { 0,0 }, { 0,0,0,0 } };	// For testing legacy client support
#define MulticastDNSPortAsNumber 5353
static const mDNSInetAddress mDNSPortInetAddress = { AF_INET, { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF }, { 0,0,0,0 } };
static const TBind mDNSbindReq = { sizeof(mDNSPortInetAddress), sizeof(mDNSPortInetAddress), (UInt8*)&mDNSPortInetAddress, 0 };

static const TNetbuf zeroTNetbuf = { 0 };

// ***************************************************************************
// Functions

mDNSlocal void SafeDebugStr(unsigned char *buffer)
{
    int i;
    // Don't want semicolons in MacsBug messages -- they signify commands to execute
    for (i=1; i<= buffer[0]; i++) if (buffer[i] == ';') buffer[i] = '.';
    DebugStr(buffer);
}

#if MDNS_DEBUGMSGS
mDNSexport void debugf_(const char *format, ...)
{
    unsigned char buffer[256];
    va_list ptr;
    va_start(ptr,format);
    buffer[0] = (unsigned char)mDNS_vsnprintf((char*)buffer+1, 255, format, ptr);
    va_end(ptr);
#if MDNS_ONLYSYSTEMTASK
    buffer[1+buffer[0]] = 0;
    fprintf(stderr, "%s\n", buffer+1);
    fflush(stderr);
#else
    SafeDebugStr(buffer);
#endif
}
#endif

#if MDNS_BUILDINGSHAREDLIBRARY >= 2
// When building the non-debug version of the Extension, intended to go on end-user systems, we don't want
// MacsBug breaks for *anything*, not even for the serious LogMsg messages that on OS X would be written to syslog
mDNSexport void LogMsg(const char *format, ...) { (void)format; }
#else
mDNSexport void LogMsg(const char *format, ...)
{
    unsigned char buffer[256];
    va_list ptr;
    va_start(ptr,format);
    buffer[0] = (unsigned char)mDNS_vsnprintf((char*)buffer+1, 255, format, ptr);
    va_end(ptr);
#if MDNS_ONLYSYSTEMTASK
    buffer[1+buffer[0]] = 0;
    fprintf(stderr, "%s\n", buffer+1);
    fflush(stderr);
#else
    SafeDebugStr(buffer);
#endif
}
#endif

mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end,
                                       mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstPort)
{
    // Note: If we did multi-homing, we'd have to use the InterfaceID parameter to specify from which interface to send this response
    #pragma unused(InterfaceID)

    InetAddress InetDest;
    TUnitData senddata;

    if (dst->type != mDNSAddrType_IPv4) return(mStatus_NoError);

    InetDest.fAddressType = AF_INET;
    InetDest.fPort        = dstPort.NotAnInteger;
    InetDest.fHost        = dst->ip.v4.NotAnInteger;

    senddata.addr.maxlen = sizeof(InetDest);
    senddata.addr.len    = sizeof(InetDest);
    senddata.addr.buf    = (UInt8*)&InetDest;
    senddata.opt          = zeroTNetbuf;
    senddata.udata.maxlen = (UInt32)((UInt8*)end - (UInt8*)msg);
    senddata.udata.len    = (UInt32)((UInt8*)end - (UInt8*)msg);
    senddata.udata.buf    = (UInt8*)msg;

    return(OTSndUData(m->p->ep, &senddata));
}

mDNSlocal OSStatus readpacket(mDNS *m)
{
    mDNSAddr senderaddr, destaddr;
    mDNSInterfaceID interface;
    mDNSIPPort senderport;
    InetAddress sender;
    char options[256];
    DNSMessage packet;
    TUnitData recvdata;
    OTFlags flags = 0;
    OSStatus err;

    recvdata.addr.maxlen = sizeof(sender);
    recvdata.addr.len    = 0;
    recvdata.addr.buf    = (UInt8*)&sender;
    recvdata.opt.maxlen = sizeof(options);
    recvdata.opt.len    = 0;
    recvdata.opt.buf    = (UInt8*)&options;
    recvdata.udata.maxlen = sizeof(packet);
    recvdata.udata.len    = 0;
    recvdata.udata.buf    = (UInt8*)&packet;

    err = OTRcvUData(m->p->ep, &recvdata, &flags);
    if (err && err != kOTNoDataErr) debugf("OTRcvUData error %d", err);

    if (err) return(err);

    senderaddr.type = mDNSAddrType_IPv4;
    senderaddr.ip.v4.NotAnInteger = sender.fHost;
    senderport.NotAnInteger = sender.fPort;

    destaddr.type = mDNSAddrType_IPv4;
    destaddr.ip.v4  = zerov4Addr;

    #if OTCARBONAPPLICATION
    // IP_RCVDSTADDR is known to fail on OS X Carbon, so we'll just assume the packet was probably multicast
    destaddr.ip.v4  = AllDNSLinkGroup_v4.ip.v4;
    #endif

    if (recvdata.opt.len)
    {
        TOption *c = nil;
        while (1)
        {
            err = OTNextOption(recvdata.opt.buf, recvdata.opt.len, &c);
            if (err || !c) break;
            if (c->level == INET_IP && c->name == IP_RCVDSTADDR && c->len - kOTOptionHeaderSize == sizeof(destaddr.ip.v4))
                mDNSPlatformMemCopy(&destaddr.ip.v4, c->value, sizeof(destaddr.ip.v4));
        }
    }

    interface = m->HostInterfaces->InterfaceID;

    if      (flags & T_MORE) debugf("ERROR: OTRcvUData() buffer too small (T_MORE set)");
    else if (recvdata.addr.len < sizeof(InetAddress)) debugf("ERROR: recvdata.addr.len (%d) too short", recvdata.addr.len);
    else mDNSCoreReceive(m, &packet, recvdata.udata.buf + recvdata.udata.len, &senderaddr, senderport, &destaddr, MulticastDNSPort, interface);

    return(err);
}

mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS * const m, TCPSocketFlags flags, mDNSIPPort * port)
{
    (void)m;            // Unused
    (void)flags;        // Unused
    (void)port;         // Unused
    return NULL;
}

mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int sd)
{
    (void)flags;        // Unused
    (void)sd;           // Unused
    return NULL;
}

mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock)
{
    (void)sock;         // Unused
    return -1;
}

mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr * dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID,
                                          TCPConnectionCallback callback, void * context)
{
    (void)sock;         // Unused
    (void)dst;          // Unused
    (void)dstport;      // Unused
    (void)InterfaceID;  // Unused
    (void)callback;     // Unused
    (void)context;      // Unused
    return(mStatus_UnsupportedErr);
}

mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sd)
{
    (void)sd;           // Unused
}

mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed)
{
    (void)sock;         // Unused
    (void)buf;          // Unused
    (void)buflen;       // Unused
    (void)closed;       // Unused
    return(0);
}

mDNSexport long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len)
{
    (void)sock;         // Unused
    (void)msg;          // Unused
    (void)len;          // Unused
    return(0);
}

mDNSexport UDPSocket *mDNSPlatformUDPSocket(mDNS * const m, mDNSIPPort port)
{
    (void)m;            // Unused
    (void)port;         // Unused
    return NULL;
}

mDNSexport void           mDNSPlatformUDPClose(UDPSocket *sock)
{
    (void)sock;         // Unused
}

mDNSlocal void mDNSOptionManagement(mDNS *const m)
{
    OSStatus err;

    // Make sure the length in the TNetbuf agrees with the length in the TOptionHeader
    m->p->optReq.opt.len    = m->p->optBlock.h.len;
    m->p->optReq.opt.maxlen = m->p->optBlock.h.len;
    if (m->p->optReq.opt.maxlen < 4)
        m->p->optReq.opt.maxlen = 4;

    err = OTOptionManagement(m->p->ep, &m->p->optReq, NULL);
    if (err) LogMsg("OTOptionManagement failed %d", err);
}

mDNSlocal void mDNSinitComplete(mDNS *const m, mStatus result)
{
    m->mDNSPlatformStatus = result;
    mDNSCoreInitComplete(m, mStatus_NoError);
}

mDNSlocal pascal void mDNSNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie)
{
    mDNS *const m = (mDNS *const)contextPtr;
    if (!m) debugf("mDNSNotifier FATAL ERROR! No context");
    switch (code)
    {
    case T_OPENCOMPLETE:
    {
        OSStatus err;
        InetInterfaceInfo interfaceinfo;
        if (result) { LogMsg("T_OPENCOMPLETE failed %d", result); mDNSinitComplete(m, result); return; }
        //debugf("T_OPENCOMPLETE");
        m->p->ep = (EndpointRef)cookie;
        //debugf("OTInetGetInterfaceInfo");
        // (In future may want to loop over all interfaces instead of just using kDefaultInetInterface)
        err = OTInetGetInterfaceInfo(&interfaceinfo, kDefaultInetInterface);
        if (err) { LogMsg("OTInetGetInterfaceInfo failed %d", err); mDNSinitComplete(m, err); return; }

        // Make our basic standard host resource records (address, PTR, etc.)
        m->p->interface.InterfaceID             = (mDNSInterfaceID)&m->p->interface;
        m->p->interface.ip.type               = mDNSAddrType_IPv4;
        m->p->interface.ip.ip.v4.NotAnInteger = interfaceinfo.fAddress;
        m->p->interface.mask.type               = mDNSAddrType_IPv4;
        m->p->interface.mask.ip.v4.NotAnInteger = interfaceinfo.fNetmask;
        m->p->interface.ifname[0]               = 0;
        m->p->interface.Advertise               = m->AdvertiseLocalAddresses;
        m->p->interface.McastTxRx               = mDNStrue;
    }

    case T_OPTMGMTCOMPLETE:
    case T_BINDCOMPLETE:
        // IP_RCVDSTADDR is known to fail on OS X Carbon, so we don't want to abort for that error
        // (see comment above at the definition of kRcvDestAddrOption)
            #if OTCARBONAPPLICATION
        if (result && m->p->mOTstate == mOT_RcvDestAddr)
            LogMsg("Carbon IP_RCVDSTADDR option failed %d; continuing anyway", result);
        else
            #endif
        if (result) { LogMsg("T_OPTMGMTCOMPLETE/T_BINDCOMPLETE %d failed %d", m->p->mOTstate, result); mDNSinitComplete(m, result); return; }
        //LogMsg("T_OPTMGMTCOMPLETE/T_BINDCOMPLETE %d", m->p->mOTstate);
        switch (++m->p->mOTstate)
        {
        case mOT_ReusePort:     m->p->optBlock.b = kReusePortOption;         mDNSOptionManagement(m); break;
        case mOT_RcvDestAddr:   m->p->optBlock.i = kRcvDestAddrOption;       mDNSOptionManagement(m); break;
        case mOT_SetUTTL:       m->p->optBlock.i = kSetUnicastTTLOption;     mDNSOptionManagement(m); break;
        case mOT_SetMTTL:       m->p->optBlock.i = kSetMulticastTTLOption;   mDNSOptionManagement(m); break;
        case mOT_LLScope:       m->p->optBlock.m = kAddLinkMulticastOption;  mDNSOptionManagement(m); break;
//				case mOT_AdminScope:	m->p->optBlock.m = kAddAdminMulticastOption; mDNSOptionManagement(m); break;
        case mOT_Bind:          OTBind(m->p->ep, (TBind*)&mDNSbindReq, NULL); break;
        case mOT_Ready:         mDNSinitComplete(m, mStatus_NoError);
            // Can't do mDNS_RegisterInterface until *after* mDNSinitComplete has set m->mDNSPlatformStatus to mStatus_NoError
            mDNS_RegisterInterface(m, &m->p->interface, mDNSfalse);
            break;
        default:                LogMsg("Unexpected m->p->mOTstate %d", m->p->mOTstate-1);
        }
        break;

    case T_DATA:
        //debugf("T_DATA");
        while (readpacket(m) == kOTNoError) continue;       // Read packets until we run out
        break;

    case kOTProviderWillClose: LogMsg("kOTProviderWillClose"); break;
    case kOTProviderIsClosed:           // Machine is going to sleep, shutting down, or reconfiguring IP
        LogMsg("kOTProviderIsClosed");
        if (m->p->mOTstate == mOT_Ready)
        {
            m->p->mOTstate = mOT_Closed;
            mDNS_DeregisterInterface(m, &m->p->interface, mDNSfalse);
        }
        if (m->p->ep) { OTCloseProvider(m->p->ep); m->p->ep = NULL; }
        break;                          // Do we need to do anything?

    default: debugf("mDNSNotifier: Unexpected OTEventCode %X", code);
        break;
    }
}

#if MDNS_ONLYSYSTEMTASK

static Boolean ONLYSYSTEMTASKevent;
static void       *ONLYSYSTEMTASKcontextPtr;
static OTEventCode ONLYSYSTEMTASKcode;
static OTResult ONLYSYSTEMTASKresult;
static void       *ONLYSYSTEMTASKcookie;

mDNSlocal pascal void CallmDNSNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie)
{
    ONLYSYSTEMTASKcontextPtr = contextPtr;
    ONLYSYSTEMTASKcode       = code;
    ONLYSYSTEMTASKresult     = result;
    ONLYSYSTEMTASKcookie     = cookie;
}

#else

mDNSlocal pascal void CallmDNSNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie)
{
    mDNS *const m = (mDNS *const)contextPtr;
    if (!m) debugf("mDNSNotifier FATAL ERROR! No context");
    if (m->p->nesting) LogMsg("CallmDNSNotifier ERROR! OTEnterNotifier is supposed to suppress notifier callbacks");
    mDNSNotifier(contextPtr, code, result, cookie);
}

#endif

static OTNotifyUPP CallmDNSNotifierUPP;

mDNSlocal OSStatus mDNSOpenEndpoint(const mDNS *const m)
{
    OSStatus err;
    // m->optReq is pre-set to point to the shared m->optBlock
    // m->optBlock is filled in by each OTOptionManagement call
    m->p->optReq.opt.maxlen = sizeof(m->p->optBlock);
    m->p->optReq.opt.len    = sizeof(m->p->optBlock);
    m->p->optReq.opt.buf    = (UInt8*)&m->p->optBlock;
    m->p->optReq.flags      = T_NEGOTIATE;

    // Open an endpoint and start answering queries
    //printf("Opening endpoint now...\n");
    m->p->ep = NULL;
    m->p->mOTstate = mOT_Start;
    err = OTAsyncOpenEndpoint(OTCreateConfiguration(kUDPName), 0, NULL, CallmDNSNotifierUPP, (void*)m);
    if (err) { LogMsg("ERROR: OTAsyncOpenEndpoint(UDP) failed with error <%d>", err); return(err); }
    return(kOTNoError);
}

// Define these here because they're not in older versions of OpenTransport.h
enum
{
    xOTStackIsLoading   = 0x27000001,   /* Sent before Open Transport attempts to load the TCP/IP protocol stack.*/
    xOTStackWasLoaded   = 0x27000002,   /* Sent after the TCP/IP stack has been successfully loaded.*/
    xOTStackIsUnloading = 0x27000003    /* Sent before Open Transport unloads the TCP/IP stack.*/
};

static mDNS *ClientNotifierContext;

mDNSlocal pascal void ClientNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie)
{
    mDNS *const m = ClientNotifierContext;

    #pragma unused(contextPtr) // Usually zero (except one in the 'xOTStackIsLoading' case)
    #pragma unused(cookie) // Usually 'ipv4' (except for kOTPortNetworkChange)
    #pragma unused(result) // Usually zero

    switch (code)
    {
    case xOTStackIsLoading:     break;
    case xOTStackWasLoaded:     if (m->p->mOTstate == mOT_Closed)
        {
            LogMsg("kOTStackWasLoaded: Re-opening endpoint");
            if (m->p->ep)
                LogMsg("kOTStackWasLoaded: ERROR: m->p->ep already set");
            m->mDNSPlatformStatus = mStatus_Waiting;
            m->p->mOTstate = mOT_Reset;
                                        #if !MDNS_ONLYSYSTEMTASK
            mDNSOpenEndpoint(m);
                                        #endif
        }
        else
            LogMsg("kOTStackWasLoaded (no action)");
        break;
    case xOTStackIsUnloading:   break;
    case kOTPortNetworkChange:  break;
    default: debugf("ClientNotifier unknown code %X, %X, %d", contextPtr, code, result); break;
    }
}

#if TARGET_API_MAC_CARBON

mDNSlocal void GetUserSpecifiedComputerName(domainlabel *const namelabel)
{
    CFStringRef cfs = CSCopyMachineName();
    CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8);
    CFRelease(cfs);
}

#else

mDNSlocal OSStatus ConvertStringHandleToUTF8(const StringHandle machineName, UInt8 *const utf8, ByteCount maxlen)
{
    OSStatus status;
    TextEncoding utf8TextEncoding, SystemTextEncoding;
    UnicodeMapping theMapping;
    TextToUnicodeInfo textToUnicodeInfo;
    ByteCount unicodelen = 0;

    if (maxlen > 255) maxlen = 255; // Can't put more than 255 in a Pascal String

    utf8TextEncoding = CreateTextEncoding(kTextEncodingUnicodeDefault, kTextEncodingDefaultVariant, kUnicodeUTF8Format);
    UpgradeScriptInfoToTextEncoding(smSystemScript, kTextLanguageDontCare, kTextRegionDontCare, NULL, &SystemTextEncoding);
    theMapping.unicodeEncoding = utf8TextEncoding;
    theMapping.otherEncoding   = SystemTextEncoding;
    theMapping.mappingVersion  = kUnicodeUseLatestMapping;
    status = CreateTextToUnicodeInfo(&theMapping, &textToUnicodeInfo);
    if (status == noErr)
    {
        status = ConvertFromPStringToUnicode(textToUnicodeInfo, *machineName, maxlen, &unicodelen, (UniCharArrayPtr)&(utf8[1]));
        DisposeTextToUnicodeInfo(&textToUnicodeInfo);
    }
    utf8[0] = (UInt8)unicodelen;
    return(status);
}

mDNSlocal void GetUserSpecifiedComputerName(domainlabel *const namelabel)
{
    StringHandle machineName = GetString(-16413);   // Get machine name set in file sharing
    if (machineName)
    {
        char machineNameState = HGetState((Handle)machineName);
        HLock((Handle)machineName);
        ConvertStringHandleToUTF8(machineName, namelabel->c, MAX_DOMAIN_LABEL);
        HSetState((Handle)machineName, machineNameState);
    }
}

#endif

static pascal void mDNSTimerTask(void *arg)
{
#if MDNS_ONLYSYSTEMTASK
#pragma unused(arg)
    ONLYSYSTEMTASKevent = true;
#else
    mDNS *const m = (mDNS *const)arg;
    if (!m->p->ep) LogMsg("mDNSTimerTask NO endpoint");
    if (m->mDNS_busy) LogMsg("mDNS_busy");
    if (m->p->nesting) LogMsg("mDNSTimerTask ERROR! OTEnterNotifier is supposed to suppress timer callbacks too");

    // If our timer fires at a time when we have no endpoint, ignore it --
    // once we reopen our endpoint and get our T_BINDCOMPLETE message we'll call
    // mDNS_RegisterInterface(), which does a lock/unlock, which retriggers the timer.
    // Likewise, if m->mDNS_busy or m->p->nesting, we'll catch this on the unlock
    if (m->p->ep && m->mDNS_busy == 0 && m->p->nesting == 0) mDNS_Execute(m);
#endif
}

#if TEST_SLEEP
long sleep, wake, mode;
#endif

mDNSexport mStatus mDNSPlatformInit (mDNS *const m)
{
    OSStatus err = InitOpenTransport();

    ClientNotifierContext = m;
    // Note: OTRegisterAsClient returns kOTNotSupportedErr when running as Carbon code on OS X
    // -- but that's okay, we don't need a ClientNotifier when running as Carbon code on OS X
    OTRegisterAsClient(NULL, NewOTNotifyUPP(ClientNotifier));

    m->p->OTTimerTask = OTCreateTimerTask(NewOTProcessUPP(mDNSTimerTask), m);
    m->p->nesting     = 0;

#if TEST_SLEEP
    sleep = TickCount() + 600;
    wake = TickCount() + 1200;
    mode = 0;
#endif

    // Set up the nice label
    m->nicelabel.c[0] = 0;
    GetUserSpecifiedComputerName(&m->nicelabel);
//	m->nicelabel = *(domainlabel*)"\pStu";	// For conflict testing
    if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Macintosh");

    // Set up the RFC 1034-compliant label
    m->hostlabel.c[0] = 0;
    ConvertUTF8PstringToRFC1034HostLabel(m->nicelabel.c, &m->hostlabel);
    if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Macintosh");

    mDNS_SetFQDN(m);

    // When it's finished mDNSOpenEndpoint asynchronously calls mDNSinitComplete() and then mDNS_RegisterInterface()
    CallmDNSNotifierUPP = NewOTNotifyUPP(CallmDNSNotifier);
    err = mDNSOpenEndpoint(m);
    if (err)
    {
        LogMsg("mDNSOpenEndpoint failed %d", err);
        if (m->p->OTTimerTask) OTDestroyTimerTask(m->p->OTTimerTask);
        OTUnregisterAsClient();
        CloseOpenTransport();
    }
    return(err);
}

extern void mDNSPlatformClose (mDNS *const m)
{
    if (m->p->mOTstate == mOT_Ready)
    {
        m->p->mOTstate = mOT_Closed;
        mDNS_DeregisterInterface(m, &m->p->interface, mDNSfalse);
    }
    if (m->p->ep)          { OTCloseProvider   (m->p->ep);          m->p->ep          = NULL; }
    if (m->p->OTTimerTask) { OTDestroyTimerTask(m->p->OTTimerTask); m->p->OTTimerTask = 0;    }

    OTUnregisterAsClient();
    CloseOpenTransport();
}

#if MDNS_ONLYSYSTEMTASK
extern void mDNSPlatformIdle(mDNS *const m);
mDNSexport void mDNSPlatformIdle(mDNS *const m)
{
    while (ONLYSYSTEMTASKcontextPtr)
    {
        void *contextPtr = ONLYSYSTEMTASKcontextPtr;
        ONLYSYSTEMTASKcontextPtr = NULL;
        mDNSNotifier(contextPtr, ONLYSYSTEMTASKcode, ONLYSYSTEMTASKresult, ONLYSYSTEMTASKcookie);
    }
    if (ONLYSYSTEMTASKevent)
    {
        ONLYSYSTEMTASKevent = false;
        mDNS_Execute(m);
    }

    if (m->p->mOTstate == mOT_Reset)
    {
        printf("\n");
        printf("******************************************************************************\n");
        printf("\n");
        printf("Reopening endpoint\n");
        mDNSOpenEndpoint(m);
        m->ResourceRecords = NULL;
    }

#if TEST_SLEEP
    switch (mode)
    {
    case 0: if ((long)TickCount() - sleep >= 0) { mDNSCoreMachineSleep(m, 1); mode++; }
        break;
    case 1: if ((long)TickCount() - wake >= 0) { mDNSCoreMachineSleep(m, 0); mode++; }
        break;
    }
#endif
}
#endif

mDNSexport void    mDNSPlatformLock(const mDNS *const m)
{
    if (!m) { DebugStr("\pmDNSPlatformLock m NULL!"); return; }
    if (!m->p) { DebugStr("\pmDNSPlatformLock m->p NULL!"); return; }

    // If we try to call OTEnterNotifier and fail because we're already running at
    // Notifier context, then make sure we don't do the matching OTLeaveNotifier() on exit.
    // If we haven't even opened our endpoint yet, then just increment m->p->nesting for the same reason
    if (m->p->mOTstate == mOT_Ready && !m->p->ep) DebugStr("\pmDNSPlatformLock: m->p->mOTstate == mOT_Ready && !m->p->ep");
    if (!m->p->ep || m->p->nesting || OTEnterNotifier(m->p->ep) == false) m->p->nesting++;
}

mDNSlocal void ScheduleNextTimerCallback(const mDNS *const m)
{
    if (m->mDNSPlatformStatus == mStatus_NoError)
    {
        SInt32 interval = m->NextScheduledEvent - mDNS_TimeNow_NoLock(m);
        if      (interval < 1) interval = 1;
        else if (interval > 0x70000000 / 1000) interval = 0x70000000 / mDNSPlatformOneSecond;
        else interval = (interval * 1000 + mDNSPlatformOneSecond-1)/ mDNSPlatformOneSecond;
        OTScheduleTimerTask(m->p->OTTimerTask, (OTTimeout)interval);
    }
}

mDNSexport void    mDNSPlatformUnlock(const mDNS *const m)
{
    if (!m) { DebugStr("\pmDNSPlatformUnlock m NULL!"); return; }
    if (!m->p) { DebugStr("\pmDNSPlatformUnlock m->p NULL!"); return; }

    if (m->p->ep && m->mDNS_busy == 0) ScheduleNextTimerCallback(m);

    if (m->p->nesting) m->p->nesting--;
    else OTLeaveNotifier(m->p->ep);
}

mDNSexport void     mDNSPlatformStrCopy(      void *dst, const void *src)             { OTStrCopy((char*)dst, (char*)src); }
mDNSexport UInt32   mDNSPlatformStrLen (                 const void *src)             { return(OTStrLength((char*)src)); }
mDNSexport void     mDNSPlatformMemCopy(      void *dst, const void *src, UInt32 len) { OTMemcpy(dst, src, len); }
mDNSexport mDNSBool mDNSPlatformMemSame(const void *dst, const void *src, UInt32 len) { return(OTMemcmp(dst, src, len)); }
mDNSexport void     mDNSPlatformMemZero(      void *dst,                  UInt32 len) { OTMemzero(dst, len); }
mDNSexport void *   mDNSPlatformMemAllocate(mDNSu32 len)                              { return(OTAllocMem(len)); }
mDNSexport void     mDNSPlatformMemFree(void *mem)                                    { OTFreeMem(mem); }
mDNSexport mDNSu32  mDNSPlatformRandomSeed(void)                                      { return(TickCount()); }
mDNSexport mStatus  mDNSPlatformTimeInit(void)                                        { return(mStatus_NoError); }
mDNSexport SInt32   mDNSPlatformRawTime()                                             { return((SInt32)TickCount()); }
mDNSexport SInt32 mDNSPlatformOneSecond = 60;

mDNSexport mDNSs32  mDNSPlatformUTC(void)
{
    // Classic Mac OS since Midnight, 1st Jan 1904
    // Standard Unix counts from 1970
    // This value adjusts for the 66 years and 17 leap-days difference
    mDNSu32 SecsSince1904;
    MachineLocation ThisLocation;
    #define TIME_ADJUST (((1970 - 1904) * 365 + 17) * 24 * 60 * 60)
    #define ThisLocationGMTdelta ((ThisLocation.u.gmtDelta << 8) >> 8)
    GetDateTime(&SecsSince1904);
    ReadLocation(&ThisLocation);
    return((mDNSs32)(SecsSince1904 - ThisLocationGMTdelta - TIME_ADJUST));
}