pr/src/md/mac/macsockotpt.c
author cvs2hg
Fri, 03 Aug 2001 02:35:52 +0000
branchBookmarksOutliner_20010813_BRANCH
changeset 2004 e3b9fbe16b6f
parent 1204 e4d8a15c5115
permissions -rw-r--r--
fixup commit for branch 'BookmarksOutliner_20010813_BRANCH'

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * The contents of this file are subject to the Netscape Public License
 * Version 1.1 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 * 
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 * 
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/* This turns on UNIX style errors in OT 1.1 headers */
#define OTUNIXERRORS 1

#include <string.h>

#include <Gestalt.h>
#include <OpenTransport.h>
#include <OSUtils.h>

#define GESTALT_OPEN_TPT_PRESENT        gestaltOpenTptPresentMask
#define GESTALT_OPEN_TPT_TCP_PRESENT    gestaltOpenTptTCPPresentMask

#include <OpenTptInternet.h>    // All the internet typedefs

#if (UNIVERSAL_INTERFACES_VERSION >= 0x0330)
// for some reason Apple removed this typedef.
typedef struct OTConfiguration	OTConfiguration;
#endif

#include "primpl.h"

typedef enum SndRcvOpCode {
    kSTREAM_SEND,
    kSTREAM_RECEIVE,
    kDGRAM_SEND,
    kDGRAM_RECEIVE
} SndRcvOpCode;

static struct {
	PRLock *    lock;
	InetSvcRef  serviceRef;
	PRThread *  thread;
	void *      cookie;
} dnsContext;


static pascal void  DNSNotifierRoutine(void * contextPtr, OTEventCode code, OTResult result, void * cookie);
static pascal void  NotifierRoutine(void * contextPtr, OTEventCode code, OTResult result, void * cookie);
static pascal void  RawEndpointNotifierRoutine(void * contextPtr, OTEventCode code, OTResult result, void * cookie);

static PRBool GetState(PRFileDesc *fd, PRBool *readReady, PRBool *writeReady, PRBool *exceptReady);

void
WakeUpNotifiedThread(PRThread *thread, OTResult result);

extern void WaitOnThisThread(PRThread *thread, PRIntervalTime timeout);
extern void DoneWaitingOnThisThread(PRThread *thread);

#if TARGET_CARBON
OTClientContextPtr  clientContext = NULL;

#define INIT_OPEN_TRANSPORT()	InitOpenTransportInContext(kInitOTForExtensionMask, &clientContext)
#define OT_OPEN_INTERNET_SERVICES(config, flags, err)	OTOpenInternetServicesInContext(config, flags, err, clientContext)
#define OT_OPEN_ENDPOINT(config, flags, info, err)		OTOpenEndpointInContext(config, flags, info, err, clientContext)

#else

#define INIT_OPEN_TRANSPORT()	InitOpenTransport()
#define OT_OPEN_INTERNET_SERVICES(config, flags, err)	OTOpenInternetServices(config, flags, err)
#define OT_OPEN_ENDPOINT(config, flags, info, err)		OTOpenEndpoint(config, flags, info, err)
#endif /* TARGET_CARBON */

static OTNotifyUPP	DNSNotifierRoutineUPP;
static OTNotifyUPP NotifierRoutineUPP;
static OTNotifyUPP RawEndpointNotifierRoutineUPP;

void _MD_InitNetAccess()
{
    OSErr       err;
    OSStatus    errOT;
    PRBool      hasOTTCPIP = PR_FALSE;
    PRBool      hasOT = PR_FALSE;
    long        gestaltResult;

    err = Gestalt(gestaltOpenTpt, &gestaltResult);
    if (err == noErr)
        if (gestaltResult & GESTALT_OPEN_TPT_PRESENT)
            hasOT = PR_TRUE;
    
    if (hasOT)
        if (gestaltResult & GESTALT_OPEN_TPT_TCP_PRESENT)
            hasOTTCPIP = PR_TRUE;
        
    PR_ASSERT(hasOTTCPIP == PR_TRUE);

    DNSNotifierRoutineUPP	=  NewOTNotifyUPP(DNSNotifierRoutine);
    NotifierRoutineUPP		=  NewOTNotifyUPP(NotifierRoutine);
    RawEndpointNotifierRoutineUPP = NewOTNotifyUPP(RawEndpointNotifierRoutine);

    errOT = INIT_OPEN_TRANSPORT();
    PR_ASSERT(err == kOTNoError);

	dnsContext.serviceRef = NULL;
	dnsContext.lock = PR_NewLock();
	PR_ASSERT(dnsContext.lock != NULL);

	dnsContext.thread = _PR_MD_CURRENT_THREAD();
	dnsContext.cookie = NULL;
	
/* XXX Does not handle absence of open tpt and tcp yet! */
}

static void _MD_FinishInitNetAccess()
{
    OSStatus    errOT;

	if (dnsContext.serviceRef)
		return;
		
    dnsContext.serviceRef = OT_OPEN_INTERNET_SERVICES(kDefaultInternetServicesPath, NULL, &errOT);
    if (errOT != kOTNoError) {
        dnsContext.serviceRef = NULL;
        return;    /* no network -- oh well */
    }
    
    PR_ASSERT((dnsContext.serviceRef != NULL) && (errOT == kOTNoError));

    /* Install notify function for DNR Address To String completion */
    errOT = OTInstallNotifier(dnsContext.serviceRef, DNSNotifierRoutineUPP, &dnsContext);
    PR_ASSERT(errOT == kOTNoError);

    /* Put us into async mode */
    errOT = OTSetAsynchronous(dnsContext.serviceRef);
    PR_ASSERT(errOT == kOTNoError);
}


static pascal void  DNSNotifierRoutine(void * contextPtr, OTEventCode otEvent, OTResult result, void * cookie)
{
#pragma unused(contextPtr)
    _PRCPU *    cpu    = _PR_MD_CURRENT_CPU(); 
	OSStatus    errOT;

		dnsContext.thread->md.osErrCode = result;
		dnsContext.cookie = cookie;
	
	switch (otEvent) {
		case T_DNRSTRINGTOADDRCOMPLETE:
				if (_PR_MD_GET_INTSOFF()) {
					cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
					dnsContext.thread->md.missedIONotify = PR_TRUE;
					return;
				}
				DoneWaitingOnThisThread(dnsContext.thread);
				break;
		
        case kOTProviderWillClose:
                errOT = OTSetSynchronous(dnsContext.serviceRef);
                // fall through to kOTProviderIsClosed case
		
        case kOTProviderIsClosed:
                errOT = OTCloseProvider((ProviderRef)dnsContext.serviceRef);
                dnsContext.serviceRef = nil;

				if (_PR_MD_GET_INTSOFF()) {
					cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
					dnsContext.thread->md.missedIONotify = PR_TRUE;
					return;
				}
				DoneWaitingOnThisThread(dnsContext.thread);
                break;

        default: // or else we don't handle the event
	            PR_ASSERT(otEvent==NULL);
		
	}
	// or else we don't handle the event
}


static void macsock_map_error(OSStatus err)
{
    _PR_MD_CURRENT_THREAD()->md.osErrCode = err;

    if (IsEError(err) || (err >= EPERM && err <= ELASTERRNO)) {
    switch (IsEError(err) ? OSStatus2E(err) : err) {
        case EBADF:
            PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
            break;
        case EADDRNOTAVAIL:
            PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err);
            break;
        case EINPROGRESS:
            PR_SetError(PR_IN_PROGRESS_ERROR, err);
            break;
        case EWOULDBLOCK:
        case EAGAIN:
            PR_SetError(PR_WOULD_BLOCK_ERROR, err);
            break;
        case ENOTSOCK:
            PR_SetError(PR_NOT_SOCKET_ERROR, err);
            break;
        case ETIMEDOUT:
            PR_SetError(PR_IO_TIMEOUT_ERROR, err);
            break;
        case ECONNREFUSED:
            PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
            break;
        case ENETUNREACH:
            PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, err);
            break;
        case EADDRINUSE:
            PR_SetError(PR_ADDRESS_IN_USE_ERROR, err);
            break;
        case EFAULT:
            PR_SetError(PR_ACCESS_FAULT_ERROR, err);
            break;
        case EINTR:
            PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
            break;
        case EINVAL:
            PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
            break;
        case EIO:
            PR_SetError(PR_IO_ERROR, err);
            break;
        case ENOENT:
            PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
            break;
        case ENXIO:
            PR_SetError(PR_IO_ERROR, err);
            break;
        case EPROTOTYPE:
            PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err);
            break;
        case EOPNOTSUPP:
            PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, err);
            break;
        default:
            PR_SetError(PR_UNKNOWN_ERROR, err);
            break;
        }
    } else {
    PR_ASSERT(IsXTIError(err));
    switch (err) {
        case kOTNoDataErr:
        case kOTFlowErr:
            PR_SetError(PR_WOULD_BLOCK_ERROR, err);
            break;
        default:
            PR_SetError(PR_UNKNOWN_ERROR, err);
            break;
        }
    }
}

