/* 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; }
/* 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; }
/* construct an offers list from the offers contained in a frame. The frame's * Read Next pointer must be positioned at the first character of the offer. * rgerhards, 2008-03-25 */ relpRetVal relpOffersConstructFromFrame(relpOffers_t **ppOffers, relpFrame_t *pFrame) { relpOffers_t *pOffers = NULL; relpOffer_t *pOffer; relpRetVal localRet; unsigned char c; size_t iName; size_t iVal; unsigned char szFeatNam[RELP_MAX_OFFER_FEATURENAME+1]; unsigned char szFeatVal[RELP_MAX_OFFER_FEATUREVALUE+1]; ENTER_RELPFUNC; assert(ppOffers != NULL); RELPOBJ_assert(pFrame, Frame); CHKRet(relpOffersConstruct(&pOffers, pFrame->pEngine)); /* now process the command data */ localRet = relpFrameGetNextC(pFrame, &c); while(localRet == RELP_RET_OK) { /* command name */ iName = 0; while(iName < RELP_MAX_OFFER_FEATURENAME && c != '=' && localRet == RELP_RET_OK) { szFeatNam[iName++] = c; localRet = relpFrameGetNextC(pFrame, &c); } szFeatNam[iName] = '\0'; /* space is reserved for this! */ CHKRet(relpOfferAdd(&pOffer, szFeatNam, pOffers)); /* and now process the values (if any) */ while(localRet == RELP_RET_OK && c != '\n') { localRet = relpFrameGetNextC(pFrame, &c); /* eat the "=" or "," */ iVal = 0; while( iVal < RELP_MAX_OFFER_FEATUREVALUE && localRet == RELP_RET_OK && c != ',' && c != '\n' ) { szFeatVal[iVal++] = c; localRet = relpFrameGetNextC(pFrame, &c); } if(iVal > 0) { /* only set feature if one is actually given */ szFeatVal[iVal] = '\0'; /* space is reserved for this */ CHKRet(relpOfferValueAdd(szFeatVal, 0, pOffer)); } } if(localRet == RELP_RET_OK && c == '\n') localRet = relpFrameGetNextC(pFrame, &c); /* eat '\n' */ } if(localRet != RELP_RET_END_OF_DATA) ABORT_FINALIZE(localRet); *ppOffers = pOffers; finalize_it: if(iRet != RELP_RET_OK) { if(pOffers != NULL) relpOffersDestruct(&pOffers); } LEAVE_RELPFUNC; }