/* 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; }
/* 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; }
/* 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; }
/* 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; }