/* File: IrIASServer.c Contains: Implementation of the TIASServer class */ //#include "IrGlue.h" // includes CommErrors.h #include "IrIASServer.h" #include "IrIASService.h" #include "IrEvent.h" #include "IrLSAPConn.h" #include "CBufferSegment.h" #include "IrGlue.h" #include "IrDALog.h" #if (hasTracing > 0 && hasIASServerTracing > 0) enum IrIASServerTraceCodes { kNullEvent = 1, kDestroy, kInit, kUnexpectedEvent, kLogNextState, kResettingToListenEvent, kListenRequestEvent, kListenReplyEvent, kAcceptRequestEvent, kAcceptReplyEvent, kDisconnectRequestEvent, kDisconnectReplyEvent, kGetDataRequestEvent, kGetDataReplyEvent, kPutDataRequestEvent, kPutDataReplyEvent, kSendResponseEvent, kParseInputEvent, kParseRequestEvent, kEnqueueEvent, kDequeueEventStart, kDequeueEventEnd }; EventTraceCauseDesc TraceEvents[] = { {kNullEvent, "iasserver: create obj="}, {kDestroy, "iasserver: destroy obj="}, {kInit, "iasserver: init"}, {kUnexpectedEvent, "iasserver: unexpected event"}, {kLogNextState, "iasserver: nextstate, result=,event="}, {kResettingToListenEvent, "iasserver: resetting to listen"}, {kListenRequestEvent, "iasserver: listen request"}, {kListenReplyEvent, "iasserver: listen reply"}, {kAcceptRequestEvent, "iasserver: accept request"}, {kAcceptReplyEvent, "iasserver: accept reply"}, {kDisconnectRequestEvent, "iasserver: disconnect request"}, {kDisconnectReplyEvent, "iasserver: disconnect reply"}, {kGetDataRequestEvent, "iasserver: get data request"}, {kGetDataReplyEvent, "iasserver: get data reply"}, {kPutDataRequestEvent, "iasserver: put data request"}, {kPutDataReplyEvent, "iasserver: put data reply"}, {kSendResponseEvent, "iasserver: send ias response"}, {kParseInputEvent, "iasserver: parse ias input"}, {kParseRequestEvent, "iasserver: parse ias request"}, {kEnqueueEvent, "iasserver: Event Queued"}, {kDequeueEventStart, "iasserver: Event Start"}, {kDequeueEventEnd, "iasserver: Event End"} }; #define XTRACE(x, y, z) IrDALogAdd ( x, y, z, TraceEvents, true) #else #define XTRACE(x, y, z) ((void)0) #endif //-------------------------------------------------------------------------------- #define super TIrStream OSDefineMetaClassAndStructors(TIASServer, TIrStream); //-------------------------------------------------------------------------------- //-------------------------------------------------------------------------------- // TIASServer //-------------------------------------------------------------------------------- /*static*/ TIASServer * TIASServer::tIASServer(TIrGlue* irda, TIASService *nameService) { TIASServer *obj = new TIASServer; XTRACE(kNullEvent, (int)obj >> 16, (short)obj); if (obj && !obj->Init(irda, nameService)) { obj->release(); obj = nil; } return obj; } //-------------------------------------------------------------------------------- // free //-------------------------------------------------------------------------------- void TIASServer::free() { XTRACE(kDestroy, (int)this >> 16, (short)this); #define FREE(x) { if (x) { (x)->release(); x = nil; } } FREE(fLSAPConn); // free the lsap connection if (fRequestReply) { fIrDA->ReleaseEventBlock(fRequestReply); fRequestReply = nil; } // Delete the buffer if (fGetPutBuffer) { fGetPutBuffer->Delete(); // jdg: new style buffer release fGetPutBuffer = nil; } super::free(); } // TIASServer::free //-------------------------------------------------------------------------------- // Init //-------------------------------------------------------------------------------- Boolean TIASServer::Init(TIrGlue* irda, TIASService *nameService) { XTRACE(kInit, (int)this >> 16, (short)this); int Listen_Start_Commented_Out; fOpCode = kIASOpUnassigned; fReceiveState = kIASServerReceiveStart; fNameService = nil; fLSAPConn = nil; fRequestReply = nil; fGetPutBuffer = nil; // Init IrStream #if (hasTracing > 0 && hasIASServerTracing > 0) if (!super::Init(irda, TraceEvents, kEnqueueEvent)) return false; #else if (!super::Init(irda)) return false; #endif // save name db service fNameService = nameService; // need an lsapconn fLSAPConn = TLSAPConn::tLSAPConn(irda, this); require(fLSAPConn, Fail); fRequestReply = fIrDA->GrabEventBlock(); // get an event block for us (wait til later?) require(fRequestReply, Fail); // Allocate, init a buffer segment fGetPutBuffer = CBufferSegment::New(kIASServerBufferSize); require(fGetPutBuffer, Fail); // Claim the well-known NameServer LSAP id fLSAPConn->AssignId(kNameServerLSAPId); // Start everything off ////// ListenStart(); return true; Fail: return false; } // TIASServer::Init //-------------------------------------------------------------------------------- // NextState //-------------------------------------------------------------------------------- void TIASServer::NextState(ULong event) { TIrEvent* reqOrReply = GetCurrentEvent(); XTRACE(kLogNextState, reqOrReply->fResult, event); require(reqOrReply == (TIrEvent *)fRequestReply, Fail); if (reqOrReply->fResult != noErr) { // if previous request failed and if (reqOrReply->fEvent != kIrDisconnectReplyEvent && // neither a disconnect or listen reply reqOrReply->fEvent != kIrListenReplyEvent) { // then let's do a disconnect to clean up XTRACE(kDisconnectRequestEvent, (int)this >> 16, (short)this); reqOrReply->fEvent = kIrDisconnectRequestEvent; // request a disconnect fLSAPConn->EnqueueEvent(reqOrReply); return; } } switch (event) { case kIrDisconnectReplyEvent: // disconnect finished, start up a listen again XTRACE(kResettingToListenEvent, 0, 0); // fall through case kIrListenRequestEvent: ListenStart(); // xtrace inside ListenStart() break; case kIrListenReplyEvent: XTRACE(kListenReplyEvent, (int)this >> 16, (short)this); if (reqOrReply->fResult == noErr) { // if listen worked XTRACE(kAcceptRequestEvent, 0, 0); // Send the listen reply back as the accept TIrConnLstnRequest* acceptRequest = (TIrConnLstnRequest*)GetCurrentEvent(); acceptRequest->fEvent = kIrAcceptRequestEvent; fLSAPConn->EnqueueEvent(acceptRequest); } else { // if it failed, listen again ListenStart(); } break; case kIrAcceptReplyEvent: XTRACE(kAcceptReplyEvent, (int)this >> 16, (short)this); GetStart(); break; case kIrPutDataReplyEvent: XTRACE(kPutDataReplyEvent, (int)this >> 16, (short)this); GetStart(); break; case kIrGetDataReplyEvent: XTRACE(kGetDataReplyEvent, (int)this >> 16, (short)this); ParseInput(); break; default: XTRACE(kUnexpectedEvent, 0, event); DebugLog("TIASServer::NextState: unknown event"); break; } Fail: return; } // TIASServer::NextState //================================ Helper methods ================================ //-------------------------------------------------------------------------------- // ParseInput //-------------------------------------------------------------------------------- void TIASServer::ParseInput() { UByte ctrlByte; Boolean lastFrame; Boolean ackedFrame; UByte iasReturnCode; TIASAttribute* iasEntry; // An operation frame has been received - parse it and decide what to do with it fGetPutBuffer->Seek(0, kPosBeg); ctrlByte = fGetPutBuffer->Get(); lastFrame = ctrlByte & kIASFrameLstBit; ackedFrame = ctrlByte & kIASFrameAckBit; XTRACE(kParseInputEvent, ctrlByte, fReceiveState); switch(fReceiveState) { case kIASServerReceiveStart: if (ackedFrame) { // Must be an ack from my previous response (or some other bogus data) // Keep looking } else { fOpCode = ctrlByte & kIASFrameOpCodeMask; if (lastFrame) { if (fOpCode == kIASOpGetValueByClass) { iasEntry = ParseRequest(iasReturnCode); } else { iasEntry = nil; iasReturnCode = kIASRetUnsupported; } } else { fReceiveState = kIASServerReceiveWaitFinal; } } break; case kIASServerReceiveWaitFinal: // I didn't accept the request, so all I want to do is get the // final frame of the request so I can reject it. XASSERT(!ackedFrame); if (lastFrame) { // I don't really care if they sent an ack w/final, so ignore it ackedFrame = false; // Return no such class for too large get value by class requests // Return unsupported for all other requests iasEntry = nil; iasReturnCode = fOpCode == kIASOpGetValueByClass ? kIASRetNoSuchClass : kIASRetUnsupported; } break; default: break; } // Either respond to the current request or continue accepting more of the request if (lastFrame && !ackedFrame) { // Reply to the request SendResponse(iasReturnCode, iasEntry); // Reset the receive state fOpCode = kIASOpUnassigned; fReceiveState = kIASServerReceiveStart; } else if (fReceiveState == kIASServerReceiveWaitFinal) { // Ack the frame I don't want/care about fGetPutBuffer->Seek(0, kPosBeg); fGetPutBuffer->Put(fOpCode | kIASFrameAckBit); PutStart(); } else { // Post another get GetStart(); } } // TIASServer::ParseInput //-------------------------------------------------------------------------------- // ParseRequest //-------------------------------------------------------------------------------- TIASAttribute* TIASServer::ParseRequest(UByte& iasReturnCode) { TIASClass* classItem; TIASAttribute* attrItem; //UChar theString[kIASMaxClassOrAttrStrLen+1]; UChar classString[kIASMaxClassOrAttrStrLen+1]; UChar attrString[kIASMaxClassOrAttrStrLen+1]; // Prepare return for ill-formed class string or class not found iasReturnCode = kIASRetNoSuchClass; // Get the class string if (!GotAValidString((UChar*)classString)) { XTRACE(kParseRequestEvent, 4, 0); return nil; } // Look up the class classItem = fNameService->FindClass((const UChar*)classString); if (classItem == nil) { XTRACE(kParseRequestEvent, 3, 0); return nil; } // Prepare return for ill-formed attribute string or attribute not found iasReturnCode = kIASRetNoSuchAttribute; // Get the attribute string if (!GotAValidString((UChar*)attrString)) { XTRACE(kParseRequestEvent, 2, 0); return nil; } // Look up the attribute attrItem = classItem->FindAttribute((const UChar*)attrString); if (attrItem == nil) { //DebugPrintf("ias server, class '%s' attr '%s' not found", // classString, attrString); XTRACE(kParseRequestEvent, 1, 0); return nil; } // Found the attribute iasReturnCode = kIASRetOkay; XTRACE(kParseRequestEvent, 0, 0); return attrItem; } // TIASServer::ParseRequest //-------------------------------------------------------------------------------- // GotAValidString //-------------------------------------------------------------------------------- Boolean TIASServer::GotAValidString(UChar* theString) { // In this case, a valid string is one that is less than kIASMaxClassOrAttrStrLen // and is contained entirely within the current receive buffer. UByte nameLength; // Get the class string length nameLength = fGetPutBuffer->Get(); if (nameLength > kIASMaxClassOrAttrStrLen) return false; // Get the class string if (fGetPutBuffer->Getn(theString, nameLength) != nameLength) return false; // Null terminate the string theString[nameLength] = 0; return true; } // TIASServer::GotAValidString //-------------------------------------------------------------------------------- // SendResponse //-------------------------------------------------------------------------------- void TIASServer::SendResponse(UByte iasReturnCode, TIASAttribute* attrEntry) { XTRACE(kSendResponseEvent, iasReturnCode, 0); // Do the header common to all types of responses fGetPutBuffer->Seek(0, kPosBeg); fGetPutBuffer->Put(fOpCode | kIASFrameLstBit); fGetPutBuffer->Put(iasReturnCode); // Have the attribute object and its elements add their data to the buffer if (iasReturnCode == kIASRetOkay) { XASSERT(attrEntry != nil); attrEntry->AddInfoToBuffer(fGetPutBuffer); } PutStart(); } // TIASServer::SendResponse //-------------------------------------------------------------------------------- // ListenStart //-------------------------------------------------------------------------------- void TIASServer::ListenStart() { XTRACE(kListenRequestEvent, (int)this >> 16, (short)this); TIrConnLstnRequest* listenRequest = (TIrConnLstnRequest*)fRequestReply; listenRequest->fEvent = kIrListenRequestEvent; listenRequest->fResult = noErr; listenRequest->fDevAddr = 0; listenRequest->fLSAPId = 0; listenRequest->fMyQOS = fIrDA->GetMyQOS(); listenRequest->fPeerQOS = fIrDA->GetPeerQOS(); listenRequest->fData = nil; fLSAPConn->EnqueueEvent(listenRequest); fReceiveState = kIASServerReceiveStart; } // TIASServer::ListenStart //-------------------------------------------------------------------------------- // GetStart //-------------------------------------------------------------------------------- void TIASServer::GetStart() { XTRACE(kGetDataRequestEvent, (int)this >> 16, (short)this); TIrGetRequest* getRequest = (TIrGetRequest*)fRequestReply; getRequest->fEvent = kIrGetDataRequestEvent; getRequest->fData = fGetPutBuffer; getRequest->fOffset = 0; getRequest->fLength = fGetPutBuffer->GetSize(); fLSAPConn->EnqueueEvent(getRequest); } // TIASServer::GetStart //-------------------------------------------------------------------------------- // PutStart //-------------------------------------------------------------------------------- void TIASServer::PutStart() { XTRACE(kPutDataRequestEvent, (int)this >> 16, (short)this); TIrPutRequest* putRequest = (TIrPutRequest*)fRequestReply; putRequest->fEvent = kIrPutDataRequestEvent; putRequest->fData = fGetPutBuffer; putRequest->fOffset = 0; putRequest->fLength = fGetPutBuffer->Position(); fLSAPConn->EnqueueEvent(putRequest); } // TIASServer::PutStart