/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape Portable Runtime (NSPR). * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998-2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. */ /* This turns on UNIX style errors in OT 1.1 headers */ #define OTUNIXERRORS 1 #include #include #include #include #define GESTALT_OPEN_TPT_PRESENT gestaltOpenTptPresentMask #define GESTALT_OPEN_TPT_TCP_PRESENT gestaltOpenTptTCPPresentMask #include // 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()) { dnsContext.thread->md.missedIONotify = PR_TRUE; cpu->u.missed[cpu->where] |= _PR_MISSED_IO; 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()) { dnsContext.thread->md.missedIONotify = PR_TRUE; cpu->u.missed[cpu->where] |= _PR_MISSED_IO; 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()) { thread->md.missedIONotify = PR_TRUE; cpu->u.missed[cpu->where] |= _PR_MISSED_IO; return; } DoneWaitingOnThisThread(thread); } } // Notification routine // Async callback routine. // A5 is OK. Cannot allocate memory here // Ref: http://gemma.apple.com/techpubs/mac/NetworkingOT/NetworkingWOT-100.html // 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 * readThread = NULL; // also used for 'misc' PRThread * writeThread = NULL; 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) { readThread = 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. writeThread = 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 readThread = 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; // XXX Check this // wake up waiting threads, if any result = -3199 - discon.reason; // obtain the negative error code if ((readThread = secret->md.read.thread) != NULL) { secret->md.read.thread = NULL; secret->md.read.cookie = cookie; } if ((writeThread = secret->md.write.thread) != NULL) { secret->md.write.thread = NULL; secret->md.write.cookie = cookie; } 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; readThread = 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 writeThread = 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: readThread = 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 (readThread) WakeUpNotifiedThread(readThread, result); if (writeThread && (writeThread != readThread)) WakeUpNotifiedThread(writeThread, 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()) { thread->md.asyncNotifyPending = PR_TRUE; cpu->u.missed[cpu->where] |= _PR_MISSED_IO; 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); PR_ASSERT(fd->secret->md.write.thread == NULL); 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 || (opCode == kSTREAM_RECEIVE && flags == PR_MSG_PEEK)); PR_ASSERT(opCode == kSTREAM_SEND || opCode == kSTREAM_RECEIVE); if (endpoint == NULL) { err = kEBADFErr; goto ErrorExit; } if (buf == NULL) { err = kEFAULTErr; goto ErrorExit; } PR_ASSERT(opCode == kSTREAM_SEND ? fd->secret->md.write.thread == NULL : fd->secret->md.read.thread == NULL); 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) { amount = result; goto NormalExit; } } 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) { if (bytesLeft == amount) { // no data was sent err = result; goto ErrorExit; } // some data was sent amount -= bytesLeft; goto NormalExit; } 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) { amount = 0; goto NormalExit; } // else fall through default: err = result; goto ErrorExit; } } } NormalExit: PR_ASSERT(opCode == kSTREAM_SEND ? fd->secret->md.write.thread == NULL : fd->secret->md.read.thread == NULL); return amount; ErrorExit: PR_ASSERT(opCode == kSTREAM_SEND ? fd->secret->md.write.thread == NULL : fd->secret->md.read.thread == NULL); 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; } // OT endpoint states are documented here: // http://gemma.apple.com/techpubs/mac/NetworkingOT/NetworkingWOT-27.html#MARKER-9-65 // 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_IDLE: case T_UNBND: // the socket is not connected. Emulating BSD sockets, // we mark it readable and writable. The next PR_Read // or PR_Write will then fail. Usually, in this situation, // fd->secret->md.exceptReady is also set, and returned if // anyone is polling for it. *readReady = PR_FALSE; *writeReady = PR_FALSE; break; case T_DATAXFER: // data transfer *writeReady = fd->secret->md.writeReady; break; case T_INREL: // incoming orderly release *writeReady = fd->secret->md.writeReady; break; case T_OUTCON: // outgoing connection pending case T_INCON: // incoming connection pending case T_OUTREL: // outgoing orderly release 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)) { if (pd->in_flags & PR_POLL_READ) { PR_ASSERT(thread == NULL || bottomFD->secret->md.read.thread == NULL); bottomFD->secret->md.read.thread = thread; } if (pd->in_flags & PR_POLL_WRITE) { // it's possible for the writing thread to be non-null during // a non-blocking connect, so we assert that we're on // the same thread, or the thread is null. // Note that it's strictly possible for the connect and poll // to be on different threads, so ideally we need to assert // that if md.write.thread is non-null, there is a non-blocking // connect in progress. PR_ASSERT(thread == NULL || (bottomFD->secret->md.write.thread == NULL || bottomFD->secret->md.write.thread == thread)); bottomFD->secret->md.write.thread = thread; } } } } } PRInt32 _MD_poll(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) { PRThread *thread = _PR_MD_CURRENT_THREAD(); intn is; PRInt32 ready; 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); 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); } void _MD_initfdinheritable(PRFileDesc *fd, PRBool imported) { /* XXX this function needs to be implemented */ fd->secret->inheritable = _PR_TRI_UNKNOWN; } void _MD_queryfdinheritable(PRFileDesc *fd) { /* XXX this function needs to be implemented */ PR_ASSERT(0); } 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