// Mac utils
#include "MoreMixedMode.h"
// MacOS
#include <Dialogs.h>
#include <Files.h>
#include <Devices.h>
// stdclib
#include <stdlib.h>
// Network stuff
#include <OpenTransport.h>
#include <MacTCP.h>
#include "mactcpdriver.h"
// NSPR stuff
#include "swkern.h"
// For HoldMemoryGlue and UnholdMemoryGlue
#include "macutil.h"
#ifdef PROFILE
#pragma profile on
#endif
#ifdef assert
#undef assert
#endif
#ifdef DEBUG
#define assert(x) if (!(x)) abort()
#else
#define assert(x) ((void)0)
#endif
//#define TCP_DEBUG
#ifdef TCP_DEBUG
#ifndef DEBUG
#error "You can only define TCP_DEBUG in MacTCP builds"
#endif
#endif
#pragma mark AnnotatedPB
// Utility struct for PBs that we just need within the routine
struct AcquirePB {
AnnotatedPB * fApb;
AcquirePB(int streamType, int pbKind, MacTCPStream * stream)
{
fApb = ((MacTCPDriver*)gNetDriver)->GetPB(streamType, pbKind, stream);
}
~AcquirePB() { ((MacTCPDriver*)gNetDriver)->ReturnPB(fApb);};
};
#pragma mark -
#pragma mark Cwds
#pragma mark -
// =========================================================================
// Cwds
// =========================================================================
// Write data structure for MacTCP
struct Cwds {
UInt16 fSize;
void * fData;
UInt16 fZero;
Cwds(UInt16 length, const void * dataCopy);
~Cwds();
};
// Makes a copy of the data to be written
Cwds::Cwds(UInt16 length, const void * dataCopy)
{
fSize = length;
fZero = 0;
fData = malloc(fSize);
ThrowIfNil_(fData);
::BlockMoveData(dataCopy, fData, fSize);
::HoldMemoryGlue( fData, fSize );
::HoldMemoryGlue( this, sizeof (*this ));
}
Cwds::~Cwds()
{
if (fData != NULL)
{
free(fData);
::UnholdMemoryGlue( fData, fSize );
::UnholdMemoryGlue( this, sizeof (*this ));
}
}
#pragma mark -
#pragma mark MacTCPDriver
#pragma mark -
// =========================================================================
// MacTCPDriver
// =========================================================================
// Reset all the variables. Does not open the driver
MacTCPDriver::MacTCPDriver() : CNetworkDriver()
{
fDriverNum = 0;
fOutstanding.qHead = fOutstanding.qTail = NULL;
fReceived.qHead = fReceived.qTail = NULL;
fWaitDNSCalls.qHead = fWaitDNSCalls.qTail = NULL;
::HoldMemoryGlue( this, sizeof(*this) );
}
extern void WaitToQuit();
// Make sure that all async calls have been completed
MacTCPDriver::~MacTCPDriver()
{
// Delete all the sockets
for (int i=0; i<MAX_SOCKETS; i++)
if (MacSocket::sSockets[i] != NULL)
MacSocket::sSockets[i]->Destroy(TRUE);
if ((fOutstanding.qHead == nil) &&
(fReceived.qHead == nil) &&
(fOutstanding.qHead == nil))
return;
// Put up a dialog here
DialogPtr alert = ::GetNewDialog( 1042, NULL, (WindowPtr)-1 );
if (alert)
DrawDialog(alert);
// Spin wait for all async calls to complete
while ((fOutstanding.qHead != nil) ||
(fReceived.qHead != nil) ||
(fOutstanding.qHead != nil))
{
Spin();
WaitToQuit();
}
CloseResolver();
if (alert)
DisposeDialog(alert);
// Kill a dialog here
::UnholdMemoryGlue( this, sizeof(this));
}
CNetStream * MacTCPDriver::CreateStream(MacSocket * socket)
{
return new MacTCPStream(socket);
}
// Receive buffer size for streams. How to calculate is unclear (MTU, or big hardcoded value
// Currently, uses 16K
UInt16 MacTCPDriver::GetStreamBufferSize()
{
return DEFAULT_BUFFER_SIZE;
}
// Opens the driver. Can fail.
OSErr MacTCPDriver::DoOpenDriver()
{
ParamBlockRec pb;
pb.ioParam.ioCompletion = 0L;
pb.ioParam.ioNamePtr = "\p.IPP";
pb.ioParam.ioPermssn = fsCurPerm;
OSErr err = PBOpenSync(&pb);
if (err == noErr)
{
fDriverNum = pb.ioParam.ioRefNum;
OpenResolver(NULL);
}
fDriverOpen = TRUE;
return err;
}
// Returns annotatedPB
AnnotatedPB * MacTCPDriver::GetPB(int streamType, int pbKind, MacTCPStream * stream)
{
AnnotatedPB * apb = NULL;
if (fPBs.GetCount() == 0) // No cached PBs, use the build in ones
{
try
{
apb = new AnnotatedPB;
}
catch(OSErr err)
{
return NULL;
}
}
else
{
fPBs.FetchItemAt(fPBs.GetCount(), &apb);
fPBs.RemoveItemsAt(1, fPBs.GetCount());
}
memset(apb, 0, sizeof(AnnotatedPB));
apb->streamType = streamType;
apb->stream = stream;
apb->driver = (MacTCPDriver*)gNetDriver;
// Set the standard fields - kind of tcp call, tcp stream, and driver num
if (streamType == SOCK_STREAM) {
apb->pb.tcpPB.csCode = pbKind;
apb->pb.tcpPB.ioCRefNum = fDriverNum;
apb->pb.tcpPB.tcpStream = stream->fStream;
}
else {
apb->pb.udpPB.csCode = pbKind;
apb->pb.udpPB.ioCRefNum = fDriverNum;
apb->pb.udpPB.udpStream = stream->fStream;
}
return apb;
}
void MacTCPDriver::ReturnPB(AnnotatedPB * apb)
{
if (apb)
fPBs.InsertItemsAt(1, fPBs.GetCount(), &apb);
}
#pragma mark -
#pragma mark Async Callbacks
#pragma mark -
// Callback for async calls. Passes it on to the driver
// Interrupt level routine! No globals or mallocs
void TCPAsyncCallDone(TCPiopb *PB);
void TCPAsyncCallDone(TCPiopb *PB)
{
short diff = offsetof(AnnotatedPB, pb);
AnnotatedPB * apb = (AnnotatedPB *)((UInt32)PB - (UInt32)diff);
apb->driver->CompleteAsyncCall(apb);
}
PROCEDURE(TCPAsyncCallDone, uppTCPIOCompletionProcInfo)
void UDPAsyncCallDone(UDPiopb *pb);
void UDPAsyncCallDone(UDPiopb *pb)
{
}
PROCEDURE(UDPAsyncCallDone, uppUDPIOCompletionProcInfo)
// Places the call on the queue of incomplete calls.
// AsyncCallDone removes it, and puts it on the queue of calls to be processed
// We can have only one async call per stream at one time.
// This restriction might be removed later
void MacTCPDriver::MakeAsyncCall(AnnotatedPB * apb)
{
if (apb->stream->fLastCallInProgress == TRUE)
assert(FALSE);
#ifdef DEBUG
if (apb->streamType == SOCK_STREAM)
apb->stream->fLastAsyncCall = apb->pb.tcpPB.csCode;
else
apb->stream->fLastAsyncCall = apb->pb.udpPB.csCode;
#endif
apb->stream->fLastCallInProgress = TRUE;
apb->stream->fLastCallResult = -1;
// Make sure we have no other outstanding calls
::Enqueue((QElemPtr)apb, &fOutstanding);
if (apb->streamType == SOCK_STREAM)
apb->pb.tcpPB.ioCompletion = &PROCPTR(TCPAsyncCallDone);
else
apb->pb.udpPB.ioCompletion = &PROCPTR(UDPAsyncCallDone);
HoldMemoryGlue( apb, sizeof(*apb)); // Unhold is CompleteAsyncCall
OSErr err = PBControlAsync((ParmBlkPtr)&(apb->pb));
if (err != noErr)
{
if (apb->streamType == SOCK_STREAM)
apb->pb.tcpPB.ioResult = driverError;
else
apb->pb.udpPB.ioResult = driverError;
CompleteAsyncCall(apb);
SpendTime();
assert(FALSE); // OK if you are running surfwatch
}
}
// Completion just adds it to the queue of things to process
// Interrupt level routine! No globals or mallocs
void MacTCPDriver::CompleteAsyncCall(AnnotatedPB * apb)
{
UnholdMemoryGlue( apb, sizeof (*apb) );
fQueueLock = FALSE; // If we are walking the queue, the walker will check this lock to see if the queue has been modified
OSErr err = ::Dequeue((QElemPtr)apb,&fOutstanding);
if (err != noErr)
{
assert(FALSE);
return;
}
apb->qLink = NULL;
{
AnnotatedPB * oldApb = (AnnotatedPB *)fReceived.qHead;
while (oldApb != NULL)
{
if (oldApb == apb)
assert(FALSE);
oldApb = (AnnotatedPB *)oldApb->qLink;
}
}
::Enqueue((QElemPtr)apb, &fReceived);
}
void MacTCPDriver::DispatchCompleteAsyncCall(AnnotatedPB * apb)
{
short code;
apb->stream->fLastCallInProgress = FALSE;
if (apb->streamType == SOCK_STREAM) {
apb->stream->fLastCallResult = apb->pb.tcpPB.ioResult;
code = apb->pb.tcpPB.csCode;
}
else {
apb->stream->fLastCallResult = apb->pb.udpPB.ioResult;
code = apb->pb.udpPB.csCode;
}
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X Dispatching completed Async call. Code is %d.\n", this, code);
#endif
switch (code)
{
case TCPPassiveOpen:
apb->stream->TCPPassiveOpenComplete(apb);
break;
case TCPActiveOpen:
apb->stream->TCPActiveOpenComplete(apb);
break;
case TCPSend:
apb->stream->TCPSendComplete(apb);
break;
case TCPClose:
apb->stream->TCPCloseComplete(apb);
break;
case TCPRelease:
apb->stream->TCPReleaseComplete(apb);
break;
/*
case TCPCreate:
apb->stream->TCPCreateComplete(apb);
break;
case TCPNoCopyRcv:
assert(false);
break;
case TCPRcvBfrReturn:
assert(FALSE);
break;
case TCPRcv:
apb->stream->TCPRcvComplete(apb);
break;
case TCPAbort:
// apb->stream->TCPAbortComplete(apb);
break;
case TCPStatus:
// apb->stream->TCPStatusComplete(apb);
break;
case TCPExtendedStat:
// apb->stream->TCPExtendedStatComplete(apb);
break;
case TCPGlobalInfo:
// apb->stream->TCPGlobalInfoComplete(apb);
break;
case TCPCtlMax:
// apb->stream->TCPCtlMaxComplete(apb);
break;
*/
default:
assert(FALSE);
}
}
// When async calls complete, they end up on the fReceived queue
// we figure out what the calls were (stream, operation), and call
// the stream to handle the call (ignored right now
static Boolean spendTimeLock = FALSE; // Prevents reentrancy
void MacTCPDriver::SpendTime()
{
AnnotatedPB * apb = NULL;
while(fReceived.qHead != NULL)
{
// Be very careful about the order of these calls
apb = (AnnotatedPB *)fReceived.qHead;
OSErr err = ::Dequeue((QElemPtr)apb,&fReceived);
if (err) assert(FALSE);
if (apb == (AnnotatedPB *)fReceived.qHead)
assert(FALSE);
DispatchCompleteAsyncCall(apb);
ReturnPB(apb);
}
QElemPtr dnsElem = fDoneDNSCalls.qHead;
while (dnsElem != NULL)
{
CMacTCPDNSObject * dns = (CMacTCPDNSObject *)((UInt32)dnsElem - (UInt32)offsetof(CMacTCPDNSObject, fQLink));
dnsElem = dnsElem->qLink;
if (dns->fSocket)
dns->fSocket->DNSNotify();
else if (dns->fDeleteSelfWhenDone)
delete dns;
}
}
// Should be used only to get the status of the connection
OSErr MacTCPDriver::MakeSyncCall(AnnotatedPB * apb)
{
if (apb->streamType == SOCK_STREAM)
apb->pb.tcpPB.ioCompletion = NULL; // Just in case
else
apb->pb.udpPB.ioCompletion = NULL; // Just in case
OSErr err = PBControlSync((ParmBlkPtr)&(apb->pb));
return err;
}
// Asych TCP notify procedure
// Has our stream class as user data ptr
pascal void TCPNotifyProc (StreamPtr tcpStream,unsigned short eventCode,Ptr userDataPtr,unsigned short terminReason,struct ICMPReport *icmpMsg);
pascal void TCPNotifyProc ( StreamPtr /* tcpStream*/ ,
unsigned short eventCode,
Ptr userDataPtr,
unsigned short terminReason,
struct ICMPReport *icmpMsg)
{
MacTCPStream * myStream = (MacTCPStream *)userDataPtr;
myStream->TCPNotify(eventCode, terminReason, icmpMsg);
}
PROCEDURE (TCPNotifyProc, uppTCPNotifyProcInfo)
// Asynch UDP notify procedure
// The creatorÕs stream class will be returned in the user data ptr
pascal void UDPNotifyProc(StreamPtr, unsigned short, Ptr, struct ICMPReport *);
pascal void UDPNotifyProc( StreamPtr /* tcpStream */,
unsigned short eventCode,
Ptr userDataPtr,
struct ICMPReport *icmpMsg)
{
MacTCPStream * myStream = (MacTCPStream *)userDataPtr;
myStream->UDPNotify(eventCode, icmpMsg);
}
PROCEDURE (UDPNotifyProc, uppUDPNotifyProcInfo)
// ¥¥ DNS interface
PROCEDURE (DNRDone, uppResultProcInfo)
// Starts the StrToAddress call
CDNSObject * MacTCPDriver::StrToAddress(char * hostName)
{
AssertOpen();
CMacTCPDNSObject * dns = new CMacTCPDNSObject;
ThrowIfNil_(dns);
strncpy(dns->fHostInfo.name, hostName, kMaxHostNameLen);
::Enqueue((QElemPtr) &dns->fQLink, &fWaitDNSCalls);
OSErr err = StrToAddr(hostName, &dns->fTCPHostInfo, &PROCPTR(DNRDone), (char*)dns);
if (err == noErr)
{
dns->fStatus = noErr;
dns->TCPHostInfoToOT(&dns->fTCPHostInfo);
::Dequeue((QElemPtr) &dns->fQLink,&fWaitDNSCalls);
}
else if (err != cacheFault)
{
dns->fStatus = err;
::Dequeue((QElemPtr) &dns->fQLink,&fWaitDNSCalls);
}
return dns;
}
CDNSObject * MacTCPDriver::StrToAddress(char * hostName, MacSocket * socket)
{
AssertOpen();
CMacTCPDNSObject * dns = new CMacTCPDNSObject(socket);
ThrowIfNil_(dns);
strcpy(dns->fHostInfo.name, hostName);
::Enqueue((QElemPtr) &dns->fQLink, &fWaitDNSCalls);
OSErr err = StrToAddr(hostName, &dns->fTCPHostInfo, &PROCPTR(DNRDone), (char*)dns);
if (err == noErr)
{
dns->fStatus = noErr;
dns->TCPHostInfoToOT(&dns->fTCPHostInfo);
::Dequeue((QElemPtr) &dns->fQLink,&fWaitDNSCalls);
}
else if (err != cacheFault)
{
dns->fStatus = err;
::Dequeue((QElemPtr) &dns->fQLink,&fWaitDNSCalls);
}
return dns;
}
int MacTCPDriver::GetHostName(char *name, int namelen)
{
AssertOpen();
if (namelen < 16)
return EINVAL;
struct GetAddrParamBlock pbr;
pbr.ioCRefNum = fDriverNum;
pbr.csCode = ipctlGetAddr;
InetHost thisHost;
if (PBControlSync(ParmBlkPtr(&pbr)) != noErr)
return ENETDOWN;
thisHost = pbr.ourAddress;
if (AddrToStr(thisHost, name) != noErr)
return EINVAL;
else
return 0;
}
char * MacTCPDriver::AddressToString(InetHost host)
{
AssertOpen();
assert(fHostEnt.h_name);
if (AddrToStr(host, fHostEnt.h_name) != noErr)
return NULL;
return fHostEnt.h_name;
}
#pragma mark -
#pragma mark CMacTCPDNSObject
#pragma mark -
// =========================================================================
// CMacTCPDNSObject
// =========================================================================
// DNR completion
// Interrupt level routine. No mallocs, or accessing global variables
static pascal void DNRDone (struct hostInfo * hostInformation, Ptr userData)
{
CMacTCPDNSObject * dns = (CMacTCPDNSObject*)userData;
dns->fStatus = hostInformation->rtnCode;
dns->TCPHostInfoToOT(hostInformation);
OSErr err = ::Dequeue((QElemPtr) &dns->fQLink,&((MacTCPDriver*)dns->fInterruptDriver)->fWaitDNSCalls);
::Enqueue((QElemPtr)&dns->fQLink, &(dns->fInterruptDriver)->fDoneDNSCalls);
}
CMacTCPDNSObject::CMacTCPDNSObject(MacSocket * socket) : CDNSObject(socket)
{
::HoldMemoryGlue( this, sizeof(*this) );
}
CMacTCPDNSObject::CMacTCPDNSObject() : CDNSObject()
{
::HoldMemoryGlue( this, sizeof(*this) );
}
CMacTCPDNSObject::~CMacTCPDNSObject()
{
::UnholdMemoryGlue( this, sizeof(*this) );
}
// Interrupt level routine. No mallocs, or accessing global variables. Called from DNRDone
void CMacTCPDNSObject::TCPHostInfoToOT(struct hostInfo * hostInformation)
{
int i;
for (i=0; i< NUM_ALT_ADDRS; i++)
fHostInfo.addrs[i] = hostInformation->addr[i];
fHostInfo.addrs[i] = 0;
}
#pragma mark -
#pragma mark MacTCPStream
#pragma mark -
// =========================================================================
// MacTCPStream
// =========================================================================
// ¥¥ constructors
MacTCPStream::MacTCPStream(MacSocket * socket) : CNetStream(socket)
{
// Variable init
fStream = NULL;
fBufferSize = ((MacTCPDriver*)gNetDriver)->GetStreamBufferSize();
fBuffer = malloc(fBufferSize);
ThrowIfNil_(fBuffer);
fNoMoreWrites = FALSE;
fLastCallInProgress = FALSE;
fLastCallResult = 0;
fIsBound = false;
fBoundHost = 0;
fBoundPort = ((MacTCPDriver*)gNetDriver)->GetNextLocalPort();
fPeekBuffer = NULL;
#ifdef DEBUG
fConnectCalled = FALSE;
fLastAsyncCall = 0;
#endif
}
MacTCPStream::~MacTCPStream()
{
if (fBuffer)
free(fBuffer);
}
int MacTCPStream::CreateStream()
{
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X CreateStream.\n", this);
#endif
if (fSocket->fType == SOCK_STREAM)
return CreateTypedStream(NULL); // Actually do the creation now - donÕt care about port #
else
return TCPErrorToUnix(TCPCreate, noErr); // Delay until bind() time; we need to know the port
}
// Create a UDP stream
int MacTCPStream::CreateTypedStream(InetPort port)
{
AcquirePB * apb;
// Create a new paramblock, initialized correctly
if (fSocket->fType == SOCK_STREAM)
apb = new AcquirePB(SOCK_STREAM, TCPCreate, this);
else
apb = new AcquirePB(SOCK_DGRAM, UDPCreate, this);
if (apb->fApb == NULL)
return -1;
if (fSocket->fType == SOCK_STREAM) {
apb->fApb->pb.tcpPB.csParam.create.rcvBuff = (Ptr)fBuffer;
apb->fApb->pb.tcpPB.csParam.create.rcvBuffLen = fBufferSize;
apb->fApb->pb.tcpPB.csParam.create.userDataPtr = (Ptr)this;
apb->fApb->pb.tcpPB.csParam.create.notifyProc = &PROCPTR(TCPNotifyProc);
}
else { // fSocket->fType == SOCK_STREAM
fPendingUDPReceives = 0;
apb->fApb->pb.udpPB.csParam.create.rcvBuff = (Ptr)fBuffer;
apb->fApb->pb.udpPB.csParam.create.rcvBuffLen = fBufferSize;
apb->fApb->pb.udpPB.csParam.create.userDataPtr = (Ptr)this;
apb->fApb->pb.udpPB.csParam.create.notifyProc = &PROCPTR(UDPNotifyProc);
apb->fApb->pb.udpPB.csParam.create.localPort = port;
}
((MacTCPDriver*)gNetDriver)->MakeSyncCall(apb->fApb);
if (fSocket->fType == SOCK_STREAM)
return TCPCreateComplete(apb->fApb);
else
return UDPCreateComplete(apb->fApb);
}
// Call to close the socket
// If we are not aborting, call the normal close
// close callback calls the TCPRelease
// TCPRelease callback destroys the stream
// TCPRelease cannot
int MacTCPStream::DestroyStream(Boolean abort)
{
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X DestroyStream\n", this);
#endif
int result = 0;
if ((fStream == NULL) || (fSocket == NULL))
return 0;
if (fSocket->fType == SOCK_STREAM)
result = TCPDestroyStream(abort);
else
result = UDPDestroyStream(abort);
fSocket = NULL;
return result;
}
int MacTCPStream::TCPDestroyStream(Boolean abort)
{
if (!abort) // Normal closing
{
fLastCallInProgress = FALSE; // It is OK to multiplex TCPClose calls
AnnotatedPB * apb = ((MacTCPDriver*)gNetDriver)->GetPB(fSocket->fType, TCPClose, this);
if (apb == NULL)
return -1;
apb->pb.tcpPB.csParam.close.validityFlags = timeoutValue | timeoutAction;
apb->pb.tcpPB.csParam.close.ulpTimeoutValue = CLOSE_TIMEOUT; // seconds
apb->pb.tcpPB.csParam.close.ulpTimeoutAction = 1; // 1:abort 0:report
// TimeoutAction must be abort. Otherwise, the async call never comes back,
// and we just get notified.
((MacTCPDriver*)gNetDriver)->MakeAsyncCall(apb);
return 0;
}
else
{
// WARNING: TCPRelease cannot be called asynchronously, because MacTCP gets
// confused, and reports an infinite number of callbacks (sometimes). So I make
// a sync call, and return
AcquirePB apb(SOCK_STREAM, TCPRelease, this);
if (apb.fApb == NULL)
return -1;
((MacTCPDriver*)gNetDriver)->MakeSyncCall(apb.fApb);
TCPReleaseComplete(apb.fApb); // This deletes this stream
return 0;
}
return 0;
}
int MacTCPStream::UDPDestroyStream(Boolean abort)
{
AcquirePB apb(SOCK_DGRAM, UDPRelease, this);
if (apb.fApb == NULL)
return -1;
((MacTCPDriver*)gNetDriver)->MakeSyncCall(apb.fApb);
return 0;
}
// ¥¥ completion routines
// Notification routine
// Async callback routine.
// A5 is OK. Cannot allocate memory here. Can issue other TCP calls
void MacTCPStream::TCPNotify(unsigned short eventCode,
unsigned short /* terminReason */,
struct ICMPReport * /* icmpMsg */)
{
switch (eventCode) {
case TCPClosing: // Noted
case TCPTerminate:
if (fSocket)
fSocket->CloseNotify();
fNoMoreWrites = TRUE;
break;
case TCPULPTimeout: // Ignored
case TCPUrgent:
case TCPICMPReceived:
break;
case TCPDataArrival:
default:
break;
}
// Notify the NSPR threads mechanism that an I/O interrupt has occured.
_PR_AsyncIOInterruptHandler();
}
void MacTCPStream::UDPNotify(unsigned short eventCode, struct ICMPReport *icmpMsg)
{
if (eventCode == UDPDataArrival)
fPendingUDPReceives++;
// Notify the NSPR threads mechanism that an I/O interrupt has occured.
_PR_AsyncIOInterruptHandler();
}
// ¥¥ Completion routines
// each routine:
// translates MacTCP errors into UNIX codes
// notifies sockets that it is ready
// Misc:
// See docs above
// Misc: sets the stream
int MacTCPStream::TCPCreateComplete(AnnotatedPB * apb)
{
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X TCPCreateComplete\n", this);
#endif
// Making sure MacTCP has given us the stream
if ((apb->pb.tcpPB.tcpStream == NULL) && (apb->pb.tcpPB.ioResult == noErr))
apb->pb.tcpPB.ioResult = insufficientResources;
if (apb->pb.tcpPB.ioResult != noErr)
apb->pb.tcpPB.tcpStream = NULL;
fStream = apb->pb.tcpPB.tcpStream; // We are ready to roll
return TCPErrorToUnix(apb->pb.tcpPB.csCode, apb->pb.tcpPB.ioResult);
}
int MacTCPStream::UDPCreateComplete(AnnotatedPB * apb)
{
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X TCPCreateComplete\n", this);
#endif
// Making sure MacTCP has given us the stream
if ((apb->pb.udpPB.udpStream == NULL) && (apb->pb.udpPB.ioResult == noErr))
apb->pb.udpPB.ioResult = insufficientResources;
if (apb->pb.udpPB.ioResult != noErr)
apb->pb.udpPB.udpStream = NULL;
fBoundPort = apb->pb.udpPB.csParam.create.localPort;
fStream = apb->pb.udpPB.udpStream; // We are ready to roll
return TCPErrorToUnix(apb->pb.udpPB.csCode, apb->pb.udpPB.ioResult);
}
int MacTCPStream::TCPActiveOpenComplete(AnnotatedPB * apb)
{
short code, result;
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X TCPActiveOpenComplete\n", this);
#endif
if (apb->streamType == SOCK_STREAM) {
code = apb->pb.tcpPB.csCode;
result = apb->pb.tcpPB.ioResult;
}
else {
code = apb->pb.udpPB.csCode;
result = apb->pb.udpPB.ioResult;
}
int err = TCPErrorToUnix(code, result);
if (fSocket)
fSocket->ConnectComplete(FALSE, err);
return err;
}
int MacTCPStream::TCPSendComplete(AnnotatedPB * apb)
{
Cwds * wds = (Cwds *)apb->pb.tcpPB.csParam.send.wdsPtr;
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X TCPSendComplete, wds: %Xd\n", this, wds);
#endif
if (wds != NULL)
delete wds;
else
assert(FALSE);
int err = TCPErrorToUnix(apb->pb.tcpPB.csCode, apb->pb.tcpPB.ioResult);
if (fSocket)
fSocket->WriteComplete(err);
return err;
}
int MacTCPStream::TCPPassiveOpenComplete(AnnotatedPB * apb)
{
int err = TCPErrorToUnix(apb->pb.tcpPB.csCode, apb->pb.tcpPB.ioResult);
if (fSocket)
fSocket->AcceptComplete(err);
return err;
}
// Makes the release call
int MacTCPStream::TCPCloseComplete(AnnotatedPB * apb)
{
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X TCPCloseComplete\n", this);
#endif
int err = TCPErrorToUnix(apb->pb.tcpPB.csCode, apb->pb.tcpPB.ioResult);
AcquirePB newApb(SOCK_STREAM, TCPRelease, this);
if ( newApb.fApb == NULL)
return -1;
((MacTCPDriver*)gNetDriver)->MakeSyncCall(newApb.fApb);
TCPReleaseComplete(newApb.fApb);
return err;
}
// deletes the stream
int MacTCPStream::TCPReleaseComplete(AnnotatedPB * apb)
{
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X TCPReleaseComplete\n", this);
#endif
// ¥¥¥ LAM, it would be nice if we checked for any outstanding async calls here
int err = TCPErrorToUnix(apb->pb.tcpPB.csCode, apb->pb.tcpPB.ioResult);
#ifdef DEBUG
if (apb->pb.tcpPB.csParam.create.rcvBuffLen != apb->stream->fBufferSize)
{
::DebugStr("\pMacTCP Release leakage;g;");
apb->stream->fBuffer = NULL;
}
#endif
delete apb->stream;
return err;
}
// Giant lookup table of tcp error codes, to socket ones
// Everyone can get our internal driverError or streamBusy
// TCPCreate, TCPActiveOpen, TCPClose, TCPRelease, TCPSend, TCPNoCpyRvc,
// TCPRcvBfrReturn, TCPStatus
Int32 MacTCPStream::TCPErrorToUnix(int tcpCall, int error)
{
if (error == 0)
return 0;
switch (tcpCall)
{
case TCPCreate:
// possible errors:
// streamAlreadyOpen, invalidLength, invalidBufPtr, insufficientResources
// Maps to connect errors
if ((error == streamAlreadyOpen) || (error == streamBusy))
return EACCESS;
else
return ENOBUFS;
break;
case TCPActiveOpen:
// possible errors
// invalidStreamPtr, connectionExists, duplicateSocket, openFailed,
switch (error)
{
case invalidStreamPtr:
return EBADF;
case connectionExists:
return EALREADY;
case duplicateSocket:
return EADDRINUSE;
case openFailed:
return ECONNREFUSED;
case driverError:
return EBADF;
case streamBusy:
return EINPROGRESS;
case connectionTerminated:
return ECONNREFUSED;
default:
assert(FALSE);
return EBADF;
}
break;
case TCPSend:
// possible errors
// invalidStreamPtr, invalidLength, invalidWDS the WDS pointer was 0
// connectionDoesntExist, connectionClosing ,connectionTerminated
switch (error)
{
case invalidStreamPtr:
return EINVAL;
case invalidLength:
case invalidWDS:
return EFAULT;
case connectionDoesntExist:
case connectionClosing:
case connectionTerminated:
return EPIPE;
case driverError:
return EPIPE;
case streamBusy:
return EWOULDBLOCK;
default:
assert(FALSE);
return EINVAL;
}
break;
case TCPRcv:
switch (error)
{
case connectionClosing:
case connectionTerminated:
return 0;
case streamBusy:
return EWOULDBLOCK;
case invalidStreamPtr:
case connectionDoesntExist:
case invalidLength:
case invalidBufPtr:
case commandTimeout:
case driverError:
default:
return EIO;
}
break;
case TCPClose:
return error;
break;
case TCPRelease:
return error;
case TCPPassiveOpen:
return error;
case TCPNoCopyRcv:
case TCPRcvBfrReturn:
case TCPAbort:
case TCPStatus:
case TCPExtendedStat:
case TCPGlobalInfo:
case TCPCtlMax:
assert(FALSE);
}
/*
case inProgress = 1, // I/O in progress
case ipBadLapErr = -23000, // bad network configuration
case ipBadCnfgErr = -23001, // bad IP configuration error
case ipNoCnfgErr = -23002, // missing IP or LAP configuration error
case ipLoadErr = -23003, //error in MacTCP load
case ipBadAddr = -23004, // error in getting address
case connectionClosing = -23005, // connection is closing
case invalidLength = -23006,
case connectionExists = -23007, // this TCP stream already has an open connection
case connectionDoesntExist = -23008, // connection does not exist
case insufficientResources = -23009, // insufficient resources to perform request
case invalidStreamPtr = -23010, // the specified TCP stream is not open
case streamAlreadyOpen = -23011,
case connectionTerminated = -23012,
case invalidBufPtr = -23013,
case invalidRDS = -23014,
case invalidWDS = -23014,
case openFailed = -23015, // the connection came halfway up and then failed
case commandTimeout = -23016,
case duplicateSocket = -23017 // a connection already exists between this local IP address and TCP port, and the specified remote IP address and TCP port
*/
return error;
}
// Connect to a given host, port
// Asynchronous, needs to keep state to know if it has already returned an error code
// returns
// EINPROGRESS if connection is being made
// ECONNREFUSED if connection is closing/broken
// 0 if connection is up.
int MacTCPStream::ActiveConnect(InetHost host, InetPort port)
{
short state;
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X TCP Active Connect starting\n", this);
#endif
#ifdef DEBUG
if (fConnectCalled)
BreakToSourceDebugger_(); // Should call this only once from the socket layer
fConnectCalled = TRUE;
#endif
// Make sure that we have a stream
AcquirePB acqPB(SOCK_STREAM, TCPStatus, this);
if ( acqPB.fApb == NULL )
return ECONNREFUSED;
OSErr err = ((MacTCPDriver*)gNetDriver)->MakeSyncCall(acqPB.fApb);
// What is our connection state
// What are the proper errors to return here?
// If closing, tell them that the remote peer has closed the connection
// possible error: we decided to close
// If we are listening, tell them address is in use
// If we are already trying to establish, tell them so
if (err == noErr) {
state = acqPB.fApb->pb.tcpPB.csParam.status.connectionState;
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X TCP Active Connect current state is %d\n", this, state);
#endif
switch (state)
{
case 0: // Closed no connection exists on this stream
break;
case 2: // Listen listening for an incoming connection
case 4: // SYN received incoming connection is being established
case 6: // SYN sent outgoing connection is being established
return EINPROGRESS;
case 8: // Established connection is up
return 0;
case 10: // FIN Wait 1 connection is up; close has been issued
case 12: // FIN Wait 2 connection is up; close has been completed
case 14: // Close Wait connection is up; close has been received
case 16: // Closing connection is up; close has been issued and received
case 18: // Last Ack connection is up; close has been issued and received
case 20: // Time Wait connection is being broken
return ECONNREFUSED;
}
}
AnnotatedPB * capb = NULL;
if ((err == connectionDoesntExist) ||
((err == noErr) && acqPB.fApb->pb.tcpPB.csParam.status.connectionState == 0))
// No connection, make a call to open it
{
capb = ((MacTCPDriver*)gNetDriver)->GetPB(SOCK_STREAM, TCPActiveOpen, this);
if (capb == NULL)
return -1;
capb->pb.tcpPB.csParam.open.remoteHost = host;
capb->pb.tcpPB.csParam.open.remotePort = port;
capb->pb.tcpPB.csParam.open.validityFlags = timeoutValue | timeoutAction;
capb->pb.tcpPB.csParam.open.ulpTimeoutValue = OPEN_TIMEOUT;
capb->pb.tcpPB.csParam.open.ulpTimeoutAction = 1; // 1:abort 0:report
capb->pb.tcpPB.csParam.open.localHost = fBoundHost;
capb->pb.tcpPB.csParam.open.localPort = fBoundPort;
((MacTCPDriver*)gNetDriver)->MakeAsyncCall(capb);
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %Xd TCP Active Connect issued.\n", this);
#endif
}
return EINPROGRESS;
}
// This is dependent on the type of connection. For TCP, itÕs
// easy; we donÕt bind until connect time.
// For UDP, we actually create the socket here since the port
// is specified at UDPCreate() time.
int MacTCPStream::Bind(InetHost host, InetPort port)
{
int returnValue;
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X Request bind to host %#x, port %d\n", this, host, port);
#endif
switch (fSocket->fType) {
case SOCK_STREAM:
fBoundHost = host;
if (port != 0) // Should we override the allcoated one?
fBoundPort = port;
returnValue = 0;
break;
case SOCK_DGRAM: {
returnValue = TCPErrorToUnix(UDPCreate, CreateTypedStream(port));
break;
}
default:
assert(false); // Better never happen
}
fIsBound = (noErr == returnValue);
return returnValue;
}
// Backlog parameter is ignored for MacTCP streams
int MacTCPStream::Listen(int /* backlog*/)
{
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X TCP Listen\n", this);
#endif
#ifdef DEBUG
if (fConnectCalled)
BreakToSourceDebugger_(); // Should call this only once from the socket layer
fConnectCalled = TRUE;
#endif
gNetDriver->SpendTime();
AcquirePB acqPB(SOCK_STREAM, TCPStatus, this);
if ( acqPB.fApb == NULL )
return -1;
((MacTCPDriver*)gNetDriver)->MakeSyncCall(acqPB.fApb);
// What is our connection state
// What are the proper errors to return here?
// If closing, tell them that the remote peer has closed the connection
// possible error: we decided to close
// If we are listening, tell them address is in use
// If we are already trying to establish, tell them so
switch (acqPB.fApb->pb.tcpPB.csParam.status.connectionState)
{
case 0: // Closed no connection exists on this stream
break;
case 2: // Listen listening for an incoming connection
case 4: // SYN received incoming connection is being established
case 6: // SYN sent outgoing connection is being established
return EINPROGRESS;
case 8: // Established connection is up
return EISCONN;
// ¥¥¥ LAM, I am not sure what the status should be here
case 10: // FIN Wait 1 connection is up; close has been issued
case 12: // FIN Wait 2 connection is up; close has been completed
case 14: // Close Wait connection is up; close has been received
case 16: // Closing connection is up; close has been issued and received
case 18: // Last Ack connection is up; close has been issued and received
case 20: // Time Wait connection is being broken
return EISCONN;
}
// Make the call
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X TCP Listen making the call\n", this);
#endif
AnnotatedPB * apb = ((MacTCPDriver*)gNetDriver)->GetPB(SOCK_STREAM, TCPPassiveOpen, this);
if (apb == NULL)
return -1;
apb->pb.tcpPB.csParam.open.ulpTimeoutValue = 255;
apb->pb.tcpPB.csParam.open.ulpTimeoutAction = 1; // 1:abort 0:report
apb->pb.tcpPB.csParam.open.commandTimeoutValue = 0; // infinity
apb->pb.tcpPB.csParam.open.localHost = fBoundHost;
apb->pb.tcpPB.csParam.open.localPort = fBoundPort;
((MacTCPDriver*)gNetDriver)->MakeAsyncCall(apb);
// Success/failure is figured out in accept
return 0;
}
int MacTCPStream::Accept()
{
AcquirePB acqPB(SOCK_STREAM, TCPStatus, this);
if (acqPB.fApb == NULL)
return -1;
OSErr err = ((MacTCPDriver*)gNetDriver)->MakeSyncCall(acqPB.fApb);
// What is our connection state
// What are the proper errors to return here?
// If closing, tell them that the remote peer has closed the connection
// possible error: we decided to close
// If we are listening, tell them address is in use
// If we are already trying to establish, tell them so
if (err == noErr)
switch (acqPB.fApb->pb.tcpPB.csParam.status.connectionState)
{
case 0: // Closed no connection exists on this stream
return ECONNREFUSED;
case 2: // Listen listening for an incoming connection
case 4: // SYN received incoming connection is being established
case 6: // SYN sent outgoing connection is being established
return EWOULDBLOCK;
case 8: // Established connection is up
return 0;
case 10: // FIN Wait 1 connection is up; close has been issued
case 12: // FIN Wait 2 connection is up; close has been completed
case 14: // Close Wait connection is up; close has been received
case 16: // Closing connection is up; close has been issued and received
case 18: // Last Ack connection is up; close has been issued and received
case 20: // Time Wait connection is being broken
return ECONNREFUSED;
}
else
return EBADF;
return -1; // Never reached
}
// Only one write call at a time is allowed
// bytesWritten is always set to whatever we try to write, but it is only valid if
// we return no error
int MacTCPStream::Write(const void *buffer, unsigned int buflen, UInt16& bytesWritten)
{
short state;
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X Starting TCP Write, length %d\n", this, buflen);
#endif
bytesWritten = 0;
AcquirePB acqPB(SOCK_STREAM, TCPStatus, this);
if ( acqPB.fApb == NULL )
return -1;
((MacTCPDriver*)gNetDriver)->MakeSyncCall(acqPB.fApb);
state = acqPB.fApb->pb.tcpPB.csParam.status.connectionState;
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X TCP Write socket status: %d\n", this, state);
#endif
switch (state)
{
case 0: // Closed no connection exists on this stream
case 2: // Listen listening for an incoming connection
return EPIPE;
case 4: // SYN received incoming connection is being established
case 6: // SYN sent outgoing connection is being established
return EWOULDBLOCK;
case 8: // Established connection is up
break;
case 10: // FIN Wait 1 connection is up; close has been issued
case 12: // FIN Wait 2 connection is up; close has been completed
case 14: // Close Wait connection is up; close has been received
case 16: // Closing connection is up; close has been issued and received
case 18: // Last Ack connection is up; close has been issued and received
case 20: // Time Wait connection is being broken
return EPIPE;
}
// At this point, we are doing one of these:
// - Establishing a connection
// - Waiting for another write to complete
// - Nothing
// Wait for all async calls to complete, or user interrupt
if (fNoMoreWrites)
return EPIPE;
// We might have closed by now, process the notifications
// If there was an error, return IO error
if (fLastCallResult != noErr)
return EIO;
// Prepare all the stuctures (wds, and the buffer), copy the data to the buffer
bytesWritten = buflen > MAX_SEND_BLOCK ? MAX_SEND_BLOCK : buflen;
Cwds * volatile wds = NULL;
Try_
{
wds = new Cwds(bytesWritten, buffer);
}
Catch_(inErr)
{
if (wds)
delete wds;
return EWOULDBLOCK;
}
EndCatch_
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X TCP Write, cwd is %xd\n", this, wds);
#endif
// Complete the calling structure, make the call
AnnotatedPB * apb = ((MacTCPDriver*)gNetDriver)->GetPB(SOCK_STREAM, TCPSend, this);
if (apb == NULL)
return -1;
apb->pb.tcpPB.csParam.send.validityFlags = timeoutValue | timeoutAction;
apb->pb.tcpPB.csParam.send.ulpTimeoutValue = SEND_TIMEOUT; // seconds
apb->pb.tcpPB.csParam.send.ulpTimeoutAction = 1; // 1:abort 0:report
// pushFlag, urgentFlag are all 0
apb->pb.tcpPB.csParam.send.wdsPtr = (Ptr)wds;
((MacTCPDriver*)gNetDriver)->MakeAsyncCall(apb);
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X TCP Write, complete.\n", this, wds);
#endif
return 0;
}
int MacTCPStream::Read(void *buffer, unsigned int buflen, UInt16& bytesRead)
{
bytesRead = 0;
UInt16 bytesToRead = 0;
Boolean readAlready = FALSE;
// Check our status if we do not already have the data
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X TCP Read starts. Reading %d bytes,\n", this, buflen);
#endif
readagain: // For speed, we loop through the read loop
// We can read, even if the connection is closing
// Some data might already be in the buffer
{
AcquirePB acqPB(SOCK_STREAM, TCPStatus, this);
if ( acqPB.fApb == NULL )
return -1;
((MacTCPDriver*)gNetDriver)->MakeSyncCall(acqPB.fApb);
switch (acqPB.fApb->pb.tcpPB.csParam.status.connectionState)
{
case 0: // Closed no connection exists on this stream
case 2: // Listen listening for an incoming connection
return EPIPE;
case 4: // SYN received incoming connection is being established
case 6: // SYN sent outgoing connection is being established
case 8: // Established connection is up
break;
case 10: // FIN Wait 1 connection is up; close has been issued
case 12: // FIN Wait 2 connection is up; close has been completed
case 14: // Close Wait connection is up; close has been received
case 16: // Closing connection is up; close has been issued and received
case 18: // Last Ack connection is up; close has been issued and received
case 20: // Time Wait connection is being broken
if (acqPB.fApb->pb.tcpPB.csParam.status.amtUnreadData == 0) // Nothing more to read, die now
return 0;
break;
}
bytesToRead = acqPB.fApb->pb.tcpPB.csParam.status.amtUnreadData > buflen ? buflen : acqPB.fApb->pb.tcpPB.csParam.status.amtUnreadData;
}
if (readAlready && (bytesToRead < 500)) // Do not bother with repetitive reads for small chunks
return 0;
if (bytesToRead == 0)
return EWOULDBLOCK;
// Make the rcv call
{
AcquirePB acqPB(SOCK_STREAM, TCPRcv, this);
if ( acqPB.fApb == NULL )
return -1;
acqPB.fApb->pb.tcpPB.csParam.receive.commandTimeoutValue = 0; /* seconds, 0 = blocking */
acqPB.fApb->pb.tcpPB.csParam.receive.rcvBuff = (Ptr)buffer;
acqPB.fApb->pb.tcpPB.csParam.receive.rcvBuffLen = bytesToRead;
((MacTCPDriver*)gNetDriver)->MakeSyncCall(acqPB.fApb);
if (acqPB.fApb->pb.tcpPB.ioResult != 0)
return TCPErrorToUnix(TCPRcv, acqPB.fApb->pb.tcpPB.ioResult);
bytesRead += acqPB.fApb->pb.tcpPB.csParam.receive.rcvBuffLen;
buflen -= acqPB.fApb->pb.tcpPB.csParam.receive.rcvBuffLen; // Adjust the pointers in case we read again
buffer = (void*)((UInt32)buffer + (UInt32)acqPB.fApb->pb.tcpPB.csParam.receive.rcvBuffLen);
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X TCP Read had : %d\n", this, bytesRead);
#endif
}
readAlready = TRUE;
goto readagain;
return 0;
}
Boolean MacTCPStream::Select(Boolean& readReady,Boolean& writeReady,Boolean& exceptReady)
{
AcquirePB acqPB(SOCK_STREAM, TCPStatus, this);
if ( acqPB.fApb == NULL )
return false;
readReady = writeReady = exceptReady = FALSE;
((MacTCPDriver*)gNetDriver)->MakeSyncCall(acqPB.fApb);
if (acqPB.fApb->pb.tcpPB.ioResult != 0)
exceptReady = TRUE;
else
{
switch (acqPB.fApb->pb.tcpPB.csParam.status.connectionState)
{
case 0: // Closed no connection exists on this stream
case 2: // Listen listening for an incoming connection
break;
case 8: // Established connection is up
writeReady = TRUE;
break;
case 4: // SYN received incoming connection is being established
case 6: // SYN sent outgoing connection is being established
case 10: // FIN Wait 1 connection is up; close has been issued
case 12: // FIN Wait 2 connection is up; close has been completed
break;
case 14: // Close Wait connection is up; close has been received
case 16: // Closing connection is up; close has been issued and received
case 18: // Last Ack connection is up; close has been issued and received
case 20: // Time Wait connection is being broken
exceptReady = TRUE; // Not sure what to do here
}
readReady = acqPB.fApb->pb.tcpPB.csParam.status.amtUnreadData > 0;
#ifdef TCP_DEBUG
// XP_Trace("MacTCPDriver: TCP Select %Xd, read %d, write %d, exception %d\n", this, readReady, writeReady, exceptReady);
#endif
}
return (readReady || writeReady || exceptReady);
}
int MacTCPStream::GetPeerName(InetHost& host, InetPort& port)
{
AcquirePB acqPB(SOCK_STREAM, TCPStatus, this);
if (acqPB.fApb == NULL)
return -1;
((MacTCPDriver*)gNetDriver)->MakeSyncCall(acqPB.fApb);
switch (acqPB.fApb->pb.tcpPB.csParam.status.connectionState)
{
case 8: // Established connection is up
case 10: // FIN Wait 1 connection is up; close has been issued
case 12: // FIN Wait 2 connection is up; close has been completed
case 14: // Close Wait connection is up; close has been received
case 16: // Closing connection is up; close has been issued and received
case 18: // Last Ack connection is up; close has been issued and received
case 20: // Time Wait connection is being broken
host = acqPB.fApb->pb.tcpPB.csParam.status.remoteHost;
port = acqPB.fApb->pb.tcpPB.csParam.status.remotePort;
return 0;
case 0: // Closed no connection exists on this stream
case 2: // Listen listening for an incoming connection
case 4: // SYN received incoming connection is being established
case 6: // SYN sent outgoing connection is being established
return ENOTCONN;
}
return ENOTCONN;
}
int MacTCPStream::GetSockName(InetHost& host, InetPort& port)
{
AcquirePB acqPB(SOCK_STREAM, TCPStatus, this);
if ( acqPB.fApb == NULL )
return -1;
((MacTCPDriver*)gNetDriver)->MakeSyncCall(acqPB.fApb);
switch (acqPB.fApb->pb.tcpPB.csParam.status.connectionState)
{
case 8: // Established connection is up
case 10: // FIN Wait 1 connection is up; close has been issued
case 12: // FIN Wait 2 connection is up; close has been completed
case 14: // Close Wait connection is up; close has been received
case 16: // Closing connection is up; close has been issued and received
case 18: // Last Ack connection is up; close has been issued and received
case 20: // Time Wait connection is being broken
host = acqPB.fApb->pb.tcpPB.csParam.status.localHost;
port = acqPB.fApb->pb.tcpPB.csParam.status.localPort;
return 0;
case 0: // Closed no connection exists on this stream
// Can be called after BIND. So, if fBoundHost != 0, fake it
if (fIsBound)
{
host = fBoundHost;
port = fBoundPort;
return 0;
}
else
return ENOTCONN;
case 2: // Listen listening for an incoming connection
case 4: // SYN received incoming connection is being established
case 6: // SYN sent outgoing connection is being established
return ENOTCONN;
}
return ENOTCONN;
}
int MacTCPStream::UDPSendTo(const void *msg, unsigned int msgLen, unsigned int flags,
InetHost host, InetPort port, unsigned int& bytesSent)
{
Cwds * volatile wds = NULL;
short ioResult;
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X UDP SendTo, msgLen = %d, host = %#x, port = %d\n", this, msgLen, host, port);
DumpBuffer(msg, msgLen);
#endif
// We need to create the Write Data Structure
bytesSent = (msgLen > MAX_SEND_BLOCK) ? MAX_SEND_BLOCK : msgLen;
Try_
{
wds = new Cwds(bytesSent, msg);
}
Catch_(inErr)
{
if (wds)
delete wds;
return EWOULDBLOCK;
}
EndCatch_
#ifdef TCP_DEBUG
XP_Trace("MacTCPDriver: %X UDP Send To, cwd is %#x\n", this, wds);
#endif
// Complete the calling structure, make the call
AnnotatedPB * apb = ((MacTCPDriver*)gNetDriver)->GetPB(SOCK_DGRAM, UDPWrite, this);
if (apb == NULL)
return -1;
apb->pb.udpPB.csParam.send.remoteHost = host;
apb->pb.udpPB.csParam.send.remotePort = port;
apb->pb.udpPB.csParam.send.wdsPtr = (Ptr)wds;
apb->pb.udpPB.csParam.send.checkSum = false;
apb->pb.udpPB.csParam.send.filler = 0;
apb->pb.udpPB.csParam.send.userDataPtr = (Ptr)this;
((MacTCPDriver*)gNetDriver)->MakeSyncCall(apb);
// Writes are done synchronously, we can tear down our data structures.
delete wds;
// Tell our socket weÕre done.
fSocket->WriteComplete(ioResult = apb->pb.udpPB.ioResult);
// Success?
if (ioResult == noErr)
return noErr;
else if (ioResult == insufficientResources)
return EWOULDBLOCK;
else
return -1;
}
int MacTCPStream::UDPReadIntoBuffer(void* buffer, unsigned int len, struct sockaddr* from)
{
short bytesToRead;
short receivedBufferLen;
sockaddr_in* returnAddress;
AnnotatedPB * apb = ((MacTCPDriver*)gNetDriver)->GetPB(SOCK_DGRAM, UDPRead, this);
ThrowIfNil_(apb);
apb->pb.udpPB.csParam.receive.timeOut = 2; // Minimum suported.
ThrowIfOSErr_(((MacTCPDriver*)gNetDriver)->MakeSyncCall(apb)); // Go for it. Asynch not supported.
ThrowIfOSErr_(apb->pb.udpPB.ioResult); // Successful?
receivedBufferLen = apb->pb.udpPB.csParam.receive.rcvBuffLen;
// Read was successful, get what we want out of the param block.
bytesToRead = MIN(receivedBufferLen, len); // How much to read?
BlockMoveData(apb->pb.udpPB.csParam.receive.rcvBuff, buffer, bytesToRead); // What do we do with the extra?
returnAddress = (sockaddr_in *)from; // Pull out the senderÕs address
from->sa_len = offsetof(sockaddr,sa_data) + sizeof(in_addr);
from->sa_family = SOCK_DGRAM;
returnAddress->sin_port = apb->pb.udpPB.csParam.receive.remotePort;
returnAddress->sin_addr.s_addr = apb->pb.udpPB.csParam.receive.remoteHost;
// Release the buffer
apb->pb.udpPB.csCode = UDPBfrReturn;
((MacTCPDriver*)gNetDriver)->MakeSyncCall(apb); // Release it.
fPendingUDPReceives--;
return bytesToRead;
}
int MacTCPStream::UDPReceiveFrom(void *buffer, unsigned int len, unsigned int flags, struct sockaddr *from, unsigned int& fromLen)
{
int numBytes;
unsigned short receivedBufferLen;
unsigned int bytesToRead;
sockaddr_in * returnAddress;
Boolean isPeek;
sockaddr_in* fromAddress = (sockaddr_in *)from;
// Are we doing a peek?
isPeek = (flags & MSG_PEEK);
ThrowIfNil_(from);
ThrowIf_(fromLen < sizeof(sockaddr_in));
ThrowIfNil_(buffer);
// If nothing has been received, donÕt try to read
if ((fPendingUDPReceives == 0) && (NULL == fPeekBuffer))
return EWOULDBLOCK;
// 4 different situations here:
// 1) We are doing a peek & havenÕt done one before Ñ> read into buffer
// 2) We are doing a peek & have done one before Ñ> donÕt read
// 3) We are doing a read & we havenÕt done a peek Ñ> read directly
// 4) We are doing a read & weÕve done a peek Ñ> read from buffer & clear buffer
Try_{
if (isPeek && (NULL == fPeekBuffer)) {
fPeekBuffer = (PeekBuffer *)malloc(fBufferSize);
ThrowIfNil_(fPeekBuffer);
numBytes = UDPReadIntoBuffer(fPeekBuffer, fBufferSize, from);
fPeekBuffer->from = *from;
}
else if (isPeek && (NULL != fPeekBuffer)) {
numBytes = MIN(len, fBufferSize);
::BlockMoveData(&(fPeekBuffer->data), buffer, numBytes);
*from = fPeekBuffer->from;
}
else if (!isPeek && (NULL == fPeekBuffer)) {
numBytes = UDPReadIntoBuffer(buffer, len, from);
}
else { // !isPeek && (NULL != fPeekBuffer)
numBytes = MIN(len, fBufferSize);
::BlockMoveData(&(fPeekBuffer->data), buffer, numBytes);
*from = fPeekBuffer->from;
free(fPeekBuffer);
fPeekBuffer = NULL;
}
}
Catch_(inErr) {
return -1;
}
fromLen = from->sa_len;
return numBytes;
}
int MacTCPStream::SetSocketOption(int level, int optname, const void *optval)
{
// No applicable options.
return 0;
}
int MacTCPStream::BytesAvailable(size_t& bytesAvailable)
{
AcquirePB acqPB(SOCK_STREAM, TCPStatus, this);
if ( acqPB.fApb == NULL )
return -1;
if (((MacTCPDriver*)gNetDriver)->MakeSyncCall(acqPB.fApb) != noErr)
return -1;
if (acqPB.fApb->pb.tcpPB.ioResult != noErr)
return -1;
bytesAvailable = acqPB.fApb->pb.tcpPB.csParam.status.amtUnreadData;
return noErr;
}
#ifdef PROFILE
#pragma profile off
#endif