/* callback when the "open" command has been processed * Most importantly, this function needs to check if we are * compatible with the server-provided offers and terminate if * not. If we are, we must set our own parameters to match the * server-provided ones. Please note that the offer processing here * is the last and final. So the ultimate decision is made and if we * are unhappy with something that we can not ignore, we must * terminate with an error status. * In such cases, we flag the session as broken, as theoretically * it is possible to fix it by restarting the server with a set * of different parameters. The question remains, though, if that's * the smartest route to take... * Note that offer-processing is very similiar to offer-processing * at the server end (copen.c). We may be able to combine some of * it in the future (or may not, depending on the subtly different * needs both parts have. For now I leave this to a ... TODO ;)). * rgerhards, 2008-03-25 */ static relpRetVal relpSessCBrspOpen(relpSess_t *pThis, relpFrame_t *pFrame) { relpEngine_t *pEngine; relpOffers_t *pOffers = NULL; relpOffer_t *pOffer; relpOfferValue_t *pOfferVal; ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); RELPOBJ_assert(pFrame, Frame); pEngine = pThis->pEngine; /* first get the offers list from the server response */ CHKRet(relpOffersConstructFromFrame(&pOffers, pFrame)); /* we loop through the offers and set session parameters. If we find * something truely unacceptable, we break the session. */ for(pOffer = pOffers->pRoot ; pOffer != NULL ; pOffer = pOffer->pNext) { pEngine->dbgprint("processing server offer '%s'\n", pOffer->szName); if(!strcmp((char*)pOffer->szName, "relp_version")) { if(pOffer->pValueRoot == NULL) ABORT_FINALIZE(RELP_RET_INVALID_OFFER); if(pOffer->pValueRoot->intVal == -1) ABORT_FINALIZE(RELP_RET_INVALID_OFFER); if(pOffer->pValueRoot->intVal > pEngine->protocolVersion) ABORT_FINALIZE(RELP_RET_INCOMPAT_OFFERS); /* Once we support multiple versions, we may need to check what we * are compatible with. For now, we accept anything, because there is * nothing else yet ;) */ relpSessSetProtocolVersion(pThis, pOffer->pValueRoot->intVal); } else if(!strcmp((char*)pOffer->szName, "commands")) { for(pOfferVal = pOffer->pValueRoot ; pOfferVal != NULL ; pOfferVal = pOfferVal->pNext) { /* we do not care about return code in this case */ relpSessSetEnableCmd(pThis, pOfferVal->szVal, eRelpCmdState_Enabled); pEngine->dbgprint("enabled command '%s'\n", pOfferVal->szVal); } } else if(!strcmp((char*)pOffer->szName, "relp_software")) { /* we know this parameter, but we do not do anything * with it -- this may change if we need to emulate * something based on known bad relp software behaviour. */ } else { /* if we do not know an offer name, we ignore it - in this * case, we may simply not support it (but the client does and * must now live without it...) */ pEngine->dbgprint("ignoring unknown server offer '%s'\n", pOffer->szName); } } relpSessSetSessState(pThis, eRelpSessState_INIT_RSP_RCVD); finalize_it: if(pOffers != NULL) relpOffersDestruct(&pOffers); LEAVE_RELPFUNC; }
/* add an entry to our unacked frame list. The sendbuf object is handed over and must * no longer be accessed by the caller. * NOTE: we do not need mutex locks. This changes when we have a background transfer * thread (which we currently do not have). * rgerhards, 2008-03-20 */ relpRetVal relpSessAddUnacked(relpSess_t *pThis, relpSendbuf_t *pSendbuf) { relpSessUnacked_t *pUnackedLstEntry; ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); RELPOBJ_assert(pSendbuf, Sendbuf); if((pUnackedLstEntry = calloc(1, sizeof(relpSessUnacked_t))) == NULL) ABORT_FINALIZE(RELP_RET_OUT_OF_MEMORY); pUnackedLstEntry->pSendbuf = pSendbuf; DLL_Add(pUnackedLstEntry, pThis->pUnackedLstRoot, pThis->pUnackedLstLast); ++pThis->lenUnackedLst; if(pThis->lenUnackedLst >= pThis->sizeWindow) { /* in theory, we would need to check if the session is initialized, as * we would mess up session state in that case. However, as the init * process is just one frame, we can never run into the situation that * the window is exhausted during init, so we do not check it. */ relpSessSetSessState(pThis, eRelpSessState_WINDOW_FULL); if(pThis->lenUnackedLst >= pThis->sizeWindow) pThis->pEngine->dbgprint("Warning: exceeding window size, max %d, curr %d\n", pThis->lenUnackedLst, pThis->sizeWindow); } pThis->pEngine->dbgprint("ADD sess %p unacked %d, sessState %d\n", pThis, pThis->lenUnackedLst, pThis->sessState); finalize_it: LEAVE_RELPFUNC; }
/* This functions sends a complete sendbuf (a blocking call). It * is intended for use by clients. Do NOT use it on servers as * that will block other activity. bAddToUnacked specifies if the * sendbuf should be linked to the unacked list (if 1). If it is 0 * this shall NOT happen. Mode 0 is used for session reestablishment, * when the unacked list needs to be retransmitted. * rgerhards, 2008-03-19 */ relpRetVal relpSendbufSendAll(relpSendbuf_t *pThis, relpSess_t *pSess, int bAddToUnacked) { ssize_t lenToWrite; ssize_t lenWritten; ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sendbuf); RELPOBJ_assert(pSess, Sess); lenToWrite = pThis->lenData - pThis->bufPtr; while(lenToWrite != 0) { lenWritten = lenToWrite; //pSess->pEngine->dbgprint("sendbuf len %d, still to write %d\n", (int) pThis->lenData, (int) lenToWrite); CHKRet(relpTcpSend(pSess->pTcp, pThis->pData + (9 - pThis->lenTxnr) + pThis->bufPtr, &lenWritten)); if(lenWritten == -1) { ABORT_FINALIZE(RELP_RET_IO_ERR); } else if(lenWritten == lenToWrite) { lenToWrite = 0; } else { pThis->bufPtr += lenWritten; lenToWrite = pThis->lenData - pThis->bufPtr; } } /* ok, we now have sent the full buf. So we now need to add it to the unacked list, so that * we know what to do when the "rsp" packet comes in. -- rgerhards, 2008-03-20 */ if(bAddToUnacked) { if((iRet = relpSessAddUnacked(pSess, pThis)) != RELP_RET_OK) { relpSendbufDestruct(&pThis); FINALIZE; } pSess->pEngine->dbgprint("sendbuf added to unacked list\n"); #if 0 { relpSessUnacked_t *pUnackedEtry; pUnackedEtry = pThis->pUnackedLstRoot; if(pUnackedEtry != NULL) { pThis->pEngine->dbgprint("resending frame '%s'\n", pUnackedEtry->pSendbuf->pData + 9 - pUnackedEtry->pSendbuf->lenTxnr); CHKRet(relpFrameRewriteTxnr(pUnackedEtry->pSendbuf, pThis->txnr)); pThis->txnr = relpEngineNextTXNR(pThis->txnr); CHKRet(relpSendbufSendAll(pUnackedEtry->pSendbuf, pThis, 0)); pUnackedEtry = pUnackedEtry->pNext; } } #endif } else pSess->pEngine->dbgprint("sendbuf NOT added to unacked list\n"); finalize_it: LEAVE_RELPFUNC; }
/** Try to reconnect a broken session to the remote * server. The main difference to relpCltConnect() is that the * session object is already existing and session parameters (like * remote host) can not be changed. * rgerhards, 2008-03-23 */ relpRetVal relpCltReconnect(relpClt_t *pThis) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Clt); RELPOBJ_assert(pThis->pSess, Sess); CHKRet(relpSessTryReestablish(pThis->pSess)); finalize_it: LEAVE_RELPFUNC; }
/* This accepts a new session, and, if all goes well, constructs a new * session object and adds it to the engine's list of sessions. * rgerhards, 2008-03-17 */ relpRetVal relpSessAcceptAndConstruct(relpSess_t **ppThis, relpSrv_t *pSrv, int sock) { relpSess_t *pThis; ENTER_RELPFUNC; assert(ppThis != NULL); RELPOBJ_assert(pSrv, Srv); assert(sock >= 0); CHKRet(relpSessConstruct(&pThis, pSrv->pEngine, pSrv)); CHKRet(relpTcpAcceptConnReq(&pThis->pTcp, sock, pThis->pEngine)); /* TODO: check hostname against ACL (callback?) */ /* TODO: check against max# sessions */ *ppThis = pThis; finalize_it: pSrv->pEngine->dbgprint("relp session accepted with state %d\n", iRet); if(iRet != RELP_RET_OK) { if(pThis != NULL) relpSessDestruct(&pThis); } LEAVE_RELPFUNC; }
/* Send a command to the server. * The is the "regular" function which ensures that server messages are received * and messages are only sent when we have space left in our window. This function * must only be called after the session is fully initialized. Calling it before * initialization is finished will probably hang the client. * rgerhards, 2008-03-19 */ relpRetVal relpSessSendCommand(relpSess_t *pThis, unsigned char *pCmd, size_t lenCmd, unsigned char *pData, size_t lenData, relpRetVal (*rspHdlr)(relpSess_t*,relpFrame_t*)) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); /* this both reads server responses as well as makes sure we have space left * in our window. We provide a nearly eternal timeout (3 minutes). If we are not * ready to send in that period, something is awfully wrong. TODO: we may want * to make this timeout configurable, but I don't think it is a priority. */ //CHKRet(relpSessWaitState(pThis, eRelpSessState_READY_TO_SEND, 2)); CHKRet(relpSessWaitState(pThis, eRelpSessState_READY_TO_SEND, 180)); /* re-try once if automatic retry mode is set */ pThis->pEngine->dbgprint("send command relp sess state %d\n", pThis->sessState); if(pThis->bAutoRetry && pThis->sessState == eRelpSessState_BROKEN) { pThis->pEngine->dbgprint("SendCommand does auto-retry\n"); CHKRet(relpSessTryReestablish(pThis)); } pThis->pEngine->dbgprint("sendcommand ready to send, relp sess state %d\n", pThis->sessState); /* then send our data */ if(pThis->sessState == eRelpSessState_BROKEN) ABORT_FINALIZE(RELP_RET_SESSION_BROKEN); CHKRet(relpSessRawSendCommand(pThis, pCmd, lenCmd, pData, lenData, rspHdlr)); finalize_it: LEAVE_RELPFUNC; }
/* Try to restablish a broken session. A single try is made and the result * reported back. RELP_RET_OK if we could get a new session, a RELP error state * otherwise (e.g. RELP_RET_SESSION_BROKEN, but could also be a more precise * error code). If the session can be re-established, any unsent frames are * resent. The function returns only after that happened. * rgerhards, 2008-03-22 */ relpRetVal relpSessTryReestablish(relpSess_t *pThis) { relpSessUnacked_t *pUnackedEtry; ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); assert(pThis->sessState = eRelpSessState_BROKEN); CHKRet(relpTcpAbortDestruct(&pThis->pTcp)); CHKRet(relpSessConnect(pThis, pThis->protFamily, pThis->srvPort, pThis->srvAddr)); /* if we reach this point, we could re-establish the session. We now * need to resend any unacked data. Note that we need to patch in new txnr's * into the existing frames. We need to do a special send command, as the usual * one would maintain the unacked list, what we can not do right now (because * it is not to be modified. */ pUnackedEtry = pThis->pUnackedLstRoot; if(pUnackedEtry != NULL) pThis->pEngine->dbgprint("relp session %p reestablished, now resending %d unacked frames\n", pThis, pThis->lenUnackedLst); while(pUnackedEtry != NULL) { pThis->pEngine->dbgprint("resending frame '%s'\n", pUnackedEtry->pSendbuf->pData + 9 - pUnackedEtry->pSendbuf->lenTxnr); CHKRet(relpFrameRewriteTxnr(pUnackedEtry->pSendbuf, pThis->txnr)); pThis->txnr = relpEngineNextTXNR(pThis->txnr); CHKRet(relpSendbufSendAll(pUnackedEtry->pSendbuf, pThis, 0)); pUnackedEtry = pUnackedEtry->pNext; } finalize_it: pThis->pEngine->dbgprint("after TryReestablish, sess state %d\n", pThis->sessState); LEAVE_RELPFUNC; }
/* Send a command to the server. * The is the "regular" function which ensures that server messages are received * and messages are only sent when we have space left in our window. This function * must only be called after the session is fully initialized. Calling it before * initialization is finished will probably hang the client. * rgerhards, 2008-03-19 */ relpRetVal relpSessSendCommand(relpSess_t *pThis, unsigned char *pCmd, size_t lenCmd, unsigned char *pData, size_t lenData, relpRetVal (*rspHdlr)(relpSess_t*,relpFrame_t*)) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); /* this both reads server responses as well as makes sure we have space * left in our window. */ CHKRet(relpSessWaitState(pThis, eRelpSessState_READY_TO_SEND, pThis->timeout)); /* re-try once if automatic retry mode is set */ if(pThis->bAutoRetry && pThis->sessState == eRelpSessState_BROKEN) { CHKRet(relpSessTryReestablish(pThis)); } /* then send our data */ if(pThis->sessState == eRelpSessState_BROKEN) ABORT_FINALIZE(RELP_RET_SESSION_BROKEN); CHKRet(relpSessRawSendCommand(pThis, pCmd, lenCmd, pData, lenData, rspHdlr)); finalize_it: LEAVE_RELPFUNC; }
/* Send a response to the client. * rgerhards, 2008-03-19 */ relpRetVal relpSessSendResponse(relpSess_t *pThis, relpTxnr_t txnr, unsigned char *pData, size_t lenData) { relpSendbuf_t *pSendbuf; ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); CHKRet(relpFrameBuildSendbuf(&pSendbuf, txnr, (unsigned char*)"rsp", 3, pData, lenData, pThis, NULL)); /* now enqueue it to the sendq (which means "send it" ;)) */ CHKRet(relpSendqAddBuf(pThis->pSendq, pSendbuf, pThis->pTcp)); finalize_it: if(iRet != RELP_RET_OK) { if(iRet == RELP_RET_IO_ERR) { pThis->pEngine->dbgprint("relp session %p is broken, io error\n", pThis); pThis->sessState = eRelpSessState_BROKEN; } if(pSendbuf != NULL) relpSendbufDestruct(&pSendbuf); } LEAVE_RELPFUNC; }
/* Construct an offer with name pszName and add it to the offers list. * The offer object is returned to the caller. It is read-only and MUST NOT * be destructed. * rgerhards, 2008-03-24 */ relpRetVal relpOfferAdd(relpOffer_t **ppThis, unsigned char *pszName, relpOffers_t *pOffers) { relpOffer_t *pThis = NULL; ENTER_RELPFUNC; assert(ppThis != NULL); assert(pszName != NULL); RELPOBJ_assert(pOffers, Offers); CHKRet(relpOfferConstruct(&pThis, pOffers->pEngine)); strncpy((char*)pThis->szName, (char*)pszName, sizeof(pThis->szName)); pThis->pNext = pOffers->pRoot; pOffers->pRoot = pThis; *ppThis = pThis; finalize_it: if(iRet != RELP_RET_OK) { if(pThis != NULL) relpOfferDestruct(&pThis); } LEAVE_RELPFUNC; }
/* Delete an entry from our unacked list. The list entry is destructed, but * the sendbuf not. * rgerhards, 2008-03-20 */ static relpRetVal relpSessDelUnacked(relpSess_t *pThis, relpSessUnacked_t *pUnackedLstEntry) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); assert(pUnackedLstEntry != NULL); DLL_Del(pUnackedLstEntry, pThis->pUnackedLstRoot, pThis->pUnackedLstLast); --pThis->lenUnackedLst; if( pThis->lenUnackedLst < pThis->sizeWindow && relpSessGetSessState(pThis) == eRelpSessState_WINDOW_FULL) { /* here, we need to check if we had WINDOW_FULL condition. The reason is that * we otherwise mess up the session init handling - contrary to ...AddUnacked(), * we run into the situation of a session state change on init. So we * need to make sure it works. */ relpSessSetSessState(pThis, eRelpSessState_READY_TO_SEND); } free(pUnackedLstEntry); pThis->pEngine->dbgprint("DEL sess %p unacked %d, sessState %d\n", pThis, pThis->lenUnackedLst, pThis->sessState); LEAVE_RELPFUNC; }
/** Destruct a RELP offers list * rgerhards, 2008-03-24 */ relpRetVal relpOffersDestruct(relpOffers_t **ppThis) { relpOffers_t *pThis; relpOffer_t *pOffer; relpOffer_t *pOfferToDel; ENTER_RELPFUNC; assert(ppThis != NULL); pThis = *ppThis; RELPOBJ_assert(pThis, Offers); pOffer = pThis->pRoot; while(pOffer != NULL) { pOfferToDel = pOffer; pOffer = pOffer->pNext; relpOfferDestruct(&pOfferToDel); } /* done with de-init work, now free offers object itself */ free(pThis); *ppThis = NULL; LEAVE_RELPFUNC; }
/** Destruct a RELP sess instance */ relpRetVal relpSessDestruct(relpSess_t **ppThis) { relpSess_t *pThis; relpSessUnacked_t *pUnacked; relpSessUnacked_t *pUnackedToDel; ENTER_RELPFUNC; assert(ppThis != NULL); pThis = *ppThis; RELPOBJ_assert(pThis, Sess); /* pTcp may be NULL if we run into the destructor due to an error that occured * during construction. */ if(pThis->pTcp != NULL) { if(pThis->pSrv != NULL) { relpSessSrvDoDisconnect(pThis); } else { /* we are at the client side of the connection */ if( pThis->sessState != eRelpSessState_DISCONNECTED && pThis->sessState != eRelpSessState_BROKEN) { relpSessCltDoDisconnect(pThis); } } } if(pThis->pSendq != NULL) relpSendqDestruct(&pThis->pSendq); if(pThis->pTcp != NULL) relpTcpDestruct(&pThis->pTcp); /* unacked list */ for(pUnacked = pThis->pUnackedLstRoot ; pUnacked != NULL ; ) { pUnackedToDel = pUnacked; pUnacked = pUnacked->pNext; relpSendbufDestruct(&pUnackedToDel->pSendbuf); free(pUnackedToDel); } free(pThis->srvPort); free(pThis->srvAddr); free(pThis->clientIP); free(pThis->pristring); free(pThis->ownCertFile); free(pThis->privKeyFile); relpSessFreePermittedPeers(pThis); pthread_mutex_destroy(&pThis->mutSend); /* done with de-init work, now free object itself */ free(pThis); *ppThis = NULL; LEAVE_RELPFUNC; }
/* actually send to the remote peer * This function takes data from the sendq and sends as much as * possible to the remote peer. * rgerhards, 2008-03-19 */ relpRetVal relpSessSndData(relpSess_t *pThis) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); CHKRet(relpSendqSend(pThis->pSendq, pThis->pTcp)); finalize_it: LEAVE_RELPFUNC; }
/* Connect to the server. All session parameters (like remote address) must * already have been set. * rgerhards, 2008-03-19 */ relpRetVal relpSessConnect(relpSess_t *pThis, int protFamily, unsigned char *port, unsigned char *host) { relpOffers_t *pOffers; unsigned char *pszOffers = NULL; size_t lenOffers; ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); CHKRet(relpSessFixCmdStates(pThis)); if(pThis->srvAddr == NULL) { /* initial connect, need to save params */ pThis->protFamily = protFamily; if((pThis->srvPort = (unsigned char*) strdup((char*)port)) == NULL) ABORT_FINALIZE(RELP_RET_OUT_OF_MEMORY); if((pThis->srvAddr = (unsigned char*) strdup((char*)host)) == NULL) ABORT_FINALIZE(RELP_RET_OUT_OF_MEMORY); } /* (re-)init some counters */ pThis->txnr = 1; pThis->sessType = eRelpSess_Client; /* indicate we have a client session */ CHKRet(relpTcpConstruct(&pThis->pTcp, pThis->pEngine)); CHKRet(relpTcpConnect(pThis->pTcp, protFamily, port, host)); relpSessSetSessState(pThis, eRelpSessState_PRE_INIT); /* create offers */ CHKRet(relpSessConstructOffers(pThis, &pOffers)); CHKRet(relpOffersToString(pOffers, NULL, 0, &pszOffers, &lenOffers)); CHKRet(relpOffersDestruct(&pOffers)); CHKRet(relpSessRawSendCommand(pThis, (unsigned char*)"open", 4, pszOffers, lenOffers, relpSessCBrspOpen)); relpSessSetSessState(pThis, eRelpSessState_INIT_CMD_SENT); CHKRet(relpSessWaitState(pThis, eRelpSessState_INIT_RSP_RCVD, pThis->timeout)); /* we now have received the server's response. Now is a good time to check if the offers * received back are compatible with what we need - and, if not, terminate the session... */ pThis->pEngine->dbgprint("pre CltConnChkOffers %d\n", iRet); CHKRet(relpSessCltConnChkOffers(pThis)); /* TODO: flag sesssion as broken if we did not succeed? */ /* if we reach this point, we have a valid relp session */ relpSessSetSessState(pThis, eRelpSessState_READY_TO_SEND); /* indicate session startup */ finalize_it: pThis->pEngine->dbgprint("end relpSessConnect, iRet %d\n", iRet); if(pszOffers != NULL) free(pszOffers); LEAVE_RELPFUNC; }
/* actually send to the remote peer * This function takes data from the sendq and sends as much as * possible to the remote peer. * rgerhards, 2008-03-19 */ relpRetVal relpSessSndData(relpSess_t *pThis) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); if (pThis->sessState != eRelpSessState_BROKEN) { CHKRet(relpSendqSend(pThis->pSendq, pThis->pTcp)); } finalize_it: LEAVE_RELPFUNC; }
/* set send buffer contents. The provided data pointer is handed *over* to * the sendbuf, so the caller can no longer access it. Most importantly, the * caller MUST NOT free the buffer! * rgerhards, 2008-03-17 */ relpRetVal relpSendbufSetData(relpSendbuf_t *pThis, relpOctet_t *pData, size_t lenData) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Engine); assert(pData != NULL); assert(lenData > 0); pThis->pData = pData; pThis->lenData = lenData; pThis->bufPtr = 0; LEAVE_RELPFUNC; }
/* Sends as much data from the send buffer as possible. * This function tries to send as much data from the send buffer * as possible. For partial writes, the sendbuffer is updated to * contain the correct "already written" count. * rgerhards, 2008-03-19 */ relpRetVal relpSendbufSend(relpSendbuf_t *pThis, relpTcp_t *pTcp) { ssize_t lenToWrite; ssize_t lenWritten; ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sendbuf); RELPOBJ_assert(pTcp, Tcp); lenToWrite = pThis->lenData - pThis->bufPtr; lenWritten = lenToWrite; //pTcp->pEngine->dbgprint("sendbuf len %d, still to write %d\n", (int) pThis->lenData, (int) lenToWrite); CHKRet(relpTcpSend(pTcp, pThis->pData + (9 - pThis->lenTxnr) + pThis->bufPtr, &lenWritten)); if(lenWritten != lenToWrite) { pThis->bufPtr += lenWritten; iRet = RELP_RET_PARTIAL_WRITE; } finalize_it: LEAVE_RELPFUNC; }
/* Check if the set of offers inclulded in our session is compatible with * what we need. By now, all session parameters should be set to enabled. If * there are some left as "Required", they are not supported by the server, * in which case we can not open the session. -- rgerhards, 2008-03-27 */ relpRetVal relpSessCltConnChkOffers(relpSess_t *pThis) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); if(pThis->stateCmdSyslog == eRelpCmdState_Required) ABORT_FINALIZE(RELP_RET_RQD_FEAT_MISSING); finalize_it: if(iRet != RELP_RET_OK) pThis->sessState = eRelpSessState_BROKEN; LEAVE_RELPFUNC; }
/** Construct a RELP sess instance * the pSrv parameter may be set to NULL if the session object is for a client. */ relpRetVal relpSessConstruct(relpSess_t **ppThis, relpEngine_t *pEngine, int connType, void *pParent) { relpSess_t *pThis; ENTER_RELPFUNC; assert(ppThis != NULL); RELPOBJ_assert(pEngine, Engine); if((pThis = calloc(1, sizeof(relpSess_t))) == NULL) { ABORT_FINALIZE(RELP_RET_OUT_OF_MEMORY); } RELP_CORE_CONSTRUCTOR(pThis, Sess); pThis->pEngine = pEngine; /* use Engine's command enablement states as default */ pThis->stateCmdSyslog = pEngine->stateCmdSyslog; if(connType == RELP_SRV_CONN) { pThis->pSrv = (relpSrv_t*) pParent; } else { pThis->pClt = (relpClt_t*) pParent; } pThis->txnr = 1; /* txnr start at 1 according to spec */ pThis->timeout = 90; pThis->pUsr = NULL; pThis->sizeWindow = RELP_DFLT_WINDOW_SIZE; pThis->maxDataSize = RELP_DFLT_MAX_DATA_SIZE; pThis->authmode = eRelpAuthMode_None; pThis->pristring = NULL; pThis->caCertFile = NULL; pThis->ownCertFile = NULL; pThis->privKeyFile = NULL; pThis->permittedPeers.nmemb = 0; CHKRet(relpSendqConstruct(&pThis->pSendq, pThis->pEngine)); pthread_mutex_init(&pThis->mutSend, NULL); *ppThis = pThis; finalize_it: if(iRet != RELP_RET_OK) { if(pThis != NULL) { relpSessDestruct(&pThis); } } LEAVE_RELPFUNC; }
/** Destruct a RELP offer value instance * rgerhards, 2008-03-24 */ static relpRetVal relpOfferValueDestruct(relpOfferValue_t **ppThis) { relpOfferValue_t *pThis; ENTER_RELPFUNC; assert(ppThis != NULL); pThis = *ppThis; RELPOBJ_assert(pThis, OfferValue); /* done with de-init work, now free offers object itself */ free(pThis); *ppThis = NULL; LEAVE_RELPFUNC; }
/* Disconnect from the client. After disconnect, the session object * is destructed. This is the server-side disconnect and must be called * for server sessions only. * rgerhards, 2008-03-31 */ static relpRetVal relpSessSrvDoDisconnect(relpSess_t *pThis) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); /* Try to hint the client that we are closing the session. If the hint does * not make it through to the client, that's ok, too. Then, it'll notice when it * tries to use the connection. In any case, it can handle that (but obviously its * better if the client is able to receive the hint, as this cleans up things a bit * faster). */ CHKRet(relpSessSrvSendHint(pThis, (unsigned char*)"serverclose", 11, (unsigned char*)"", 0)); finalize_it: LEAVE_RELPFUNC; }
/** open a relp session to a remote peer * remote servers parameters must already have been set. * rgerhards, 2008-03-19 */ relpRetVal relpCltConnect(relpClt_t *pThis, int protFamily, unsigned char *port, unsigned char *host) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Clt); CHKRet(relpSessConstruct(&pThis->pSess, pThis->pEngine, NULL)); CHKRet(relpSessConnect(pThis->pSess, protFamily, port, host)); finalize_it: if(iRet != RELP_RET_OK) { if(pThis->pSess != NULL) { relpSessDestruct(&pThis->pSess); } } LEAVE_RELPFUNC; }
/* Construct a offer value with pszVal and add it to the offer value * list. This function also checks if the value is an integer and, if so, * sets the integer value. If no string pointer is provided, the integer * value is added. The integer is ignored if a string pointer is present. * rgerhards, 2008-03-24 */ relpRetVal relpOfferValueAdd(unsigned char *pszVal, int intVal, relpOffer_t *pOffer) { relpOfferValue_t *pThis = NULL; int i; int Val; ENTER_RELPFUNC; RELPOBJ_assert(pOffer, Offer); CHKRet(relpOfferValueConstruct(&pThis, pOffer->pEngine)); /* check which value we need to use */ if(pszVal == NULL) { snprintf((char*)pThis->szVal, sizeof(pThis->szVal), "%d", intVal); pThis->intVal = intVal; } else { strncpy((char*)pThis->szVal, (char*)pszVal, sizeof(pThis->szVal)); /* check if the string actually is an integer... */ Val = 0; i = 0; while(pszVal[i]) { if(isdigit(pszVal[i])) Val = Val * 10 + pszVal[i] - '0'; else break; ++i; } if(pszVal[i] != '\0') Val = -1; /* no (unsigned!) integer! */ pThis->intVal = Val; } pThis->pNext = pOffer->pValueRoot; pOffer->pValueRoot = pThis; finalize_it: if(iRet != RELP_RET_OK) { if(pThis != NULL) relpOfferValueDestruct(&pThis); } LEAVE_RELPFUNC; }
/* Send a command hint to the remote peer. This function works for the * server-side of the connection. A modified version must be used for the * client side (because we have different ways of sending the data). We do * not yet need the client side. If we do, way should think about a generic * approach, eg by using function pointers for the send function. * rgerhards, 2008-03-31 */ static relpRetVal relpSessSrvSendHint(relpSess_t *pThis, unsigned char *pHint, size_t lenHint, unsigned char *pData, size_t lenData) { relpSendbuf_t *pSendbuf; ENTER_RELPFUNC; assert(pHint != NULL); assert(lenHint != 0); RELPOBJ_assert(pThis, Sess); CHKRet(relpFrameBuildSendbuf(&pSendbuf, 0, pHint, lenHint, pData, lenData, pThis, NULL)); /* now send it */ pThis->pEngine->dbgprint("hint-frame to send: '%s'\n", pSendbuf->pData + (9 - pSendbuf->lenTxnr)); CHKRet(relpSendbufSend(pSendbuf, pThis->pTcp)); finalize_it: LEAVE_RELPFUNC; }
/** Destruct a RELP clt instance */ relpRetVal relpCltDestruct(relpClt_t **ppThis) { relpClt_t *pThis; ENTER_RELPFUNC; assert(ppThis != NULL); pThis = *ppThis; RELPOBJ_assert(pThis, Clt); if(pThis->pSess != NULL) relpSessDestruct(&pThis->pSess); /* done with de-init work, now free clt object itself */ free(pThis); *ppThis = NULL; LEAVE_RELPFUNC; }
/** Destruct a RELP sendbuf instance */ relpRetVal relpSendbufDestruct(relpSendbuf_t **ppThis) { relpSendbuf_t *pThis; ENTER_RELPFUNC; assert(ppThis != NULL); pThis = *ppThis; RELPOBJ_assert(pThis, Sendbuf); pThis->pSess->pEngine->dbgprint("in destructor: sendbuf %p\n", pThis); if(pThis->pData != NULL) free(pThis->pData); /* done with de-init work, now free sendbuf object itself */ free(pThis); *ppThis = NULL; LEAVE_RELPFUNC; }
/* receive data from a socket * The following function is called when the relp engine has detected * that data is available on the socket. This function reads the available * data and submits it for processing. * rgerhards, 2008-03-17 */ relpRetVal relpSessRcvData(relpSess_t *pThis) { relpOctet_t rcvBuf[RELP_RCV_BUF_SIZE+1]; ssize_t lenBuf; ssize_t i; ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); lenBuf = RELP_RCV_BUF_SIZE; CHKRet(relpTcpRcv(pThis->pTcp, rcvBuf, &lenBuf)); if(lenBuf == 0) { pThis->pEngine->dbgprint("server closed relp session %p, session broken\n", pThis); /* even though we had a "normal" close, it is unexpected at this * stage. Consequently, we consider the session to be broken, because * the recovery action is the same no matter how it is broken. */ pThis->sessState = eRelpSessState_BROKEN; ABORT_FINALIZE(RELP_RET_SESSION_BROKEN); } else if ((int) lenBuf == -1) { /* I don't know why we need to cast to int, but we must... */ if(errno != EAGAIN) { pThis->pEngine->dbgprint("errno %d during relp session %p, session broken\n", errno,pThis); pThis->sessState = eRelpSessState_BROKEN; ABORT_FINALIZE(RELP_RET_SESSION_BROKEN); } } else { /* Terminate buffer and output received data to debug*/ rcvBuf[lenBuf] = '\0'; pThis->pEngine->dbgprint("relp session read %d octets, buf '%s'\n", (int) lenBuf, rcvBuf); /* we have regular data, which we now can process */ for(i = 0 ; i < lenBuf ; ++i) { CHKRet(relpFrameProcessOctetRcvd(&pThis->pCurrRcvFrame, rcvBuf[i], pThis)); } } finalize_it: LEAVE_RELPFUNC; }
/* This accepts a new session, and, if all goes well, constructs a new * session object and adds it to the engine's list of sessions. * rgerhards, 2008-03-17 */ relpRetVal relpSessAcceptAndConstruct(relpSess_t **ppThis, relpSrv_t *pSrv, int sock) { relpSess_t *pThis; ENTER_RELPFUNC; assert(ppThis != NULL); RELPOBJ_assert(pSrv, Srv); assert(sock >= 0); CHKRet(relpSessConstruct(&pThis, pSrv->pEngine, RELP_SRV_CONN, pSrv)); CHKRet(relpTcpAcceptConnReq(&pThis->pTcp, sock, pSrv)); *ppThis = pThis; finalize_it: if(iRet != RELP_RET_OK) { if(pThis != NULL) relpSessDestruct(&pThis); } LEAVE_RELPFUNC; }
/* find an entry in the unacked list and provide it to the caller. The entry is handed * over to the caller and removed from the queue of unacked entries. It is the caller's * duty to destruct the sendbuf when it is done with it. * rgerhards, 20080-03-20 */ relpRetVal relpSessGetUnacked(relpSess_t *pThis, relpSendbuf_t **ppSendbuf, relpTxnr_t txnr) { relpSessUnacked_t *pUnackedEtry; ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); assert(ppSendbuf != NULL); for( pUnackedEtry = pThis->pUnackedLstRoot ; pUnackedEtry != NULL && pUnackedEtry->pSendbuf->txnr != txnr ; pUnackedEtry = pUnackedEtry->pNext) /*JUST SKIP*/; if(pUnackedEtry == NULL) ABORT_FINALIZE(RELP_RET_NOT_FOUND); *ppSendbuf = pUnackedEtry->pSendbuf; relpSessDelUnacked(pThis, pUnackedEtry); finalize_it: LEAVE_RELPFUNC; }