static void PrepareForAsyncCompletion(PRThread * thread, PRInt32 osfd)
{
    thread->io_pending       = PR_TRUE;
    thread->io_fd            = osfd;
    thread->md.osErrCode     = noErr;
}


void
WakeUpNotifiedThread(PRThread *thread, OTResult result)
{
    _PRCPU *      cpu      = _PR_MD_CURRENT_CPU(); 

	if (thread) {
		thread->md.osErrCode = result;
		if (_PR_MD_GET_INTSOFF()) {
			cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
			thread->md.missedIONotify = PR_TRUE;
			return;
		}
		DoneWaitingOnThisThread(thread);
	}
}

// Notification routine
// Async callback routine.
// A5 is OK. Cannot allocate memory here
static pascal void  NotifierRoutine(void * contextPtr, OTEventCode code, OTResult result, void * cookie)
{
	PRFilePrivate *secret  = (PRFilePrivate *) contextPtr;
	_MDFileDesc * md       = &(secret->md);
	EndpointRef   endpoint = (EndpointRef)secret->md.osfd;
    PRThread *    thread   = NULL;
    PRThread *	  pollThread = md->poll.thread;
	OSStatus      err;
	OTResult	  resultOT;
    TDiscon		  discon;

    switch (code)
    {
// OTLook Events - 
        case T_LISTEN:        // A connection request is available
        	// If md->doListen is true, then PR_Listen has been
        	// called on this endpoint; therefore, we're ready to
        	// accept connections. But we'll do that with PR_Accept
        	// (which calls OTListen, OTAccept, etc) instead of 
        	// doing it here. 
        	if (md->doListen) {
				thread = secret->md.misc.thread;
	            secret->md.misc.thread    = NULL;
	            secret->md.misc.cookie    = cookie;
	            break;
        	} else {
        		// Reject the connection, we're not listening
        		OTSndDisconnect(endpoint, NULL);
        	}
            break;
			
        case T_CONNECT:      // Confirmation of a connect request
			// cookie = sndCall parameter from OTConnect()
            err = OTRcvConnect(endpoint, NULL);
            PR_ASSERT(err == kOTNoError);

			// wake up waiting thread, if any
            thread = secret->md.write.thread;
            secret->md.write.thread    = NULL;
            secret->md.write.cookie    = cookie;
            break;

        case T_DATA:        // Standard data is available
			// Mark this socket as readable.
			secret->md.readReady = PR_TRUE;

			// wake up waiting thread, if any
            thread = secret->md.read.thread;
            secret->md.read.thread    = NULL;
            secret->md.read.cookie    = cookie;
			break;

        case T_EXDATA:      // Expedited data is available
            PR_ASSERT(!"T_EXDATA Not implemented");
			return;

        case T_DISCONNECT:  // A disconnect is available
            discon.udata.len = 0;
            err = OTRcvDisconnect(endpoint, &discon);
            PR_ASSERT(err == kOTNoError);
            secret->md.exceptReady     = PR_TRUE;

			// wake up waiting threads, if any
			result = -3199 - discon.reason; // obtain the negative error code

            if ((thread = secret->md.read.thread) != NULL) {
		        secret->md.read.thread    = NULL;
    	        secret->md.read.cookie    = cookie;
            	WakeUpNotifiedThread(thread, result);
    	    }
            
            if ((thread = secret->md.write.thread) != NULL) {
	            secret->md.write.thread    = NULL;
	            secret->md.write.cookie    = cookie;
	            WakeUpNotifiedThread(thread, result);
	        }
	        
	        thread = NULL; // already took care of notification here
            break;
		
        case T_ERROR:       // obsolete/unused in library
            PR_ASSERT(!"T_ERROR Not implemented");
			return;		
		
        case T_UDERR:       // UDP Send error; clear the error
			(void) OTRcvUDErr((EndpointRef) cookie, NULL);
            break;

        case T_ORDREL:      // An orderly release is available
            err = OTRcvOrderlyDisconnect(endpoint);
            PR_ASSERT(err == kOTNoError);
            secret->md.readReady      = PR_TRUE;   // mark readable (to emulate bsd sockets)
            // remember connection is closed, so we can return 0 on read or receive
			secret->md.orderlyDisconnect = PR_TRUE;
	
            thread = secret->md.read.thread;
	        secret->md.read.thread    = NULL;
	        secret->md.read.cookie    = cookie;

            break;		

        case T_GODATA:   // Flow control lifted on standard data
            secret->md.writeReady = PR_TRUE;
			resultOT = OTLook(endpoint);		// clear T_GODATA event
			PR_ASSERT(resultOT == T_GODATA);
			
			// wake up waiting thread, if any
            thread = secret->md.write.thread;
            secret->md.write.thread    = NULL;
            secret->md.write.cookie    = cookie;
            break;

        case T_GOEXDATA: // Flow control lifted on expedited data
            PR_ASSERT(!"T_GOEXDATA Not implemented");
			return;

        case T_REQUEST:  // An Incoming request is available
            PR_ASSERT(!"T_REQUEST Not implemented");
            return;

        case T_REPLY:    // An Incoming reply is available
            PR_ASSERT(!"T_REPLY Not implemented");
            return;

        case T_PASSCON:  // State is now T_DATAXFER
			// OTAccept() complete, receiving endpoint in T_DATAXFER state
			// cookie = OTAccept() resRef parameter
			break;

        case T_RESET:    // Protocol has been reset
            PR_ASSERT(!"T_RESET Not implemented");
			return;
            
// Async Completion Events
        case T_BINDCOMPLETE:
        case T_UNBINDCOMPLETE:
        case T_ACCEPTCOMPLETE:
        case T_OPTMGMTCOMPLETE:
        case T_GETPROTADDRCOMPLETE:
			thread = secret->md.misc.thread;
            secret->md.misc.thread    = NULL;
            secret->md.misc.cookie    = cookie;
            break;

//      case T_OPENCOMPLETE:			// we open endpoints in synchronous mode
//      case T_REPLYCOMPLETE:
//      case T_DISCONNECTCOMPLETE:		// we don't call OTSndDisconnect()
//      case T_RESOLVEADDRCOMPLETE:
//      case T_GETINFOCOMPLETE:
//      case T_SYNCCOMPLETE:
//      case T_MEMORYRELEASED:			// only if OTAckSends() called on endpoint
//      case T_REGNAMECOMPLETE:
//      case T_DELNAMECOMPLETE:
//      case T_LKUPNAMECOMPLETE:
//      case T_LKUPNAMERESULT:
		// OpenTptInternet.h
//      case T_DNRSTRINGTOADDRCOMPLETE:	// DNS is handled by dnsContext in DNSNotifierRoutine()
//      case T_DNRADDRTONAMECOMPLETE:
//      case T_DNRSYSINFOCOMPLETE:
//      case T_DNRMAILEXCHANGECOMPLETE:
//      case T_DNRQUERYCOMPLETE:
        default:
        	// we should probably have a bit more sophisticated handling of kOTSystemSleep, etc.
			// PR_ASSERT(code != 0);
            return;
    }

	if (pollThread)
		WakeUpNotifiedThread(pollThread, kOTNoError);

	if (thread && (thread != pollThread))
		WakeUpNotifiedThread(thread, result);
}


static OSErr CreateSocket(int type, EndpointRef *endpoint)
{
    OSStatus err;
    PRThread *me = _PR_MD_CURRENT_THREAD();
    char *  configName;
    OTConfiguration *config;
    EndpointRef ep;

	// for now we just create the endpoint
	// we'll make it asynchronous and give it a notifier routine in _MD_makenonblock()

    switch (type){
        case SOCK_STREAM:   configName = kTCPName;  break;
        case SOCK_DGRAM:    configName = kUDPName;  break;
    }
    config = OTCreateConfiguration(configName);
    ep = OT_OPEN_ENDPOINT(config, 0, NULL, &err);
    if (err != kOTNoError)
        goto ErrorExit;

    *endpoint = ep;
    PR_ASSERT(*endpoint != NULL);

    return kOTNoError;

ErrorExit:
    return err;
}


