#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
static const TSetBooleanOption kReusePortOption =
{ kOTBooleanOptionSize, INET_IP, IP_REUSEPORT, 0, true };
static const TSetByteOption kRcvDestAddrOption =
{ kOTOneByteOptionSize, 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 } };
typedef struct { OTAddressType fAddressType; mDNSIPPort fPort; mDNSv4Addr fHost; UInt8 fUnused[8]; } mDNSInetAddress;
#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 };
mDNSlocal void SafeDebugStr(unsigned char *buffer)
{
int i;
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
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)
{
#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
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; (void)dstport; (void)InterfaceID; (void)callback; (void)context; (void)descriptor; return(mStatus_UnsupportedErr);
}
mDNSexport void mDNSPlatformTCPCloseConnection(int sd)
{
(void)sd; }
mDNSexport int mDNSPlatformReadTCP(int sd, void *buf, int buflen)
{
(void)sd; (void)buf; (void)buflen; return(0);
}
mDNSexport int mDNSPlatformWriteTCP(int sd, const char *msg, int len)
{
(void)sd; (void)msg; (void)len; return(0);
}
mDNSlocal void mDNSOptionManagement(mDNS *const m)
{
OSStatus err;
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; }
m->p->ep = (EndpointRef)cookie;
err = OTInetGetInterfaceInfo(&interfaceinfo, kDefaultInetInterface);
if (err) { LogMsg("OTInetGetInterfaceInfo failed %d", err); mDNSinitComplete(m, err); return; }
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:
#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; }
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_Bind: OTBind(m->p->ep, (TBind*)&mDNSbindReq, NULL); break;
case mOT_Ready: mDNSinitComplete(m, mStatus_NoError);
mDNS_RegisterInterface(m, &m->p->interface, 0);
break;
default: LogMsg("Unexpected m->p->mOTstate %d", m->p->mOTstate-1);
}
break;
case T_DATA:
while (readpacket(m) == kOTNoError) continue; break;
case kOTProviderWillClose: LogMsg("kOTProviderWillClose"); break;
case kOTProviderIsClosed: LogMsg("kOTProviderIsClosed");
if (m->p->mOTstate == mOT_Ready)
{
m->p->mOTstate = mOT_Closed;
mDNS_DeregisterInterface(m, &m->p->interface);
}
if (m->p->ep) { OTCloseProvider(m->p->ep); m->p->ep = NULL; }
break;
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->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;
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);
}
enum
{
xOTStackIsLoading = 0x27000001,
xOTStackWasLoaded = 0x27000002,
xOTStackIsUnloading = 0x27000003
};
static mDNS *ClientNotifierContext;
mDNSlocal pascal void ClientNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie)
{
mDNS *const m = ClientNotifierContext;
#pragma unused(contextPtr) #pragma unused(cookie) #pragma unused(result)
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;
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); 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 (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;
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
m->nicelabel.c[0] = 0;
GetUserSpecifiedComputerName(&m->nicelabel);
if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Macintosh");
m->hostlabel.c[0] = 0;
ConvertUTF8PstringToRFC1034HostLabel(m->nicelabel.c, &m->hostlabel);
if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Macintosh");
mDNS_SetFQDN(m);
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);
}
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 (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 - (mDNSPlatformRawTime() + m->timenow_adjust);
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)
{
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));
}