/* -*- 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: mDNSMacOS9.c,v $
Revision 1.46  2006/08/14 23:24:29  cheshire
Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0

Revision 1.45  2006/03/19 02:00:14  cheshire
<rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions

Revision 1.44  2005/09/16 21:06:50  cheshire
Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place

Revision 1.43  2004/12/17 23:37:49  cheshire
<rdar://problem/3485365> Guard against repeating wireless dissociation/re-association
(and other repetitive configuration changes)

Revision 1.42  2004/12/16 20:43:39  cheshire
interfaceinfo.fMask should be interfaceinfo.fNetmask

Revision 1.41  2004/10/16 00:17:00  cheshire
<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check

Revision 1.40  2004/09/27 23:56:27  cheshire
Fix infinite loop where mDNSPlatformUnlock() called mDNS_TimeNow(),
and then mDNS_TimeNow() called mDNSPlatformUnlock()

Revision 1.39  2004/09/21 21:02:54  cheshire
Set up ifname before calling mDNS_RegisterInterface()

Revision 1.38  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.37  2004/09/17 00:19:10  cheshire
For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4

Revision 1.36  2004/09/16 21:59:16  cheshire
For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr

Revision 1.35  2004/09/16 00:24:49  cheshire
<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()

Revision 1.34  2004/09/14 23:42:36  cheshire
<rdar://problem/3801296> Need to seed random number generator from platform-layer data

Revision 1.33  2004/09/14 23:16:31  cheshire
Fix compile error: mDNS_SetFQDNs has been renamed to mDNS_SetFQDN

Revision 1.32  2004/09/14 21:03:16  cheshire
Fix spacing

Revision 1.31  2004/08/14 03:22:42  cheshire
<rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue
Add GetUserSpecifiedDDNSName() routine
Convert ServiceRegDomain to domainname instead of C string
Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs

Revision 1.30  2004/07/29 19:26:03  ksekar
Plaform-level changes for NATPMP support

Revision 1.29  2004/05/26 20:53:16  cheshire
Remove unncecessary "return( -1 );" at the end of mDNSPlatformUTC()

Revision 1.28  2004/05/20 18:39:06  cheshire
Fix build broken by addition of mDNSPlatformUTC requirement

Revision 1.27  2004/04/21 02:49:11  cheshire
To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx'

Revision 1.26  2004/04/09 17:43:03  cheshire
Make sure to set the McastTxRx field so that duplicate suppression works correctly

Revision 1.25  2004/03/15 18:55:38  cheshire
Comment out debugging message

Revision 1.24  2004/03/12 21:30:26  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.23  2004/02/09 23:24:43  cheshire
Need to set TTL 255 to interoperate with peers that check TTL (oops!)

Revision 1.22  2004/01/27 20:15:23  cheshire
<rdar://problem/3541288>: Time to prune obsolete code for listening on port 53

Revision 1.21  2004/01/24 04:59:16  cheshire
Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again

Revision 1.20  2003/11/14 20:59:09  cheshire
Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h.
Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file.

Revision 1.19  2003/08/18 23:09:20  cheshire
<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformRawTime()

Revision 1.18  2003/08/12 19:56:24  cheshire
Update to APSL 2.0

 */

#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  = AllDNSLinkGroupv4;
	#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(c->value, &destaddr.ip.v4, 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 mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID,
										  TCPConnectionCallback callback, void *context, int *descriptor)
	{
	(void)dst;			// Unused
	(void)dstport;		// Unused
	(void)InterfaceID;	// Unused
	(void)callback;		// Unused
	(void)context;		// Unused
	(void)descriptor;	// Unused
	return(mStatus_UnsupportedErr);
	}

mDNSexport void mDNSPlatformTCPCloseConnection(int sd)
	{
	(void)sd;			// Unused
	}

mDNSexport int mDNSPlatformReadTCP(int sd, void *buf, int buflen)
	{
	(void)sd;			// Unused
	(void)buf;			// Unused
	(void)buflen;		// Unused
	return(0);
	}

mDNSexport int mDNSPlatformWriteTCP(int sd, const char *msg, int len)
	{
	(void)sd;			// Unused
	(void)msg;			// Unused
	(void)len;			// Unused
	return(0);
	}

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(const void *src,       void *dst)             { OTStrCopy((char*)dst, (char*)src); }
mDNSexport UInt32   mDNSPlatformStrLen (const void *src)                              { return(OTStrLength((char*)src)); }
mDNSexport void     mDNSPlatformMemCopy(const void *src,       void *dst, UInt32 len) { OTMemcpy(dst, src, len); }
mDNSexport mDNSBool mDNSPlatformMemSame(const void *src, const void *dst, 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));
	}