// Errors returned:
// kOTXXXX - OT returned error
// EPROTONOSUPPORT - bad socket type/protocol
// ENOBUFS - not enough space for another socket, or failure in socket creation routine
PRInt32 _MD_socket(int domain, int type, int protocol)
{
    OSStatus    err;
    EndpointRef endpoint;
    
    	_MD_FinishInitNetAccess();

    // We only deal with internet domain
    if (domain != AF_INET) {
        err = kEPROTONOSUPPORTErr;
        goto ErrorExit;
    }
    
    // We only know about tcp & udp
    if ((type != SOCK_STREAM) && (type != SOCK_DGRAM)) {
        err = kEPROTONOSUPPORTErr;
        goto ErrorExit;
    }
    
    // Convert default types to specific types.
    if (protocol == 0)  {
        if (type == SOCK_DGRAM)
            protocol = IPPROTO_UDP;
        else if (type == SOCK_STREAM)
            protocol = IPPROTO_TCP;
    }
    
    // Only support default protocol for tcp
    if ((type == SOCK_STREAM)  && (protocol != IPPROTO_TCP)) {
        err = kEPROTONOSUPPORTErr;
        goto ErrorExit;
    }
                
    // Only support default protocol for udp
    if ((type == SOCK_DGRAM)  && (protocol != IPPROTO_UDP)) {
        err = kEPROTONOSUPPORTErr;
        goto ErrorExit;
    }
        
    // Create a socket, we might run out of memory
    err = CreateSocket(type, &endpoint);
    if (err != kOTNoError)
        goto ErrorExit;

    PR_ASSERT((PRInt32)endpoint != -1);

    return ((PRInt32)endpoint);

ErrorExit:
    macsock_map_error(err);
    return -1;
}


// Errors:
// EBADF  -- bad socket id
// EFAULT -- bad address format
PRInt32 _MD_bind(PRFileDesc *fd, PRNetAddr *addr, PRUint32 addrlen)
{
    OSStatus err;
    EndpointRef endpoint = (EndpointRef) fd->secret->md.osfd;
    TBind bindReq;
    PRThread *me = _PR_MD_CURRENT_THREAD();
    PRUint32 retryCount = 0;

    if (endpoint == NULL) {
        err = kEBADFErr;
        goto ErrorExit;
    }
        
    if (addr == NULL) {
        err = kEFAULTErr;
        goto ErrorExit;
    }
        
/*
 * There seems to be a bug with OT related to OTBind failing with kOTNoAddressErr even though
 * a proper legal address was supplied.  This happens very rarely and just retrying the
 * operation after a certain time (less than 1 sec. does not work) seems to succeed.
 */

TryAgain:
    // setup our request
    bindReq.addr.len = addrlen;
        
    bindReq.addr.maxlen = addrlen;
    bindReq.addr.buf = (UInt8*) addr;
    bindReq.qlen = 1;

	PR_Lock(fd->secret->md.miscLock);
    PrepareForAsyncCompletion(me, fd->secret->md.osfd);
	fd->secret->md.misc.thread = me;

    err = OTBind(endpoint, &bindReq, NULL);
    if (err != kOTNoError) {
	    me->io_pending = PR_FALSE;
	    PR_Unlock(fd->secret->md.miscLock);
        goto ErrorExit;
	}

    WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
	PR_Unlock(fd->secret->md.miscLock);

    err = me->md.osErrCode;
    if (err != kOTNoError)
        goto ErrorExit;

    return kOTNoError;

ErrorExit:
    if ((err == kOTNoAddressErr) && (++retryCount <= 4)) {
        unsigned long finalTicks;
    
        Delay(100,&finalTicks);
        goto TryAgain;
    }
    macsock_map_error(err);
    return -1;
}


// Errors:
// EBADF -- bad socket id
PRInt32 _MD_listen(PRFileDesc *fd, PRIntn backlog)
{
    PRInt32 osfd = fd->secret->md.osfd;
    OSStatus err = 0;
    EndpointRef endpoint = (EndpointRef) osfd;
    TBind bindReq;
    PRNetAddr addr;
    PRThread *me = _PR_MD_CURRENT_THREAD();

	if ((fd == NULL) || (endpoint == NULL)) {
		err = EBADF;
		goto ErrorExit;
	}

    if (backlog == 0)
        backlog = 1;

    if (endpoint == NULL) {
        err = EBADF;
        goto ErrorExit;
    }
        
    addr.inet.family = AF_INET;
    addr.inet.port = addr.inet.ip = 0;

    bindReq.addr.maxlen = PR_NETADDR_SIZE (&addr);
    bindReq.addr.len = 0;
    bindReq.addr.buf = (UInt8*) &addr;
    bindReq.qlen = 0;
    
    PrepareForAsyncCompletion(me, fd->secret->md.osfd);    
	fd->secret->md.misc.thread = me; // tell notifier routine what to wake up

    err = OTGetProtAddress(endpoint, &bindReq, NULL);
    if (err != kOTNoError)
        goto ErrorExit;

    WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);

    err = me->md.osErrCode;
    if (err != kOTNoError)
        goto ErrorExit;

    PrepareForAsyncCompletion(me, fd->secret->md.osfd);    
	fd->secret->md.misc.thread = me; // tell notifier routine what to wake up

    err = OTUnbind(endpoint);
    if (err != kOTNoError)
        goto ErrorExit;

    WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);

    err = me->md.osErrCode;
    if (err != kOTNoError)
        goto ErrorExit;

	/* tell the notifier func that we are interested in pending connections */
	fd->secret->md.doListen = PR_TRUE;
	/* accept up to (backlog) pending connections at any one time */
    bindReq.qlen = backlog;
    
    PrepareForAsyncCompletion(me, fd->secret->md.osfd);    
	fd->secret->md.misc.thread = me; // tell notifier routine what to wake up

    err = OTBind(endpoint, &bindReq, NULL);
    if (err != kOTNoError)
        goto ErrorExit;

    WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);

    err = me->md.osErrCode;
    if (err != kOTNoError)
    {
    	// If OTBind failed, we're really not ready to listen after all.
		fd->secret->md.doListen = PR_FALSE;
        goto ErrorExit;
    }

    return kOTNoError;

ErrorExit:
	me->io_pending = PR_FALSE; // clear pending wait state if any
    macsock_map_error(err);
    return -1;
}


// Errors:
// EBADF -- bad socket id
PRInt32 _MD_getsockname(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen)
{
    OSStatus err;
    EndpointRef endpoint = (EndpointRef) fd->secret->md.osfd;
    TBind bindReq;
    PRThread *me = _PR_MD_CURRENT_THREAD();

    if (endpoint == NULL) {
        err = kEBADFErr;
        goto ErrorExit;
    }
        
    if (addr == NULL) {
        err = kEFAULTErr;
        goto ErrorExit;
    }

    bindReq.addr.len = *addrlen;
    bindReq.addr.maxlen = *addrlen;
    bindReq.addr.buf = (UInt8*) addr;
    bindReq.qlen = 0;
    
	PR_Lock(fd->secret->md.miscLock);
    PrepareForAsyncCompletion(me, fd->secret->md.osfd);    
	fd->secret->md.misc.thread = me;

    err = OTGetProtAddress(endpoint, &bindReq, NULL);
    if (err != kOTNoError) {
	    me->io_pending = PR_FALSE;
	    PR_Unlock(fd->secret->md.miscLock);
        goto ErrorExit;
	}

    WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
	PR_Unlock(fd->secret->md.miscLock);

    err = me->md.osErrCode;
    if (err != kOTNoError)
        goto ErrorExit;

    *addrlen = PR_NETADDR_SIZE(addr);
    return kOTNoError;

ErrorExit:
    macsock_map_error(err);
    return -1;
}


