static void SetNextState(STUN_TRANSACTION_DATA* trans, STUN_STATE NextState) { STUN_CLIENT_DATA* client = trans->client; if (NextState >= NoOfStates) { StunPrint(client->logUserData, client->Log_cb, StunInfoCategory_Error, "<STUNCLIENT:%02d> SetNextState, Illegal State %d", trans->inst, NextState); return; } if (trans->state != NextState) { StunPrint(client->logUserData, client->Log_cb, StunInfoCategory_Trace, "<STUNCLIENT:%02d> State (%s -> %s)", trans->inst, StateTable[trans->state].StateStr, StateTable[NextState].StateStr); trans->state = NextState; } /* always free instance on return to idle */ if (NextState == STUN_STATE_Idle) { trans->inUse = false; } }
void StunClient_HandleIncResp(STUN_CLIENT_DATA* clientData, const StunMessage* msg, const struct sockaddr* srcAddr) { if (clientData == NULL) { return; } for (int i = 0; i < MAX_STUN_TRANSACTIONS; i++) { STUN_TRANSACTION_DATA* trans = &clientData->data[i]; if ( trans->inUse && TransIdIsEqual(&msg->msgHdr.id, &trans->stunBindReq.transactionId) ) { StunRespStruct m; gettimeofday(&trans->stop[trans->retransmits], NULL); memcpy( &m.stunRespMessage, msg, sizeof(m.stunRespMessage) ); sockaddr_copy( (struct sockaddr*)&m.srcAddr, srcAddr ); StunClientMain(clientData, i, StunMsgToInternalStunSig(msg), (void*)&m); return; } } StunPrint(clientData->logUserData, clientData->Log_cb, StunInfoCategory_Trace, "<STUNCLIENT> no instance with transId, discarding, msgType %d\n ", msg->msgHdr.msgType); }
static bool StoreBindResp(STUN_TRANSACTION_DATA* trans, StunMessage* resp) { STUN_CLIENT_DATA* client = trans->client; if (resp->hasXorMappedAddress) { if (resp->xorMappedAddress.familyType == STUN_ADDR_IPv4Family) { sockaddr_initFromIPv4Int( (struct sockaddr_in*)&trans->rflxAddr, htonl(resp->xorMappedAddress.addr.v4.addr), htons(resp->xorMappedAddress.addr.v4.port) ); } else if (resp->xorMappedAddress.familyType == STUN_ADDR_IPv6Family) { sockaddr_initFromIPv6Int( (struct sockaddr_in6*)&trans->rflxAddr, resp->xorMappedAddress.addr.v6.addr, htons(resp->xorMappedAddress.addr.v6.port) ); } return true; } else { StunPrint(client->logUserData, client->Log_cb, StunInfoCategory_Error, "<STUNCLIENT:%02d> Missing XorMappedAddress BindResp", trans->inst); return false; } }
static void ICMPRespCallback(STUN_TRANSACTION_DATA* trans, const struct sockaddr* srcAddr) { STUN_CLIENT_DATA* client = trans->client; char ip_str [SOCKADDR_MAX_STRLEN]; StunCallBackData_T res; memset( &res, 0, sizeof (StunCallBackData_T) ); memcpy( &res.msgId, &trans->stunBindReq.transactionId, sizeof(StunMsgId) ); res.stunResult = StunResult_ICMPResp; res.ICMPtype = trans->ICMPtype; res.ttl = trans->ttl; res.rtt = getRTTvalue(trans); res.retransmits = trans->retransmits; sockaddr_copy( (struct sockaddr*)&res.srcAddr, srcAddr ); StunPrint( client->logUserData, client->Log_cb, StunInfoCategory_Info, "<STUNCLIENT:%02d> ICMPResp from src: %s", trans->inst, sockaddr_toString( (struct sockaddr*) &res.srcAddr, ip_str, SOCKADDR_MAX_STRLEN, true ) ); if (trans->stunBindReq.stunCbFunc) { (trans->stunBindReq.stunCbFunc)(trans->stunBindReq.userCtx, &res); } }
static void CancelRetryTimeoutHandler(STUN_TRANSACTION_DATA* trans) { STUN_CLIENT_DATA* client = trans->client; uint32_t max; if (trans->stunBindReq.stuntrace) { max = STUNTRACE_MAX_RETRANSMITS; } else { max = STUNCLIENT_MAX_RETRANSMITS; } if ( (trans->retransmits < max) && (stunTimeoutList[trans->retransmits] != 0) ) /* can be 0 terminated if * using fewer * retransmits **/ { StartNextRetransmitTimer(trans); trans->retransmits++; } else { StunPrint(client->logUserData, client->Log_cb, StunInfoCategory_Trace, "<STUNCLIENT:%02d> Cancel complete", trans->inst); CallBack(trans, StunResult_CancelComplete); SetNextState(trans, STUN_STATE_Idle); } }
void StunClient_HandleICMP(STUN_CLIENT_DATA* clientData, const struct sockaddr* srcAddr, uint32_t ICMPtype) { if (clientData == NULL) { return; } /* Todo: Test if this is for me.. */ StunPrint(clientData->logUserData, clientData->Log_cb, StunInfoCategory_Trace, "<STUNTRACE> StunClient_HandleICMP: Got ICMP type: %i\n ", ICMPtype); if ( isTimeExceeded(ICMPtype, srcAddr->sa_family) || isDstUnreachable(ICMPtype,srcAddr->sa_family) ) { for (int i = 0; i < MAX_STUN_TRANSACTIONS; i++) { STUN_TRANSACTION_DATA* trans = &clientData->data[i]; if ( trans->inUse && TransIdIsEqual(&clientData->traceResult.currStunMsgId, &trans->stunBindReq.transactionId) ) { StunRespStruct m; gettimeofday(&trans->stop[trans->retransmits], NULL); /* memcpy(&m.stunRespMessage, msg, sizeof(m.stunRespMessage)); */ sockaddr_copy( (struct sockaddr*)&m.srcAddr, srcAddr ); m.ICMPtype = ICMPtype; m.ttl = clientData->traceResult.currentTTL; StunClientMain(clientData, i, STUN_SIGNAL_ICMPResp, (void*)&m); return; } } } else { StunPrint(clientData->logUserData, clientData->Log_cb, StunInfoCategory_Trace, "<STUNTRACE> StunClient_HandleICMP: Ignoring ICMP Type, nothing to do\n ", ICMPtype); } }
/* Common signal handling for all states */ static void StunAllState(STUN_TRANSACTION_DATA* trans, STUN_SIGNAL sig) { STUN_CLIENT_DATA* client = trans->client; StunPrint(client->logUserData, client->Log_cb, StunInfoCategory_Error, "<STUNCLIENT:%02d> undefined signal %s in state %d", trans->inst, StunsigToStr(sig), trans->state); }
static void StunClientMain(STUN_CLIENT_DATA* clientData, int ctx, STUN_SIGNAL sig, uint8_t* payload) { /* if context is already known, just call the fsm */ if (ctx != STUNCLIENT_CTX_UNKNOWN) { if (ctx < MAX_STUN_TRANSACTIONS) { StunClientFsm(&clientData->data[ctx], sig, payload); } else { StunPrint(clientData->logUserData, clientData->Log_cb, StunInfoCategory_Error, "<STUNCLIENT> sig: %s illegal context %d exceeds %d\n ", StunsigToStr(sig), ctx, MAX_STUN_TRANSACTIONS); } } else if (sig == STUN_SIGNAL_BindReq) { ctx = AllocFreeInst(clientData,&sig, payload); if (ctx >= 0) { StunClientFsm(&clientData->data[ctx], sig, payload); } else { StunPrint( clientData->logUserData, clientData->Log_cb, StunInfoCategory_Error, "<STUNCLIENT> No free instances, sig: %s", StunsigToStr(sig) ); } } }
/* check if timer has expired and return the timer signal */ static void StopTimer(STUN_TRANSACTION_DATA* trans, STUN_SIGNAL sig) { STUN_CLIENT_DATA* client = trans->client; StunPrint( client->logUserData, client->Log_cb, StunInfoCategory_Trace, "<STUNCLIENT:%02d> StopTimer(%s)", trans->inst, StunsigToStr(sig) ); switch (sig) { case STUN_SIGNAL_TimerRetransmit: trans->TimerRetransmit = 0; break; default: StunPrint(client->logUserData, client->Log_cb, StunInfoCategory_Error, "<STUNCLIENT:%02d> illegal StopTimer %d", trans->inst, sig); break; } }
static void StunClientFsm(STUN_TRANSACTION_DATA* trans, STUN_SIGNAL sig, uint8_t* payload) { STUN_CLIENT_DATA* client = trans->client; if (trans->state < STUN_STATE_End) { StunPrint(client->logUserData, client->Log_cb, StunInfoCategory_Trace, "<STUNCLIENT:%02d> IN <-- %s (state %s)", trans->inst, StunsigToStr(sig), StateTable[trans->state].StateStr); (StateTable[trans->state].Statefunc)(trans, sig, payload); } else { StunPrint( client->logUserData, client->Log_cb, StunInfoCategory_Error, "<STUNCLIENT:%02d> undefned state %d, sig %s", trans->inst, trans->state, StunsigToStr(sig) ); } }
static bool SendConnectivityBindResponse(STUN_CLIENT_DATA* clientData, int32_t globalSocketId, StunMessage* stunRespMsg, const char* password, const struct sockaddr* dstAddr, void* userData, STUN_SENDFUNC sendFunc, int proto, bool useRelay) { uint8_t stunBuff[STUN_MAX_PACKET_SIZE]; int stunLen; (void) userData; /* encode bind Response */ stunLen = stunlib_encodeMessage(stunRespMsg, (uint8_t*)stunBuff, STUN_MAX_PACKET_SIZE, (unsigned char*)password, /* md5key **/ password ? strlen(password) : 0, /* keyLen **/ NULL); if (!stunLen) { StunPrint(clientData->logUserData, clientData->Log_cb, StunInfoCategory_Error, "<STUNCLIENT> Failed to encode Binding request response\n"); return false; } /* send */ /* sendFunc(globalSocketId, stunBuff, stunLen, dstAddr, useRelay, 0); */ sendFunc(clientData->userCtx, globalSocketId, stunBuff, stunLen, dstAddr, proto, useRelay, 0); clientData->stats.BindRespSent++; return true; }
static void CommonRetryTimeoutHandler(STUN_TRANSACTION_DATA* trans, StunResult_T stunResult, const char* errStr, STUN_STATE FailedState) { STUN_CLIENT_DATA* client = trans->client; uint32_t max; if (trans->stunBindReq.stuntrace) { max = STUNTRACE_MAX_RETRANSMITS; } else { max = STUNCLIENT_MAX_RETRANSMITS; } if ( (trans->retransmits < max) && (stunTimeoutList[trans->retransmits] != 0) ) /* can be 0 terminated if * using fewer * retransmits **/ { char peer [SOCKADDR_MAX_STRLEN] = {0,}; sockaddr_toString( (struct sockaddr*) &trans->stunBindReq.serverAddr, peer, sizeof (peer), true ); StunPrint(client->logUserData, client->Log_cb, StunInfoCategory_Trace, "<STUNCLIENT:%02d> Retrans %s Retry: %d to %s", trans->inst, errStr, trans->retransmits + 1, peer); RetransmitLastReq(trans, &trans->stunBindReq.serverAddr); StartNextRetransmitTimer(trans); trans->retransmits++; trans->stats.Retransmits++; } else { CallBack(trans, stunResult); SetNextState(trans, FailedState); trans->stats.Failures++; } }
static void BindRespCallback(STUN_TRANSACTION_DATA* trans, const struct sockaddr* srcAddr) { STUN_CLIENT_DATA* client = trans->client; char ip_str [SOCKADDR_MAX_STRLEN]; StunCallBackData_T res; memset( &res, 0, sizeof (StunCallBackData_T) ); memcpy( &res.msgId, &trans->stunBindReq.transactionId, sizeof(StunMsgId) ); res.stunResult = StunResult_BindOk; sockaddr_copy( (struct sockaddr*)&res.rflxAddr, (struct sockaddr*)&trans->rflxAddr ); sockaddr_copy( (struct sockaddr*)&res.srcAddr, srcAddr ); sockaddr_copy( (struct sockaddr*)&res.dstBaseAddr, (struct sockaddr*)&trans->stunBindReq.baseAddr ); /* So did we loose a packet, or got an answer to the first response?*/ res.rtt = getRTTvalue(trans); res.ttl = trans->stunBindReq.ttl; StunPrint( client->logUserData, client->Log_cb, StunInfoCategory_Info, "<STUNCLIENT:%02d> BindResp from src: %s", trans->inst, sockaddr_toString( (struct sockaddr*) &res.srcAddr, ip_str, SOCKADDR_MAX_STRLEN, true ) ); if (trans->stunBindReq.stunCbFunc) { (trans->stunBindReq.stunCbFunc)(trans->stunBindReq.userCtx, &res); } }
/********** Server handling of incoming STUN BIND REQ **********/ bool StunServer_HandleStunIncomingBindReqMsg(STUN_CLIENT_DATA* clientData, STUN_INCOMING_REQ_DATA* pReq, const StunMessage* stunMsg, bool fromRelay) { if (!clientData) { return false; } memcpy( &pReq->transactionId, &stunMsg->msgHdr.id, sizeof(StunMsgId) ); pReq->fromRelay = fromRelay; if (stunMsg->hasUsername) { strncpy( pReq->ufrag, stunMsg->username.value, min(stunMsg->username.sizeValue, STUN_MAX_STRING) ); if (stunMsg->username.sizeValue < STUN_MAX_STRING) { pReq->ufrag[stunMsg->username.sizeValue] = '\0'; } else { pReq->ufrag[STUN_MAX_STRING - 1] = '\0'; } } else { StunPrint(clientData->logUserData, clientData->Log_cb, StunInfoCategory_Error, "<STUNCLIENT> Missing Username in Binding Request\n"); return false; } if (stunMsg->hasPriority) { pReq->peerPriority = stunMsg->priority.value; } else { StunPrint(clientData->logUserData, clientData->Log_cb, StunInfoCategory_Error, "<STUNCLIENT> Missing Priority in Binding Request\n"); return false; } pReq->useCandidate = stunMsg->hasUseCandidate; if (stunMsg->hasControlling) { pReq->iceControlling = true; pReq->tieBreaker = stunMsg->controlling.value; } else { pReq->iceControlling = false; } if (stunMsg->hasControlled) { pReq->iceControlled = true; pReq->tieBreaker = stunMsg->controlled.value; } else { pReq->iceControlled = false; } if (fromRelay) { clientData->stats.BindReqReceived_ViaRelay++; } clientData->stats.BindReqReceived++; return true; }
void StunClient_dumpStats (STUN_CLIENT_DATA* clientData, STUN_INFO_FUNC_PTR logPtr, void* userData) { struct StunClientStats stats; struct StunClientStats* ptr = &clientData->stats; int usedCnt = 0; memset(&stats, 0, sizeof stats); stats.InProgress += ptr->InProgress; stats.BindReqSent += ptr->BindReqSent; stats.BindReqSent_ViaRelay += ptr->BindReqSent_ViaRelay; stats.BindRespReceived += ptr->BindRespReceived; stats.BindRespReceived_AfterCancel += ptr->BindRespReceived_AfterCancel; stats.BindRespReceived_InIdle += ptr->BindRespReceived_InIdle; stats.BindRespReceived_ViaRelay += ptr->BindRespReceived_ViaRelay; stats.BindRespErrReceived += ptr->BindRespErrReceived; stats.BindReqReceived += ptr->BindReqReceived; stats.BindReqReceived_ViaRelay += ptr->BindReqReceived_ViaRelay; stats.BindRespSent += ptr->BindRespSent; stats.BindRespSent_ViaRelay += ptr->BindRespSent_ViaRelay; stats.Retransmits += ptr->Retransmits; stats.Failures += ptr->Failures; for (int i = 0; i < MAX_STUN_TRANSACTIONS; i++) { ptr = &clientData->data[i].stats; stats.InProgress += ptr->InProgress; stats.BindReqSent += ptr->BindReqSent; stats.BindReqSent_ViaRelay += ptr->BindReqSent_ViaRelay; stats.BindRespReceived += ptr->BindRespReceived; stats.BindRespReceived_AfterCancel += ptr->BindRespReceived_AfterCancel; stats.BindRespReceived_InIdle += ptr->BindRespReceived_InIdle; stats.BindRespReceived_ViaRelay += ptr->BindRespReceived_ViaRelay; stats.BindRespErrReceived += ptr->BindRespErrReceived; stats.BindReqReceived += ptr->BindReqReceived; stats.BindReqReceived_ViaRelay += ptr->BindReqReceived_ViaRelay; stats.BindRespSent += ptr->BindRespSent; stats.BindRespSent_ViaRelay += ptr->BindRespSent_ViaRelay; stats.Retransmits += ptr->Retransmits; stats.Failures += ptr->Failures; if (ptr->BindReqSent > 0) { usedCnt++; } } StunPrint(userData, logPtr, StunInfoCategory_Info, "<STUNCLIENTS used:%02d> Stats:" "\n\t InProgress %d," "\n\t BindReqSent %d," "\n\t BindRespReceived %d," "\n\t BindRespErrReceived %d," "\n\t BindReqReceived %d," "\n\t BindRespSent %d," "\n\t Retransmits %d," "\n\t Failures %d", usedCnt, stats.InProgress, stats.BindReqSent, stats.BindRespReceived, stats.BindRespErrReceived, stats.BindReqReceived, stats.BindRespSent, stats.Retransmits, stats.Failures); }
/* encode and send */ static bool SendStunReq(STUN_TRANSACTION_DATA* trans, StunMessage* stunReqMsg) { STUN_CLIENT_DATA* client = trans->client; /* encode the BindReq */ if (strlen(trans->stunBindReq.password) > 0) { trans->stunReqMsgBufLen = stunlib_encodeMessage(stunReqMsg, (unsigned char*) (trans-> stunReqMsgBuf), STUN_MAX_PACKET_SIZE, (unsigned char*)&trans->stunBindReq.password, /* key */ strlen(trans->stunBindReq. password), /* keyLen * */ NULL); } else { trans->stunReqMsgBufLen = stunlib_encodeMessage(stunReqMsg, (unsigned char*) (trans-> stunReqMsgBuf), STUN_MAX_PACKET_SIZE, NULL, /* key */ 0, /* keyLen */ NULL); } if (!trans->stunReqMsgBufLen) { StunPrint(client->logUserData, client->Log_cb, StunInfoCategory_Error, "<STUNCLIENT:%02d> SendStunReq(BindReq), failed encode", trans->inst); return false; } /*Store Time so we can messure RTT */ gettimeofday(&trans->start[trans->retransmits], NULL); if (trans->stunBindReq.sendFunc != NULL) { trans->stunBindReq.sendFunc(trans->client->userCtx, trans->stunBindReq.sockhandle, trans->stunReqMsgBuf, trans->stunReqMsgBufLen, (struct sockaddr*)&trans->stunBindReq.serverAddr, trans->stunBindReq.proto, trans->stunBindReq.useRelay, trans->stunBindReq.ttl); } trans->stats.BindReqSent++; return true; }