/****************************************************************************** * Copyright (c) 2000-2019 Ericsson Telecom AB * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html * * Contributors: * Roland Gecse - initial implementation and initial documentation * Akos Pernek * Antal Wuh.Hen.Chang * Attila Fulop * Balazs Barcsik * Bence Molnar * Csaba Koppany * David Juhasz * Eduard Czimbalmos * Elemer Lelik * Endre Kiss * Endre Kulcsar * Gabor Szalai * Gabor Tatarka * Gergely Futo * Istvan Sandor * Krisztian Pandi * Kulcsár Endre * Laszlo Tamas Zeke * Norbert Pinter * Roland Gecse * Tibor Bende * Tibor Szabo * Timea Moder * Zoltan Medve * Zsolt Nandor Torok * Zsolt Szalai ******************************************************************************/ // // File: DIAMETER_EncDec.cc // Description: Encoder/Decoder and external functions for DPMG // Rev: R55A // Prodnr: CNL 113 462 /////////////////////////////////////////////// #include "DIAMETER_Types.hh" //#include #include #include #include #include #include namespace DIAMETER__Types{ static const unsigned char os_h_or_e_id_octets[] = { 0, 0, 0, 0 }; static const OCTETSTRING os_h_or_e_id_oct(4, os_h_or_e_id_octets); INTEGER f__DIAMETER__genEndToEnd__int() { timeval precise; // requires static bool inititalized = false; static uint32_t l_value = 0; if(!inititalized){ long int seed = getpid(); if ( gettimeofday(&precise, NULL) != -1 ) { seed <<= 8; seed += (precise.tv_sec) & 0xFF; seed <<= 8; seed += (precise.tv_usec) & 0xFF; } srand48(seed); l_value =lrand48(); if ( gettimeofday(&precise, NULL) != -1 ) { l_value = (((uint32_t)precise.tv_sec) << 20) + ( l_value >> 12); } inititalized = true; } l_value+=1; // unsigned int -> can be overflowed safely if(l_value==0){ l_value+=1; } INTEGER ret; ret.set_long_long_val(l_value); return ret; } OCTETSTRING f__DIAMETER__genEndToEnd__oct() { return int2oct(f__DIAMETER__genEndToEnd__int(),4); } INTEGER f__DIAMETER__genHopByHop__int() { return f__DIAMETER__genEndToEnd__int(); } OCTETSTRING f__DIAMETER__genHopByHop__oct() { return int2oct(f__DIAMETER__genHopByHop__int(),4); } bool chk_zero(INTEGER var) {return var==0;} bool chk_zero(OCTETSTRING var) {return var==os_h_or_e_id_oct;} OCTETSTRING f__DIAMETER__Enc(const PDU__DIAMETER& pl__pdu) { PDU__DIAMETER* par=NULL; if (chk_zero(pl__pdu.hop__by__hop__id()) && f__get__R__bit(pl__pdu) ){ par = new PDU__DIAMETER(pl__pdu); par->hop__by__hop__id() = f__DIAMETER__genHopByHop(); } if (chk_zero(pl__pdu.end__to__end__id()) && f__get__R__bit(pl__pdu) ){ if(par==NULL) par = new PDU__DIAMETER(pl__pdu); par->end__to__end__id() = f__DIAMETER__genEndToEnd(); } TTCN_Buffer buf; TTCN_EncDec::error_type_t err; buf.clear(); TTCN_EncDec::clear_error(); TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_WARNING); if(par) par->encode(PDU__DIAMETER_descr_, buf, TTCN_EncDec::CT_RAW); else pl__pdu.encode(PDU__DIAMETER_descr_, buf, TTCN_EncDec::CT_RAW); err = TTCN_EncDec::get_last_error_type(); if(err != TTCN_EncDec::ET_NONE) TTCN_warning("Encoding error: %s\n", TTCN_EncDec::get_error_str()); delete par; return OCTETSTRING(buf.get_len(), buf.get_data()); } void f__DIAMETER__Enc__fast(const PDU__DIAMETER& pl__pdu, OCTETSTRING &pl__oct ) { PDU__DIAMETER* par=NULL; if (chk_zero(pl__pdu.hop__by__hop__id()) && f__get__R__bit(pl__pdu) ){ par = new PDU__DIAMETER(pl__pdu); par->hop__by__hop__id() = f__DIAMETER__genHopByHop(); } if (chk_zero(pl__pdu.end__to__end__id()) && f__get__R__bit(pl__pdu) ){ if(par==NULL) par = new PDU__DIAMETER(pl__pdu); par->end__to__end__id() = f__DIAMETER__genEndToEnd(); } TTCN_Buffer buf; TTCN_EncDec::error_type_t err; buf.clear(); TTCN_EncDec::clear_error(); TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_WARNING); if(par) par->encode(PDU__DIAMETER_descr_, buf, TTCN_EncDec::CT_RAW); else pl__pdu.encode(PDU__DIAMETER_descr_, buf, TTCN_EncDec::CT_RAW); err = TTCN_EncDec::get_last_error_type(); if(err != TTCN_EncDec::ET_NONE) TTCN_warning("Encoding error: %s\n", TTCN_EncDec::get_error_str()); delete par; pl__oct=OCTETSTRING(buf.get_len(), buf.get_data()); } PDU__DIAMETER f__DIAMETER__Dec(const OCTETSTRING& pl__oct) { PDU__DIAMETER pdu; TTCN_Buffer buf; TTCN_EncDec::error_type_t err; TTCN_EncDec::clear_error(); buf.clear(); buf.put_os(pl__oct); TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_WARNING); pdu.decode(PDU__DIAMETER_descr_, buf, TTCN_EncDec::CT_RAW); err = TTCN_EncDec::get_last_error_type(); if(err != TTCN_EncDec::ET_NONE) TTCN_warning("Decoding error: %s\n", TTCN_EncDec::get_error_str()); return pdu; } INTEGER f__DIAMETER__Dec__fast(const OCTETSTRING& pl__oct, PDU__DIAMETER &pl__pdu ) { TTCN_Buffer buf; TTCN_EncDec::error_type_t err; TTCN_EncDec::clear_error(); buf.clear(); buf.put_os(pl__oct); TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_WARNING); pl__pdu.decode(PDU__DIAMETER_descr_, buf, TTCN_EncDec::CT_RAW); err = TTCN_EncDec::get_last_error_type(); if(err != TTCN_EncDec::ET_NONE) TTCN_warning("Decoding error: %s\n", TTCN_EncDec::get_error_str()); return 0; } void f_DIAMETER_log_hex(const char *prompt, const unsigned char *msg, size_t length) { if (prompt != NULL) TTCN_Logger::log_event_str(prompt); TTCN_Logger::log_event("Size: %lu, Msg:", (unsigned long)length); for (size_t i = 0; i < length; i++) TTCN_Logger::log_event(" %02x", msg[i]); } OCTETSTRING f_GetAVPByListOfCodesFromGroupedAVP(const unsigned char *temp, int data_len, const integerList& pl__codeList, const bool orderedList, int& bestFromGrouped){ const unsigned char *endp=temp+data_len; int avpLength = 0; unsigned int avpCode = 0; int codelist_size=pl__codeList.size_of(); bool avpFound = false; unsigned char* bestAVP; int bestAVP_length = 0; //if the f_GetAVPByListOfCodesFromGroupedAVP function was called, the bestFromGrouped variable will be initialized, else: if(bestFromGrouped < 0)bestFromGrouped = codelist_size; while(temp + 8 < endp) { /* reading the avp code value*/ avpCode = ((unsigned int)(*temp) << 24) + ((unsigned int)(*(temp + 1)) << 16) + ((unsigned int)(*(temp + 2)) << 8) + (unsigned int)(*(temp + 3)); temp+=5; // AVP code & VMPxxxxx octets (checked later) // tmp now points to the first length octet /* calculating the length of the next AVP*/ avpLength = ((unsigned int)(*temp) << 16) + ((unsigned int)(*(temp + 1)) << 8) + (unsigned int)(*(temp + 2)); if(avpLength < 8) { TTCN_Logger::begin_event( TTCN_WARNING ); TTCN_Logger::log_event("Invalid AVP length: %d; ",avpLength); f_DIAMETER_log_hex("AVP octets: ", temp - 5, endp - (temp + 5)); TTCN_Logger::end_event(); break; } avpLength-=8; // length of AVP data = AVP length - 4 - Vendor ID length (calculated below) if(*(temp-1) & 0x80){ // skip vendor id, VMPxxxxx is just before the first length octet avpLength-=4; temp+=4; } temp+=3; // skip length octets // Now temp points to the AVP data and avpLength holds the length of the data part if(orderedList){ /*checks whether the avp code value equals with one of the given avp code values or not*/ for(int i = 0; i < bestFromGrouped; i++) { if((const int)(pl__codeList[i]) == (int)avpCode) { if(i == 0)return OCTETSTRING(avpLength, temp); //Found AVP with highest priority avpFound = true; bestAVP = (unsigned char *)temp; bestAVP_length = avpLength; bestFromGrouped = i; //Already found an AVP, no need to check lower priority AVPs in the future } }; } else { /*checks whether the avp code value equals with one of the given avp code values or not*/ for(int i = 0; i < codelist_size; i++) { if((const int)(pl__codeList[i]) == (int)avpCode) { return OCTETSTRING(avpLength > 0? avpLength:0, temp); } } }; // skip to the next avp if(avpLength % 4 != 0) // AVP padding { avpLength = avpLength + (4 - (avpLength % 4)); } temp+=avpLength; } if(avpFound){ return OCTETSTRING(bestAVP_length, bestAVP); } else { /* no AVP was found, returns an empty octetstring*/ unsigned char noResult[0]; return OCTETSTRING(0, noResult); }; } OCTETSTRING f__DIAMETER__GetAVPByListOfCodesCombined(const OCTETSTRING& pl__oct, const integerList& pl__codeList, const integerList& pl__groupcodeList) { const unsigned char *temp= (const unsigned char *)pl__oct; const unsigned char *endp=temp+pl__oct.lengthof(); temp += 20; // skip the DIAMETER header int avpLength = 0; unsigned int avpCode = 0; int codelist_size=pl__codeList.size_of(); int groupcodeList_size=pl__groupcodeList.size_of(); unsigned char noResult[0]; OCTETSTRING result = OCTETSTRING(0, noResult); bool avpFound = false; unsigned char* bestAVP; int bestAVP_length = 0; int bestFromGrouped = codelist_size; //Initializing the bestFromGrouped variable for the f_GetAVPByListOfCodesFromGroupedAVP function while(temp + 8 < endp) { /* reading the avp code value*/ avpCode = ((unsigned int)(*temp) << 24) + ((unsigned int)(*(temp + 1)) << 16) + ((unsigned int)(*(temp + 2)) << 8) + (unsigned int)(*(temp + 3)); temp+=5; // AVP code & VMPxxxxx octets // tmp now points to the first length octet /* calculating the length of the next AVP*/ avpLength = ((unsigned int)(*temp) << 16) + ((unsigned int)(*(temp + 1)) << 8) + (unsigned int)(*(temp + 2)); if(avpLength < 8) { TTCN_Logger::begin_event( TTCN_WARNING ); TTCN_Logger::log_event("Invalid AVP length: %d; msg: ",avpLength); pl__oct.log(); TTCN_Logger::end_event(); break; } avpLength-=8; // length of AVP data = AVP length - 4 - Vendor ID length (calculated below) if(*(temp-1) & 0x80){ // skip vendor id, VMPxxxxx is just before the first length octet avpLength-=4; temp+=4; } temp+=3; // skip length octets // Now temp points to the AVP data and avpLength holds the length of the data part /*checks whether the avp code value equals with one of the given avp code values or not*/ for(int i = 0; i < codelist_size; i++) { if((const int)(pl__codeList[i]) == (int)avpCode) { if(i == 0)return OCTETSTRING(avpLength > 0? avpLength:0, temp); //Found AVP with highest priority avpFound = true; bestAVP = (unsigned char *)temp; bestAVP_length = avpLength; codelist_size = i; //Already found an AVP, no need to check lower priority AVPs in the future } } if(avpFound == false && bestFromGrouped != 0){//Simple AVPs have higher priority, so if we found one, it is not necessary to search in the grouped AVPs. //Chek for the groupped AVP to search in for(int i = 0; i < groupcodeList_size; i++) { if((const int)(pl__groupcodeList[i]) == (int)avpCode) { OCTETSTRING ret_val=f_GetAVPByListOfCodesFromGroupedAVP(temp,avpLength,pl__codeList, true, bestFromGrouped); if(ret_val.lengthof()>0){ result = ret_val; } break; // escape from the for loop } } }; // skip to the next avp if(avpLength % 4 != 0) // AVP padding { avpLength = avpLength + (4 - (avpLength % 4)); } temp+=avpLength; } bestFromGrouped = -1; if(avpFound){ return OCTETSTRING(bestAVP_length, bestAVP); } else { /* Grouped AVP or no AVP was found, returns an empty octetstring*/ return result; }; } OCTETSTRING f__DIAMETER__GetAVPByListOfCodes(const OCTETSTRING& pl__oct, const integerList& pl__codeList) { static const integerList glist=NULL_VALUE; return f__DIAMETER__GetAVPByListOfCodesCombined(pl__oct,pl__codeList,glist); } OCTETSTRING f__DIAMETER__GetAVPByListOfCodesFromGroupedAVP(const OCTETSTRING& pl__oct, const integerList& pl__codeList) { int bestFromGrouped = -1; return f_GetAVPByListOfCodesFromGroupedAVP((const unsigned char *)pl__oct,pl__oct.lengthof(),pl__codeList, false, bestFromGrouped); } } TTCN_Module DIAMETER_EncDec("DIAMETER_EncDec", __DATE__, __TIME__);