PRStatus _MD_getsockopt(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
{
    OSStatus err;
    EndpointRef endpoint = (EndpointRef) fd->secret->md.osfd;
    TOptMgmt cmd;
    TOption *opt;
    PRThread *me = _PR_MD_CURRENT_THREAD();
    unsigned char optionBuffer[kOTOptionHeaderSize + sizeof(PRSocketOptionData)];
    
    if (endpoint == NULL) {
        err = kEBADFErr;
        goto ErrorExit;
    }
    
    /* 
    OT wants IPPROTO_IP for level and not XTI_GENERIC.  SO_REUSEADDR and SO_KEEPALIVE 
    are equated to IP level and TCP level options respectively and hence we need to set 
    the level correctly.
    */
    if (level == SOL_SOCKET) {
        if (optname == SO_REUSEADDR)
            level = IPPROTO_IP;
        else if (optname == SO_KEEPALIVE)
            level = INET_TCP;
    }

    opt = (TOption *)&optionBuffer[0];
    opt->len = sizeof(TOption);
    opt->level = level;
    opt->name = optname;
    opt->status = 0;
    
    cmd.opt.len = sizeof(TOption);
    cmd.opt.maxlen = sizeof(optionBuffer);
    cmd.opt.buf = (UInt8*)optionBuffer;
    cmd.flags = T_CURRENT;

	PR_Lock(fd->secret->md.miscLock);
    PrepareForAsyncCompletion(me, fd->secret->md.osfd);    
	fd->secret->md.misc.thread = me;

    err = OTOptionManagement(endpoint, &cmd, &cmd);
    if (err != kOTNoError) {
	    me->io_pending = PR_FALSE;
	    PR_Unlock(fd->secret->md.miscLock);
        goto ErrorExit;
	}

    WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
	PR_Unlock(fd->secret->md.miscLock);

    err = me->md.osErrCode;
    if (err != kOTNoError)
        goto ErrorExit;

    if (opt->status == T_FAILURE || opt->status == T_NOTSUPPORT){
        err = kEOPNOTSUPPErr;
        goto ErrorExit;
    }

    PR_ASSERT(opt->status == T_SUCCESS);

    switch (optname) {
        case SO_LINGER:
            *((t_linger*)optval) = *((t_linger*)&opt->value);
            *optlen = sizeof(t_linger);
            break;
        case SO_REUSEADDR:
        case TCP_NODELAY:
        case SO_KEEPALIVE:
        case SO_RCVBUF:
        case SO_SNDBUF:
            *((PRIntn*)optval) = *((PRIntn*)&opt->value);
            *optlen = sizeof(PRIntn);
            break;
        case IP_MULTICAST_LOOP:
            *((PRUint8*)optval) = *((PRIntn*)&opt->value);
            *optlen = sizeof(PRUint8);
            break;
        case IP_TTL:
            *((PRUintn*)optval) = *((PRUint8*)&opt->value);
            *optlen = sizeof(PRUintn);
            break;
        case IP_MULTICAST_TTL:
            *((PRUint8*)optval) = *((PRUint8*)&opt->value);
            *optlen = sizeof(PRUint8);
            break;
        case IP_ADD_MEMBERSHIP:
        case IP_DROP_MEMBERSHIP:
            {
            /* struct ip_mreq and TIPAddMulticast are the same size and optval 
               is pointing to struct ip_mreq */
            *((struct ip_mreq *)optval) = *((struct ip_mreq *)&opt->value);
            *optlen = sizeof(struct ip_mreq);
            break;
            }
        case IP_MULTICAST_IF:
            {
            *((PRUint32*)optval) = *((PRUint32*)&opt->value);
            *optlen = sizeof(PRUint32);
            break;
            }
        /*case IP_TOS:*/ /*IP_TOS has same value as TCP_MAXSEG */
        case TCP_MAXSEG:
            if (level == IPPROTO_TCP) { /* it is TCP_MAXSEG */
                *((PRIntn*)optval) = *((PRIntn*)&opt->value);
                *optlen = sizeof(PRIntn);
            } else { /* it is IP_TOS */
                *((PRUintn*)optval) = *((PRUint8*)&opt->value);
                *optlen = sizeof(PRUintn);
            }
            break;
        default:
            PR_ASSERT(0);
            break;    
    }
    
    return PR_SUCCESS;

ErrorExit:
    macsock_map_error(err);
    return PR_FAILURE;
}


PRStatus _MD_setsockopt(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
{
    OSStatus err;
    EndpointRef endpoint = (EndpointRef) fd->secret->md.osfd;
    TOptMgmt cmd;
    TOption *opt;
    PRThread *me = _PR_MD_CURRENT_THREAD();
    unsigned char optionBuffer[kOTOptionHeaderSize + sizeof(PRSocketOptionData) + 1];
    
    if (endpoint == NULL) {
        err = kEBADFErr;
        goto ErrorExit;
    }
    
    /* 
    OT wants IPPROTO_IP for level and not XTI_GENERIC.  SO_REUSEADDR and SO_KEEPALIVE 
    are equated to IP level and TCP level options respectively and hence we need to set 
    the level correctly.
    */
    if (level == SOL_SOCKET) {
        if (optname == SO_REUSEADDR)
            level = IPPROTO_IP;
        else if (optname == SO_KEEPALIVE)
            level = INET_TCP;
    }

    opt = (TOption *)&optionBuffer[0];
    opt->len = kOTOptionHeaderSize + optlen;

    /* special case adjustments for length follow */
    if (optname == SO_KEEPALIVE) /* we need to pass the timeout value for OT */
        opt->len = kOTOptionHeaderSize + sizeof(t_kpalive);
    if (optname == IP_MULTICAST_TTL || optname == IP_TTL) /* it is an unsigned char value */
        opt->len = kOTOneByteOptionSize;
    if (optname == IP_TOS && level == IPPROTO_IP)
        opt->len = kOTOneByteOptionSize;

    opt->level = level;
    opt->name = optname;
    opt->status = 0;
    
    cmd.opt.len = opt->len;
    cmd.opt.maxlen = sizeof(optionBuffer);
    cmd.opt.buf = (UInt8*)optionBuffer;
    
    optionBuffer[opt->len] = 0;
    
    cmd.flags = T_NEGOTIATE;

    switch (optname) {
        case SO_LINGER:
            *((t_linger*)&opt->value) = *((t_linger*)optval);
            break;
        case SO_REUSEADDR:
        case TCP_NODELAY:
        case SO_RCVBUF:
        case SO_SNDBUF:
            *((PRIntn*)&opt->value) = *((PRIntn*)optval);
            break;
        case IP_MULTICAST_LOOP:
            if (*optval != 0)
                opt->value[0] = T_YES;
            else
                opt->value[0] = T_NO;
            break;
        case SO_KEEPALIVE:
            {
            t_kpalive *kpalive = (t_kpalive *)&opt->value;
            
            kpalive->kp_onoff = *((long*)optval);
            kpalive->kp_timeout = 10; /* timeout in minutes */
            break;
            }
        case IP_TTL:
            *((unsigned char*)&opt->value) = *((PRUintn*)optval);
            break;
        case IP_MULTICAST_TTL:
            *((unsigned char*)&opt->value) = *optval;
            break;
        case IP_ADD_MEMBERSHIP:
        case IP_DROP_MEMBERSHIP:
            {
            /* struct ip_mreq and TIPAddMulticast are the same size and optval 
               is pointing to struct ip_mreq */
            *((TIPAddMulticast *)&opt->value) = *((TIPAddMulticast *)optval);
            break;
            }
        case IP_MULTICAST_IF:
            {
            *((PRUint32*)&opt->value) = *((PRUint32*)optval);
            break;
            }
        /*case IP_TOS:*/ /*IP_TOS has same value as TCP_MAXSEG */
        case TCP_MAXSEG:
            if (level == IPPROTO_TCP) { /* it is TCP_MAXSEG */
                *((PRIntn*)&opt->value) = *((PRIntn*)optval);
            } else { /* it is IP_TOS */
                *((unsigned char*)&opt->value) = *((PRUintn*)optval);
            }
            break;
        default:
            PR_ASSERT(0);
            break;    
    }
    
	PR_Lock(fd->secret->md.miscLock);
    PrepareForAsyncCompletion(me, fd->secret->md.osfd);    
	fd->secret->md.misc.thread = me;

    err = OTOptionManagement(endpoint, &cmd, &cmd);
    if (err != kOTNoError) {
	    me->io_pending = PR_FALSE;
	    PR_Unlock(fd->secret->md.miscLock);
        goto ErrorExit;
	}

    WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
	PR_Unlock(fd->secret->md.miscLock);

    err = me->md.osErrCode;
    if (err != kOTNoError)
        goto ErrorExit;

    if (opt->status == T_FAILURE || opt->status == T_NOTSUPPORT){
        err = kEOPNOTSUPPErr;
        goto ErrorExit;
    }
    
    if (level == IPPROTO_TCP && optname == TCP_MAXSEG && opt->status == T_READONLY) {
        err = kEOPNOTSUPPErr;
        goto ErrorExit;
    }

    PR_ASSERT(opt->status == T_SUCCESS);

    return PR_SUCCESS;

ErrorExit:
    macsock_map_error(err);
    return PR_FAILURE;
}


PRInt32 _MD_socketavailable(PRFileDesc *fd)
{
    PRInt32 osfd = fd->secret->md.osfd;
    OSStatus err;
    EndpointRef endpoint = (EndpointRef) osfd;
    size_t bytes;

    if (endpoint == NULL) {
        err = kEBADFErr;
        goto ErrorExit;
    }
    
    bytes = 0;
    
    err = OTCountDataBytes(endpoint, &bytes);
    if ((err == kOTLookErr) ||         // Not really errors, we just need to do a read,
        (err == kOTNoDataErr))        // or there's nothing there.
        err = kOTNoError;
        
    if (err != kOTNoError)
        goto ErrorExit;
        
    return bytes;

ErrorExit:
    macsock_map_error(err);
    return -1;
}


typedef struct RawEndpointAndThread
{
	PRThread *  thread;
	EndpointRef endpoint;
} RawEndpointAndThread;

// Notification routine for raw endpoints not yet attached to a PRFileDesc.
// Async callback routine.
// A5 is OK. Cannot allocate memory here
static pascal void  RawEndpointNotifierRoutine(void * contextPtr, OTEventCode code, OTResult result, void * cookie)
{
	RawEndpointAndThread *endthr = (RawEndpointAndThread *) contextPtr;
    PRThread *    thread   = endthr->thread;
    EndpointRef * endpoint = endthr->endpoint;
    _PRCPU *      cpu      = _PR_MD_CURRENT_CPU(); 
	OSStatus      err;
	OTResult	  resultOT;

    switch (code)
    {
// OTLook Events - 
        case T_LISTEN:        // A connection request is available
            PR_ASSERT(!"T_EXDATA not implemented for raw endpoints");
            break;
			
        case T_CONNECT:      // Confirmation of a connect request
			// cookie = sndCall parameter from OTConnect()
            err = OTRcvConnect(endpoint, NULL);
            PR_ASSERT(err == kOTNoError);

			// wake up waiting thread
            break;

        case T_DATA:        // Standard data is available
			break;

        case T_EXDATA:      // Expedited data is available
            PR_ASSERT(!"T_EXDATA Not implemented for raw endpoints");
			return;

        case T_DISCONNECT:  // A disconnect is available
            err = OTRcvDisconnect(endpoint, NULL);
            PR_ASSERT(err == kOTNoError);
            break;
		
        case T_ERROR:       // obsolete/unused in library
            PR_ASSERT(!"T_ERROR Not implemented for raw endpoints");
			return;		
		
        case T_UDERR:       // UDP Send error; clear the error
			(void) OTRcvUDErr((EndpointRef) cookie, NULL);
            break;

        case T_ORDREL:      // An orderly release is available
            err = OTRcvOrderlyDisconnect(endpoint);
            PR_ASSERT(err == kOTNoError);
            break;		

        case T_GODATA:   // Flow control lifted on standard data
			resultOT = OTLook(endpoint);		// clear T_GODATA event
			PR_ASSERT(resultOT == T_GODATA);
			
			// wake up waiting thread, if any
            break;

        case T_GOEXDATA: // Flow control lifted on expedited data
            PR_ASSERT(!"T_GOEXDATA Not implemented");
			return;

        case T_REQUEST:  // An Incoming request is available
            PR_ASSERT(!"T_REQUEST Not implemented");
            return;

        case T_REPLY:    // An Incoming reply is available
            PR_ASSERT(!"T_REPLY Not implemented");
            return;

        case T_PASSCON:  // State is now T_DATAXFER
			// OTAccept() complete, receiving endpoint in T_DATAXFER state
			// cookie = OTAccept() resRef parameter
			break;
            
// Async Completion Events
        case T_BINDCOMPLETE:
        case T_UNBINDCOMPLETE:
        case T_ACCEPTCOMPLETE:
        case T_OPTMGMTCOMPLETE:
        case T_GETPROTADDRCOMPLETE:
            break;

		// for other OT events, see NotifierRoutine above
        default:
            return;
    }

	if (thread) {
		thread->md.osErrCode = result;
		if (_PR_MD_GET_INTSOFF()) {
			cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
			thread->md.asyncNotifyPending = PR_TRUE;
			return;
		}
		DoneWaitingOnThisThread(thread);
	}
}

PRInt32 _MD_accept(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
{
    OSStatus err;
    EndpointRef endpoint = (EndpointRef) fd->secret->md.osfd;
    PRThread *me = _PR_MD_CURRENT_THREAD();
    TBind bindReq;
    PRNetAddr bindAddr;
    PRInt32 newosfd = -1;
    TCall call;
    PRNetAddr callAddr;
    RawEndpointAndThread *endthr = NULL;

    if (endpoint == NULL) {
        err = kEBADFErr;
        goto ErrorExit;
    }
        
    memset(&call, 0 , sizeof(call));

    if (addr != NULL) {
        call.addr.maxlen = *addrlen;
        call.addr.len = *addrlen;
        call.addr.buf = (UInt8*) addr;
    } else {
        call.addr.maxlen = sizeof(callAddr);
        call.addr.len = sizeof(callAddr);
        call.addr.buf = (UInt8*) &callAddr;
    }

	do {
	    PrepareForAsyncCompletion(me, fd->secret->md.osfd);
	    fd->secret->md.misc.thread = me;
	    
	    // Perform the listen. 
	    err = OTListen (endpoint, &call);
	    if (err == kOTNoError)
	    	break; // got the call information
	    else if ((!fd->secret->nonblocking) && (err == kOTNoDataErr)) {
	        WaitOnThisThread(me, timeout);
	        err = me->md.osErrCode;
	        if ((err != kOTNoError) && (err != kOTNoDataErr))
	        	goto ErrorExit;
			// we can get kOTNoError here, but still need
			// to loop back to call OTListen, in order
			// to get call info for OTAccept
	    } else {
	    	goto ErrorExit; // we're nonblocking, and/or we got an error
	    }   
	}
	while(1);

    newosfd = _MD_socket(AF_INET, SOCK_STREAM, 0);
    if (newosfd == -1)
        return -1;
            
	// Attach the raw endpoint handler to this endpoint for now.
	endthr = (RawEndpointAndThread *) PR_Malloc(sizeof(RawEndpointAndThread));
	endthr->thread = me;
	endthr->endpoint = (EndpointRef) newosfd;
	
	err = OTInstallNotifier((ProviderRef) newosfd, RawEndpointNotifierRoutineUPP, endthr);
    PR_ASSERT(err == kOTNoError);
    
	err = OTSetAsynchronous((EndpointRef) newosfd);
	PR_ASSERT(err == kOTNoError);

    // Bind to a local port; let the system assign it.
    bindAddr.inet.family = AF_INET;
    bindAddr.inet.port = bindAddr.inet.ip = 0;

    bindReq.addr.maxlen = PR_NETADDR_SIZE (&bindAddr);
    bindReq.addr.len = 0;
    bindReq.addr.buf = (UInt8*) &bindAddr;
    bindReq.qlen = 0;

    PrepareForAsyncCompletion(me, newosfd);    
    err = OTBind((EndpointRef) newosfd, &bindReq, NULL);
    if (err != kOTNoError)
        goto ErrorExit;

    WaitOnThisThread(me, timeout);

    err = me->md.osErrCode;
    if (err != kOTNoError)
        goto ErrorExit;

    PrepareForAsyncCompletion(me, newosfd);    

    err = OTAccept (endpoint, (EndpointRef) newosfd, &call);
 	if ((err != kOTNoError) && (err != kOTNoDataErr))
        goto ErrorExit;

    WaitOnThisThread(me, timeout);

    err = me->md.osErrCode;
    if (err != kOTNoError)
        goto ErrorExit;

    if (addrlen != NULL)
        *addrlen = call.addr.len;

	// Remove the temporary notifier we installed to set up the new endpoint.
	OTRemoveNotifier((EndpointRef) newosfd);
	PR_Free(endthr); // free the temporary context we set up for this endpoint

    return newosfd;

ErrorExit:
	me->io_pending = PR_FALSE; // clear pending wait state if any
    if (newosfd != -1)
        _MD_closesocket(newosfd);
    macsock_map_error(err);
    return -1;
}


PRInt32 _MD_connect(PRFileDesc *fd, PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
{
    OSStatus err;
    EndpointRef endpoint = (EndpointRef) fd->secret->md.osfd;
    PRThread *me = _PR_MD_CURRENT_THREAD();
    TCall sndCall;
    TBind bindReq;
    PRNetAddr bindAddr;

    if (endpoint == NULL) {
        err = kEBADFErr;
        goto ErrorExit;
    }
        
    if (addr == NULL) {
        err = kEFAULTErr;
        goto ErrorExit;
    }
        
    // Bind to a local port; let the system assign it.

    bindAddr.inet.family = AF_INET;
    bindAddr.inet.port = bindAddr.inet.ip = 0;

    bindReq.addr.maxlen = PR_NETADDR_SIZE (&bindAddr);
    bindReq.addr.len = 0;
    bindReq.addr.buf = (UInt8*) &bindAddr;
    bindReq.qlen = 0;
    
	PR_Lock(fd->secret->md.miscLock);
    PrepareForAsyncCompletion(me, fd->secret->md.osfd);    
	fd->secret->md.misc.thread = me;

    err = OTBind(endpoint, &bindReq, NULL);
    if (err != kOTNoError) {
	    me->io_pending = PR_FALSE;
	    PR_Unlock(fd->secret->md.miscLock);
        goto ErrorExit;
	}

    WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
	PR_Unlock(fd->secret->md.miscLock);

    err = me->md.osErrCode;
    if (err != kOTNoError)
        goto ErrorExit;

    memset(&sndCall, 0 , sizeof(sndCall));

    sndCall.addr.maxlen = addrlen;
    sndCall.addr.len = addrlen;
    sndCall.addr.buf = (UInt8*) addr;

	if (!fd->secret->nonblocking) {    
        PrepareForAsyncCompletion(me, fd->secret->md.osfd);    
	    fd->secret->md.write.thread = me;
    }
	
    err = OTConnect (endpoint, &sndCall, NULL);
	if (err == kOTNoError) {
        PR_ASSERT(!"OTConnect returned kOTNoError in async mode!?!");	
	}
	if (fd->secret->nonblocking) {
		if (err == kOTNoDataErr)
			err = EINPROGRESS;
		goto ErrorExit;
	} else {
		if (err != kOTNoError && err != kOTNoDataErr) {
            me->io_pending = PR_FALSE;
            goto ErrorExit;
		}
	}
	
    WaitOnThisThread(me, timeout);

    err = me->md.osErrCode;
    if (err != kOTNoError)
        goto ErrorExit;

    return kOTNoError;

ErrorExit:
    macsock_map_error(err);
    return -1;
}


// Errors:
// EBADF -- bad socket id
// EFAULT -- bad buffer
static PRInt32 SendReceiveStream(PRFileDesc *fd, void *buf, PRInt32 amount, 
                               PRIntn flags, PRIntervalTime timeout, SndRcvOpCode opCode)
{
    OSStatus err;
    OTResult result;
    EndpointRef endpoint = (EndpointRef) fd->secret->md.osfd;
    PRThread *me = _PR_MD_CURRENT_THREAD();
    PRInt32 bytesLeft = amount;

    PR_ASSERT(flags == 0);
    PR_ASSERT(opCode == kSTREAM_SEND || opCode == kSTREAM_RECEIVE);
    
    if (endpoint == NULL) {
        err = kEBADFErr;
        goto ErrorExit;
    }
        
    if (buf == NULL) {
        err = kEFAULTErr;
        goto ErrorExit;
    }
    
    while (bytesLeft > 0)
    {
        Boolean disabledNotifications = OTEnterNotifier(endpoint);
    
        PrepareForAsyncCompletion(me, fd->secret->md.osfd);    

        if (opCode == kSTREAM_SEND) {
        	do {

				fd->secret->md.write.thread = me;
				fd->secret->md.writeReady = PR_FALSE;				// expect the worst
	            result = OTSnd(endpoint, buf, bytesLeft, NULL);
				fd->secret->md.writeReady = (result != kOTFlowErr);
				if (fd->secret->nonblocking)							// hope for the best
					break;
				else {

					// We drop through on anything other than a blocking write.
					if (result != kOTFlowErr)
						break;

					// Blocking write, but the pipe is full. Turn notifications on and
					// wait for an event, hoping that it's a T_GODATA event.
                    if (disabledNotifications) {
                        OTLeaveNotifier(endpoint);
                        disabledNotifications = false;
                    }
					WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
					result = me->md.osErrCode;
					if (result != kOTNoError) // got interrupted, or some other error
						break;

					// Prepare to loop back and try again
					disabledNotifications = OTEnterNotifier(endpoint);
  			   		PrepareForAsyncCompletion(me, fd->secret->md.osfd);
				}
			}
			while(1);
        } else {
        	do {
				fd->secret->md.read.thread = me;
				fd->secret->md.readReady = PR_FALSE;				// expect the worst			
	            result = OTRcv(endpoint, buf, bytesLeft, NULL);
	            if (fd->secret->nonblocking) {
					fd->secret->md.readReady = (result != kOTNoDataErr);
					break;
				} else {
					if (result != kOTNoDataErr) {
			      		// If we successfully read a blocking socket, check for more data.
			      		// According to IM:OT, we should be able to rely on OTCountDataBytes
			      		// to tell us whether there is a nonzero amount of data pending.
			        	size_t count;
			        	OSErr tmpResult;
			        	tmpResult = OTCountDataBytes(endpoint, &count);
				        fd->secret->md.readReady = ((tmpResult == kOTNoError) && (count > 0));
						break;
				    }

					// Blocking read, but no data available. Turn notifications on and
					// wait for an event on this endpoint, and hope that we get a T_DATA event.
                    if (disabledNotifications) {
                        OTLeaveNotifier(endpoint);
                        disabledNotifications = false;
                    }
					WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
					result = me->md.osErrCode;
					if (result != kOTNoError) // interrupted thread, etc.
						break;

					// Prepare to loop back and try again
					disabledNotifications = OTEnterNotifier(endpoint);
  			   		PrepareForAsyncCompletion(me, fd->secret->md.osfd);
				}
			}
			// Retry read if we had to wait for data to show up.
			while(1);
        }

		me->io_pending = PR_FALSE;
		
        if (opCode == kSTREAM_SEND)
            fd->secret->md.write.thread = NULL;
        else
            fd->secret->md.read.thread  = NULL;

        // turn notifications back on
        if (disabledNotifications)
            OTLeaveNotifier(endpoint);
            
        if (result > 0) {
            buf = (void *) ( (UInt32) buf + (UInt32)result );
            bytesLeft -= result;
            if (opCode == kSTREAM_RECEIVE)
                return result;
        } else {
			switch (result) {
				case kOTLookErr:
				    PR_ASSERT(!"call to OTLook() required after all.");
					break;
				
				case kOTFlowErr:
				case kOTNoDataErr:
				case kEAGAINErr:
				case kEWOULDBLOCKErr:
					if (fd->secret->nonblocking) {
						err = result;
						goto ErrorExit;
					}

					WaitOnThisThread(me, timeout);
					err = me->md.osErrCode;
					if (err != kOTNoError)
						goto ErrorExit;				
					break;
					
				case kOTOutStateErr:	// if provider already closed, fall through to handle error
					if (fd->secret->md.orderlyDisconnect)
						return 0;
				default:
					err = result;
					goto ErrorExit;
			}
		}
    }

    PR_ASSERT(opCode == kSTREAM_SEND ? fd->secret->md.write.thread == nil :
                                       fd->secret->md.read.thread  == nil);
    return amount;

ErrorExit:
    PR_ASSERT(opCode == kSTREAM_SEND ? fd->secret->md.write.thread == nil :
                                       fd->secret->md.read.thread  == nil);
    macsock_map_error(err);
    return -1;
}                               


PRInt32 _MD_recv(PRFileDesc *fd, void *buf, PRInt32 amount, 
                               PRIntn flags, PRIntervalTime timeout)
{
    return (SendReceiveStream(fd, buf, amount, flags, timeout, kSTREAM_RECEIVE));
}                               


PRInt32 _MD_send(PRFileDesc *fd,const void *buf, PRInt32 amount, 
                               PRIntn flags, PRIntervalTime timeout)
{
    return (SendReceiveStream(fd, (void *)buf, amount, flags, timeout, kSTREAM_SEND));
}                               


// Errors:
// EBADF -- bad socket id
// EFAULT -- bad buffer
static PRInt32 SendReceiveDgram(PRFileDesc *fd, void *buf, PRInt32 amount, 
                               PRIntn flags, PRNetAddr *addr, PRUint32 *addrlen, 
                               PRIntervalTime timeout, SndRcvOpCode opCode)
{
    OSStatus err;
    EndpointRef endpoint = (EndpointRef) fd->secret->md.osfd;
    PRThread *me = _PR_MD_CURRENT_THREAD();
    PRInt32 bytesLeft = amount;
    TUnitData dgram;
    OTResult	result;

    PR_ASSERT(flags == 0);
    
    if (endpoint == NULL) {
        err = kEBADFErr;
        goto ErrorExit;
    }
        
    if (buf == NULL || addr == NULL) {
        err = kEFAULTErr;
        goto ErrorExit;
    }
    
    if (opCode != kDGRAM_SEND && opCode != kDGRAM_RECEIVE) {
        err = kEINVALErr;
        goto ErrorExit;
    }
        
    memset(&dgram, 0 , sizeof(dgram));
    dgram.addr.maxlen = *addrlen;
    dgram.addr.len = *addrlen;
    dgram.addr.buf = (UInt8*) addr;
    dgram.udata.maxlen = amount;
    dgram.udata.len = amount;
    dgram.udata.buf = (UInt8*) buf;    

    while (bytesLeft > 0) {
    
        PrepareForAsyncCompletion(me, fd->secret->md.osfd);    

        if (opCode == kDGRAM_SEND) {
			fd->secret->md.write.thread = me;
			fd->secret->md.writeReady = PR_FALSE;				// expect the worst
            err = OTSndUData(endpoint, &dgram);
			if (result != kOTFlowErr)							// hope for the best
				fd->secret->md.writeReady = PR_TRUE;
		} else {
			fd->secret->md.read.thread = me;
			fd->secret->md.readReady = PR_FALSE;				// expect the worst			
            err = OTRcvUData(endpoint, &dgram, NULL);
			if (result != kOTNoDataErr)							// hope for the best
				fd->secret->md.readReady = PR_TRUE;
		}

        if (err == kOTNoError) {
            buf = (void *) ( (UInt32) buf + (UInt32)dgram.udata.len );
            bytesLeft -= dgram.udata.len;
            dgram.udata.buf = (UInt8*) buf;    
            me->io_pending = PR_FALSE;
        } else {
            PR_ASSERT(err == kOTNoDataErr || err == kOTOutStateErr);
            WaitOnThisThread(me, timeout);
            err = me->md.osErrCode;
            if (err != kOTNoError)
                goto ErrorExit;
        }
    }

    if (opCode == kDGRAM_RECEIVE)
        *addrlen = dgram.addr.len;

    return amount;

ErrorExit:
    macsock_map_error(err);
    return -1;
}                               


PRInt32 _MD_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount, 
                               PRIntn flags, PRNetAddr *addr, PRUint32 *addrlen,
                               PRIntervalTime timeout)
{
    return (SendReceiveDgram(fd, buf, amount, flags, addr, addrlen,
                            timeout, kDGRAM_RECEIVE));
}                               


PRInt32 _MD_sendto(PRFileDesc *fd,const void *buf, PRInt32 amount, 
                               PRIntn flags, PRNetAddr *addr, PRUint32 addrlen,
                               PRIntervalTime timeout)
{
    return (SendReceiveDgram(fd, (void *)buf, amount, flags, addr, &addrlen,
                            timeout, kDGRAM_SEND));
}                               


PRInt32 _MD_closesocket(PRInt32 osfd)
{
    OSStatus err;
    EndpointRef endpoint = (EndpointRef) osfd;
    PRThread *me = _PR_MD_CURRENT_THREAD();

    if (endpoint == NULL) {
        err = kEBADFErr;
        goto ErrorExit;
    }
        
    if (me->io_pending && me->io_fd == osfd)
        me->io_pending = PR_FALSE;

    (void) OTSndOrderlyDisconnect(endpoint);
    err = OTCloseProvider(endpoint);
    if (err != kOTNoError)
        goto ErrorExit;

    return kOTNoError;

ErrorExit:
    macsock_map_error(err);
    return -1;
}                               


PRInt32 _MD_writev(PRFileDesc *fd, const struct PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
{
#pragma unused (fd, iov, iov_size, timeout)

    PR_ASSERT(0);
    _PR_MD_CURRENT_THREAD()->md.osErrCode = unimpErr;
    return -1;
}                               


static PRBool GetState(PRFileDesc *fd, PRBool *readReady, PRBool *writeReady, PRBool *exceptReady)
{
    OTResult resultOT;
    // hack to emulate BSD sockets; say that a socket that has disconnected
    // is still readable.
    size_t   availableData = 1;
    if (!fd->secret->md.orderlyDisconnect)
        OTCountDataBytes((EndpointRef)fd->secret->md.osfd, &availableData);

    *readReady = fd->secret->md.readReady && (availableData > 0);
	*exceptReady = fd->secret->md.exceptReady;

    resultOT = OTGetEndpointState((EndpointRef)fd->secret->md.osfd);
    switch (resultOT)    {
        case T_DATAXFER:
        case T_INREL:
            *writeReady = PR_TRUE;
            break;
        default:
            *writeReady = PR_FALSE;
    }
    
    return  *readReady || *writeReady || *exceptReady;
}

// check to see if any of the poll descriptors have data available
// for reading or writing.
static PRInt32 CheckPollDescs(PRPollDesc *pds, PRIntn npds)
{
    PRInt32 ready = 0;
    PRPollDesc *pd, *epd;
    
        for (pd = pds, epd = pd + npds; pd < epd; pd++)
        {
            PRInt16  in_flags_read = 0,  in_flags_write = 0;
            PRInt16 out_flags_read = 0, out_flags_write = 0;

            if (NULL == pd->fd || pd->in_flags == 0) continue;

            if (pd->in_flags & PR_POLL_READ)
            {
                in_flags_read = (pd->fd->methods->poll)(
                    pd->fd, pd->in_flags & ~PR_POLL_WRITE, &out_flags_read);
            }
            if (pd->in_flags & PR_POLL_WRITE)
            {
                in_flags_write = (pd->fd->methods->poll)(
                    pd->fd, pd->in_flags & ~PR_POLL_READ, &out_flags_write);
            }
            if ((0 != (in_flags_read & out_flags_read))
            || (0 != (in_flags_write & out_flags_write)))
            {
                ready += 1;  /* some layer has buffer input */
                pd->out_flags = out_flags_read | out_flags_write;
            }
            else
            {
                PRFileDesc *bottomFD;
                PRBool readReady, writeReady, exceptReady;

                pd->out_flags = 0;  /* pre-condition */
                bottomFD = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
                /* bottomFD can be NULL for pollable sockets */
                if (bottomFD)
                {
                    if (_PR_FILEDESC_OPEN == bottomFD->secret->state)
                    {
                        if (GetState(bottomFD, &readReady, &writeReady, &exceptReady))
                        {
                            if (readReady)
                            {
                                if (in_flags_read & PR_POLL_READ)
                                    pd->out_flags |= PR_POLL_READ;
                                if (in_flags_write & PR_POLL_READ)
                                    pd->out_flags |= PR_POLL_WRITE;
                            }
                            if (writeReady)
                            {
                                if (in_flags_read & PR_POLL_WRITE)
                                    pd->out_flags |= PR_POLL_READ;
                                if (in_flags_write & PR_POLL_WRITE)
                                    pd->out_flags |= PR_POLL_WRITE;
                            }
                            if (exceptReady && (pd->in_flags & PR_POLL_EXCEPT))
                            {
                                pd->out_flags |= PR_POLL_EXCEPT;
                            }
                            if (0 != pd->out_flags) ready++;
                        }
                    }
                    else    /* bad state */
                    {
                        ready += 1;  /* this will cause an abrupt return */
                        pd->out_flags = PR_POLL_NVAL;  /* bogii */
                    }
                }
            }
        }

    return ready;
}

// set or clear md.poll.thread on the poll descriptors
static void SetDescPollThread(PRPollDesc *pds, PRIntn npds, PRThread* thread)
{
    PRInt32     ready = 0;
    PRPollDesc *pd, *epd;

    for (pd = pds, epd = pd + npds; pd < epd; pd++)
    {   
        if (pd->fd)
        { 
            PRFileDesc *bottomFD = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
            if (bottomFD && (_PR_FILEDESC_OPEN == bottomFD->secret->state))
            {
                bottomFD->secret->md.poll.thread = thread;
            }
        }        
    }
}

PRInt32 _MD_poll(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
{
    PRThread    *thread = _PR_MD_CURRENT_THREAD();
    intn is;
    PRInt32 ready;
    OSErr   result;
    
    if (timeout == PR_INTERVAL_NO_WAIT) {        
        return CheckPollDescs(pds, npds);
    }
    
    _PR_INTSOFF(is);
    PR_Lock(thread->md.asyncIOLock);

    // ensure that we don't miss the firing of the notifier while checking socket status
    // need to set up the thread
    PrepareForAsyncCompletion(thread, 0);

    SetDescPollThread(pds, npds, thread);        
    ready = CheckPollDescs(pds, npds);

    PR_Unlock(thread->md.asyncIOLock);
    _PR_FAST_INTSON(is);

    if (ready == 0) {
        WaitOnThisThread(thread, timeout);
        result = thread->md.osErrCode;
        if (result != noErr && result != kETIMEDOUTErr) {
            PR_ASSERT(0);   /* debug: catch unexpected errors */
            ready = -1;
        } else {
            ready = CheckPollDescs(pds, npds);
        }
    } else {
        thread->io_pending = PR_FALSE;
    }

    SetDescPollThread(pds, npds, NULL);

    return ready;
}


void _MD_initfiledesc(PRFileDesc *fd)
{
	// Allocate a PR_Lock to arbitrate miscellaneous OT calls for this endpoint between threads
	// We presume that only one thread will be making Read calls (Recv/Accept) and that only
	// one thread will be making Write calls (Send/Connect) on the endpoint at a time.
	if (fd->methods->file_type == PR_DESC_SOCKET_TCP ||
		fd->methods->file_type == PR_DESC_SOCKET_UDP )
	{
		PR_ASSERT(fd->secret->md.miscLock == NULL);
		fd->secret->md.miscLock = PR_NewLock();
		PR_ASSERT(fd->secret->md.miscLock != NULL);
		fd->secret->md.orderlyDisconnect = PR_FALSE;
		fd->secret->md.readReady = PR_FALSE;		// let's not presume we have data ready to read
		fd->secret->md.writeReady = PR_TRUE;		// let's presume we can write unless we hear otherwise
		fd->secret->md.exceptReady = PR_FALSE;
	}
}


void _MD_freefiledesc(PRFileDesc *fd)
{
	if (fd->secret->md.miscLock)
	{
		PR_ASSERT(fd->methods->file_type == PR_DESC_SOCKET_TCP || fd->methods->file_type == PR_DESC_SOCKET_UDP);
		PR_DestroyLock(fd->secret->md.miscLock);
		fd->secret->md.miscLock = NULL;
	} else {
		PR_ASSERT(fd->methods->file_type != PR_DESC_SOCKET_TCP && PR_DESC_SOCKET_TCP != PR_DESC_SOCKET_UDP);
	}
}

// _MD_makenonblock is also used for sockets meant to be used for blocking I/O,
// in order to install the notifier routine for async completion.
void _MD_makenonblock(PRFileDesc *fd)
{
	// We simulate non-blocking mode using async mode rather
	// than put the endpoint in non-blocking mode.
	// We need to install the PRFileDesc as the contextPtr for the NotifierRoutine, but it
	// didn't exist at the time the endpoint was created.  It does now though...
	ProviderRef	endpointRef = (ProviderRef)fd->secret->md.osfd;
	OSStatus	err;
	
	// Install fd->secret as the contextPtr for the Notifier function associated with this 
	// endpoint. We use this instead of the fd itself because:
	//            (a) in cases where you import I/O layers, the containing 
	//                fd changes, but the secret structure does not;
	//            (b) the notifier func refers only to the secret data structure
	//                anyway.
	err = OTInstallNotifier(endpointRef, NotifierRoutineUPP, fd->secret);
	PR_ASSERT(err == kOTNoError);
	
	// Now that we have a NotifierRoutine installed, we can make the endpoint asynchronous
	err = OTSetAsynchronous(endpointRef);
	PR_ASSERT(err == kOTNoError);
}


PR_IMPLEMENT(PRInt32) _MD_shutdown(PRFileDesc *fd, PRIntn how)
{
#pragma unused (fd, how)

/* Just succeed silently!!! */
return (0);
}                               


PR_IMPLEMENT(PRStatus) 
_MD_getpeername(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen)
{
    PRThread *me = _PR_MD_CURRENT_THREAD();
	EndpointRef ep = (EndpointRef) fd->secret->md.osfd;
	InetAddress inetAddr;
	TBind peerAddr;
	OSErr err;
	
	if (*addrlen < sizeof(InetAddress)) {

		err = (OSErr) kEINVALErr;
		goto ErrorExit;
	}

    peerAddr.addr.maxlen = sizeof(InetAddress);
    peerAddr.addr.len = 0;
    peerAddr.addr.buf = (UInt8*) &inetAddr;
    peerAddr.qlen = 0;

    PrepareForAsyncCompletion(me, fd->secret->md.osfd);    
	fd->secret->md.misc.thread = me; // tell notifier routine what to wake up

	err = OTGetProtAddress(ep, NULL, &peerAddr);
	
    if (err != kOTNoError)
        goto ErrorExit;

    WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);

    err = me->md.osErrCode;
    if ((err == kOTNoError) && (peerAddr.addr.len < sizeof(InetAddress)))
    	err = kEBADFErr; // we don't understand the address we got
    if (err != kOTNoError)
    	goto ErrorExit;
    	
    // Translate the OT peer information into an NSPR address.
    addr->inet.family = AF_INET;
    addr->inet.port = (PRUint16) inetAddr.fPort;
    addr->inet.ip = (PRUint32) inetAddr.fHost;
    
    *addrlen = PR_NETADDR_SIZE(addr); // return the amount of data obtained
	return PR_SUCCESS;

ErrorExit:
    macsock_map_error(err);
    return PR_FAILURE;
}                               


PR_IMPLEMENT(unsigned long) inet_addr(const char *cp)
{
    OSStatus err;
    InetHost host;    

    	_MD_FinishInitNetAccess();

    err = OTInetStringToHost((char*) cp, &host);
    if (err != kOTNoError)
        return -1;
    
    return host;
}


static char *sAliases[1] = {NULL};
static struct hostent sHostEnt = {NULL, &sAliases[0], AF_INET, sizeof (long), NULL};
static InetHostInfo sHostInfo;
static InetHost *sAddresses[kMaxHostAddrs+1];


PR_IMPLEMENT(struct hostent *) gethostbyname(const char * name)
{
    OSStatus err;
    PRUint32 index;
    PRThread *me = _PR_MD_CURRENT_THREAD();

    	_MD_FinishInitNetAccess();

    me->io_pending       = PR_TRUE;
    me->io_fd            = NULL;
    me->md.osErrCode     = noErr;
	
	PR_Lock(dnsContext.lock);	// so we can safely store our thread ptr in dnsContext
	dnsContext.thread = me;		// so we know what thread to wake up when OTInetStringToAddress completes

    err = OTInetStringToAddress(dnsContext.serviceRef, (char *)name, &sHostInfo);
    if (err != kOTNoError) {
        me->io_pending = PR_FALSE;
        me->md.osErrCode = err;
        goto ErrorExit;
    }

    WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
	PR_Unlock(dnsContext.lock);

    if (me->md.osErrCode != kOTNoError)
        goto ErrorExit;

    sHostEnt.h_name = sHostInfo.name;
    for (index=0; index<kMaxHostAddrs && sHostInfo.addrs[index] != NULL; index++)
        sAddresses[index] = &sHostInfo.addrs[index];
    sAddresses[index] = NULL;    
    sHostEnt.h_addr_list = (char **)sAddresses;

    return (&sHostEnt);

ErrorExit:
    return NULL;
}


PR_IMPLEMENT(struct hostent *) gethostbyaddr(const void *addr, int addrlen, int type)
{
    PR_ASSERT(type == AF_INET);
    PR_ASSERT(addrlen == sizeof(struct in_addr));

    	_MD_FinishInitNetAccess();

    OTInetHostToString((InetHost)addr, sHostInfo.name);
    
    return (gethostbyname(sHostInfo.name));
}


PR_IMPLEMENT(char *) inet_ntoa(struct in_addr addr)
{
    	_MD_FinishInitNetAccess();

    OTInetHostToString((InetHost)addr.s_addr, sHostInfo.name);
    
    return sHostInfo.name;
}


PRStatus _MD_gethostname(char *name, int namelen)
{
    OSStatus err;
    InetInterfaceInfo info;

    	_MD_FinishInitNetAccess();

    /*
     *    On a Macintosh, we don't have the concept of a local host name.
     *    We do though have an IP address & everyone should be happy with
     *     a string version of that for a name.
     *    The alternative here is to ping a local DNS for our name, they
     *    will often know it.  This is the cheap, easiest, and safest way out.
     */

    /* Make sure the string is as long as the longest possible address */
    if (namelen < strlen("123.123.123.123")) {
        err = kEINVALErr;
        goto ErrorExit;
    }

    err = OTInetGetInterfaceInfo(&info, kDefaultInetInterface);
    if (err != kOTNoError)
        goto ErrorExit;
    
    OTInetHostToString(info.fAddress, name);
    
    return PR_SUCCESS;

ErrorExit:
    macsock_map_error(err);
    return PR_FAILURE;
}


#define kIPName        "ip"
static struct protoent sIPProto = {kIPName, NULL, INET_IP};
static struct protoent sTCPProto = {kTCPName, NULL, INET_TCP};
static struct protoent sUDPProto = {kUDPName, NULL, INET_UDP};

PR_IMPLEMENT(struct protoent *) getprotobyname(const char * name)
{
    if (strcmp(name, kIPName) == 0)
        return (&sIPProto);
        
    if (strcmp(name, kTCPName) == 0)
        return (&sTCPProto);
        
    if (strcmp(name, kUDPName) == 0)
        return (&sUDPProto);
        
ErrorExit:
    macsock_map_error(kEINVALErr);
    return NULL;
}


PR_IMPLEMENT(struct protoent *) getprotobynumber(int number)
{
    if (number == INET_IP)
        return (&sIPProto);
        
    if (number == INET_TCP)
        return (&sTCPProto);
        
    if (number == INET_UDP)
        return (&sUDPProto);
        
ErrorExit:
    macsock_map_error(kEINVALErr);
    return NULL;
}


int _MD_mac_get_nonblocking_connect_error(PRInt32 osfd)
{
    OTResult resultOT;
    EndpointRef endpoint = (EndpointRef) osfd;

    resultOT = OTGetEndpointState(endpoint);
    switch (resultOT)    {
        case T_OUTCON:
            macsock_map_error(EINPROGRESS);
            return -1;
        case T_DATAXFER:
            return 0;
        case T_IDLE:
            return -1;
        case T_INREL:
        	macsock_map_error(ENOTCONN);
        	return -1;
        default:
            PR_ASSERT(0);
            return -1;
    }
}