From f2964f8319ab0b6cc788f329bcf19fc1c5b46ec2 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Jan 26 2010 10:21:45 +0000 Subject: Updated to svn ver. 586 --- diff --git a/import.log b/import.log index 63b95af..36d6510 100644 --- a/import.log +++ b/import.log @@ -1,2 +1,3 @@ sipp-3_1-2_fc9:HEAD:sipp-3.1-2.fc9.src.rpm:1215351390 sipp-3_1-5_fc11:HEAD:sipp-3.1-5.fc11.src.rpm:1245321037 +sipp-3_1-8_svn586_fc12:F-12:sipp-3.1-8.svn586.fc12.src.rpm:1264501276 diff --git a/sipp--3.1svn586.diff b/sipp--3.1svn586.diff new file mode 100644 index 0000000..69b647f --- /dev/null +++ b/sipp--3.1svn586.diff @@ -0,0 +1,10063 @@ +diff --git a/Makefile b/Makefile +index a215e39..23a3fac 100644 +--- a/Makefile ++++ b/Makefile +@@ -27,7 +27,8 @@ OUTPUT=sipp + + # C & C++ object files to be built + OBJ= xp_parser.o message.o scenario.o screen.o call.o comp.o sipp.o stat.o \ +- actions.o variables.o infile.o deadcall.o task.o socketowner.o listener.o ++ actions.o variables.o infile.o deadcall.o task.o socketowner.o listener.o \ ++ opentask.o reporttask.o watchdog.o + + # Libraries directories + LIBDIR_linux= +@@ -160,43 +161,46 @@ INCDIR=$(INCDIR_$(SYSTEM)) + + # Building without TLS and authentication (no openssl pre-requisite) + all: +- make OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` $(OUTPUT) ++ $(MAKE) OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` $(OUTPUT) + + # Building with TLS and authentication + ossl: +- make OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_TLS="auth.o sslinit.o sslthreadsafe.o milenage.o rijndael.o" TLS_LIBS="-lssl -lcrypto" TLS="-D_USE_OPENSSL -DOPENSSL_NO_KRB5" $(OUTPUT) ++ $(MAKE) OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_TLS="auth.o sslinit.o sslthreadsafe.o milenage.o rijndael.o" TLS_LIBS="-lssl -lcrypto" TLS="-D_USE_OPENSSL -DOPENSSL_NO_KRB5" $(OUTPUT) + + #Building with PCAP play + pcapplay: +- make OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_PCAPPLAY="send_packets.o prepare_pcap.o" PCAPPLAY_LIBS="-lpcap" PCAPPLAY="-DPCAPPLAY" $(OUTPUT) ++ $(MAKE) OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_PCAPPLAY="send_packets.o prepare_pcap.o" PCAPPLAY_LIBS="-lpcap" PCAPPLAY="-DPCAPPLAY" $(OUTPUT) + + pcapplay_ossl: +- make OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_TLS="auth.o sslinit.o sslthreadsafe.o milenage.o rijndael.o" TLS_LIBS="-lssl -lcrypto" TLS="-D_USE_OPENSSL -DOPENSSL_NO_KRB5" OBJ_PCAPPLAY="send_packets.o prepare_pcap.o" PCAPPLAY_LIBS="-lpcap `if test -f ./ext; then echo -L./ext/lib; fi;`" PCAPPLAY="-DPCAPPLAY `if test -f ./ext; then echo -I./ext/include; fi;`" $(OUTPUT) ++ $(MAKE) OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_TLS="auth.o sslinit.o sslthreadsafe.o milenage.o rijndael.o" TLS_LIBS="-lssl -lcrypto" TLS="-D_USE_OPENSSL -DOPENSSL_NO_KRB5" OBJ_PCAPPLAY="send_packets.o prepare_pcap.o" PCAPPLAY_LIBS="-lpcap `if test -f ./ext; then echo -L./ext/lib; fi;`" PCAPPLAY="-DPCAPPLAY `if test -f ./ext; then echo -I./ext/include; fi;`" $(OUTPUT) + + pcapplay_hp_li_ia: +- @_HPUX_LI_FLAG=-D_HPUX_LI ; export _HPUX_LI_FLAG ; make pcapplay ++ @_HPUX_LI_FLAG=-D_HPUX_LI ; export _HPUX_LI_FLAG ; $(MAKE) pcapplay + + pcapplay_ossl_hp_li_ia: +- @_HPUX_LI_FLAG=-D_HPUX_LI ; export _HPUX_LI_FLAG ; make pcapplay_ossl ++ @_HPUX_LI_FLAG=-D_HPUX_LI ; export _HPUX_LI_FLAG ; $(MAKE) pcapplay_ossl + + pcapplay_cygwin: +- make OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_PCAPPLAY="send_packets.o prepare_pcap.o" PCAPPLAY_LIBS="-lwpcap" PCAPPLAY="-DPCAPPLAY" $(OUTPUT) ++ $(MAKE) OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_PCAPPLAY="send_packets.o prepare_pcap.o" PCAPPLAY_LIBS="-lwpcap" PCAPPLAY="-DPCAPPLAY" $(OUTPUT) + + pcapplay_ossl_cygwin: +- make OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_TLS="auth.o sslinit.o sslthreadsafe.o milenage.o rijndael.o" TLS_LIBS="-lssl -lcrypto" TLS="-D_USE_OPENSSL -DOPENSSL_NO_KRB5" OBJ_PCAPPLAY="send_packets.o prepare_pcap.o" PCAPPLAY_LIBS="-lwpcap" PCAPPLAY="-DPCAPPLAY" $(OUTPUT) ++ $(MAKE) OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_TLS="auth.o sslinit.o sslthreadsafe.o milenage.o rijndael.o" TLS_LIBS="-lssl -lcrypto" TLS="-D_USE_OPENSSL -DOPENSSL_NO_KRB5" OBJ_PCAPPLAY="send_packets.o prepare_pcap.o" PCAPPLAY_LIBS="-lwpcap" PCAPPLAY="-DPCAPPLAY" $(OUTPUT) + + $(OUTPUT): $(OBJ_TLS) $(OBJ_PCAPPLAY) $(OBJ) + $(CCLINK) $(LFLAGS) $(MFLAGS) $(LIBDIR_$(SYSTEM)) \ + $(DEBUG_FLAGS) -o $@ $(OBJ_TLS) $(OBJ_PCAPPLAY) $(OBJ) $(LIBS) $(TLS_LIBS) $(PCAPPLAY_LIBS) $(EXTRAENDLIBS) + + debug: +- DEBUG_FLAGS="-g -pg" ; export DEBUG_FLAGS ; make all ++ DEBUG_FLAGS="-g -pg" ; export DEBUG_FLAGS ; $(MAKE) all + + debug_ossl: +- @DEBUG_FLAGS=-g ; export DEBUG_FLAGS ; make ossl ++ @DEBUG_FLAGS=-g ; export DEBUG_FLAGS ; $(MAKE) ossl ++ ++debug_pcap: ++ @DEBUG_FLAGS=-g ; export DEBUG_FLAGS ; make pcapplay + + debug_pcap_cygwin: +- @DEBUG_FLAGS=-g ; export DEBUG_FLAGS ; make pcapplay_ossl_cygwin ++ @DEBUG_FLAGS=-g ; export DEBUG_FLAGS ; $(MAKE) pcapplay_ossl_cygwin + + clean: + rm -f *.o $(OUTPUT) *~ $(TOCLEAN) +@@ -204,7 +208,7 @@ clean: + + archive: + rm -f TMP_TAR_FILE.* $(ARCHIVE) +- make clean ++ $(MAKE) clean + tar cf TMP_TAR_FILE.tar . + gzip TMP_TAR_FILE.tar + cp TMP_TAR_FILE.tar.gz $(ARCHIVE) +@@ -224,3 +228,7 @@ archive: + + .c.o: + $(CC) $(CFLAGS) $(MFLAGS) $(DEBUG_FLAGS) $(_HPUX_LI_FLAG) $(INCDIR) -c -o $*.o $< ++ ++fortune.so: fortune.cpp ++ g++ -fPIC $(CPPFLAGS) $(MFLAGS) $(DEBUG_FLAGS) $(_HPUX_LI_FLAG) $(INCDIR) -c -o fortune.o $< ++ gcc -shared -Wl,-soname,fortune.so -o $@ fortune.o +diff --git a/actions.cpp b/actions.cpp +index 92ad968..f944233 100644 +--- a/actions.cpp ++++ b/actions.cpp +@@ -66,7 +66,7 @@ const char * CAction::comparatorToString(T_Comparator comp) { + + bool CAction::compare(VariableTable *variableTable) { + double lhs = variableTable->getVar(M_varInId)->getDouble(); +- double rhs = M_doubleValue; ++ double rhs = M_varIn2Id ? variableTable->getVar(M_varIn2Id)->getDouble() : M_doubleValue; + + switch(M_comp) { + case E_C_EQ: +@@ -91,58 +91,60 @@ void CAction::afficheInfo() + { + if (M_action == E_AT_ASSIGN_FROM_REGEXP) { + if(M_lookingPlace == E_LP_MSG) { +- printf("Type[%d] - regexp[%s] where[%s] - checkIt[%d] - $%s", ++ printf("Type[%d] - regexp[%s] where[%s] - checkIt[%d] - checkItInverse[%d] - $%s", + M_action, + M_regularExpression, + "Full Msg", + M_checkIt, ++ M_checkItInverse, + display_scenario->allocVars->getName(M_varId)); + } else { +- printf("Type[%d] - regexp[%s] where[%s-%s] - checkIt[%d] - $%d", ++ printf("Type[%d] - regexp[%s] where[%s-%s] - checkIt[%d] - checkItInverse[%d] - $%s", + M_action, + M_regularExpression, + "Header", + M_lookingChar, +- M_checkIt, display_scenario->allocVars->getName(M_varId)); ++ M_checkIt, ++ M_checkItInverse, display_scenario->allocVars->getName(M_varId)); + } + } else if (M_action == E_AT_EXECUTE_CMD) { +- if (M_cmdLine) { +- printf("Type[%d] - command[%-32.32s]", M_action, M_cmdLine); +- } ++ printf("Type[%d] - command[%-32.32s]", M_action, M_message_str[0]); + } else if (M_action == E_AT_EXEC_INTCMD) { + printf("Type[%d] - intcmd[%-32.32s]", M_action, strIntCmd(M_IntCmd)); + } else if (M_action == E_AT_LOG_TO_FILE) { +- printf("Type[%d] - message[%-32.32s]", M_action, M_message[0]); ++ printf("Type[%d] - message[%-32.32s]", M_action, M_message_str[0]); + } else if (M_action == E_AT_LOG_WARNING) { +- printf("Type[%d] - warning[%-32.32s]", M_action, M_message[0]); ++ printf("Type[%d] - warning[%-32.32s]", M_action, M_message_str[0]); ++ } else if (M_action == E_AT_LOG_ERROR) { ++ printf("Type[%d] - error[%-32.32s]", M_action, M_message_str[0]); + } else if (M_action == E_AT_ASSIGN_FROM_SAMPLE) { + char tmp[40]; + M_distribution->textDescr(tmp, sizeof(tmp)); +- printf("Type[%d] - sample varId[%d] %s", M_action, display_scenario->allocVars->getName(M_varId), tmp); ++ printf("Type[%d] - sample varId[%s] %s", M_action, display_scenario->allocVars->getName(M_varId), tmp); + } else if (M_action == E_AT_ASSIGN_FROM_VALUE) { +- printf("Type[%d] - assign varId[%d] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue); ++ printf("Type[%d] - assign varId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue); + } else if (M_action == E_AT_ASSIGN_FROM_INDEX) { +- printf("Type[%d] - assign index[%d]", M_action, display_scenario->allocVars->getName(M_varId)); ++ printf("Type[%d] - assign index[%s]", M_action, display_scenario->allocVars->getName(M_varId)); + } else if (M_action == E_AT_ASSIGN_FROM_GETTIMEOFDAY) { +- printf("Type[%d] - assign gettimeofday[%d, %d]", M_action, display_scenario->allocVars->getName(M_varId)); ++ printf("Type[%d] - assign gettimeofday[%s, %s]", M_action, display_scenario->allocVars->getName(M_varId), display_scenario->allocVars->getName(M_subVarId[0])); + } else if (M_action == E_AT_ASSIGN_FROM_STRING) { +- printf("Type[%d] - string assign varId[%d] [%-32.32s]", M_action, display_scenario->allocVars->getName(M_varId), M_message[0]); ++ printf("Type[%d] - string assign varId[%s] [%-32.32s]", M_action, display_scenario->allocVars->getName(M_varId), M_message_str[0]); + } else if (M_action == E_AT_JUMP) { +- printf("Type[%d] - jump varInId[%d] %lf", M_action, display_scenario->allocVars->getName(M_varInId), M_doubleValue); ++ printf("Type[%d] - jump varInId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varInId), M_doubleValue); + } else if (M_action == E_AT_PAUSE_RESTORE) { +- printf("Type[%d] - restore pause varInId[%d] %lf", M_action, display_scenario->allocVars->getName(M_varInId), M_doubleValue); ++ printf("Type[%d] - restore pause varInId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varInId), M_doubleValue); + } else if (M_action == E_AT_VAR_ADD) { +- printf("Type[%d] - add varId[%d] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue); ++ printf("Type[%d] - add varId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue); + } else if (M_action == E_AT_VAR_MULTIPLY) { +- printf("Type[%d] - multiply varId[%d] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue); ++ printf("Type[%d] - multiply varId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue); + } else if (M_action == E_AT_VAR_DIVIDE) { +- printf("Type[%d] - divide varId[%d] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue); ++ printf("Type[%d] - divide varId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue); + } else if (M_action == E_AT_VAR_TRIM) { +- printf("Type[%d] - trim varId[%d]", M_action, display_scenario->allocVars->getName(M_varId)); ++ printf("Type[%d] - trim varId[%s]", M_action, display_scenario->allocVars->getName(M_varId)); + } else if (M_action == E_AT_VAR_TEST) { +- printf("Type[%d] - divide varId[%d] varInId[%d] %s %lf", M_action, display_scenario->allocVars->getName(M_varId), display_scenario->allocVars->getName(M_varInId), comparatorToString(M_comp), M_doubleValue); ++ printf("Type[%d] - divide varId[%s] varInId[%s] %s %lf", M_action, display_scenario->allocVars->getName(M_varId), display_scenario->allocVars->getName(M_varInId), comparatorToString(M_comp), M_doubleValue); + } else if (M_action == E_AT_VAR_TO_DOUBLE) { +- printf("Type[%d] - toDouble varId[%d]", M_action, display_scenario->allocVars->getName(M_varId)); ++ printf("Type[%d] - toDouble varId[%s]", M_action, display_scenario->allocVars->getName(M_varId)); + #ifdef PCAPPLAY + } else if ((M_action == E_AT_PLAY_PCAP_AUDIO) || (M_action == E_AT_PLAY_PCAP_VIDEO)) { + printf("Type[%d] - file[%s]", M_action, M_pcapArgs->file); +@@ -159,14 +161,15 @@ CAction::T_IntCmdType CAction::getIntCmd () { return(M_IntCmd); } + CAction::T_Comparator CAction::getComparator () { return(M_comp); } + + bool CAction::getCheckIt() { return(M_checkIt); } ++bool CAction::getCheckItInverse() { return(M_checkItInverse); } + bool CAction::getCaseIndep() { return(M_caseIndep); } + bool CAction::getHeadersOnly() { return(M_headersOnly); } + int CAction::getOccurence() { return(M_occurence); } + int CAction::getVarId() { return(M_varId); } + int CAction::getVarInId() { return(M_varInId); } ++int CAction::getVarIn2Id() { return(M_varIn2Id); } + char* CAction::getLookingChar() { return(M_lookingChar); } + SendingMessage *CAction::getMessage(int n) { return(M_message[n]); } +-SendingMessage *CAction::getCmdLine() { return(M_cmdLine); } + CSample* CAction::getDistribution() { return(M_distribution); } + double CAction::getDoubleValue() { return(M_doubleValue); } + char* CAction::getStringValue() { return(M_stringValue); } +@@ -180,10 +183,14 @@ void CAction::setLookingPlace (CAction::T_LookingPlace P_value) + { M_lookingPlace = P_value; } + void CAction::setCheckIt (bool P_value) + { M_checkIt = P_value; } ++void CAction::setCheckItInverse (bool P_value) ++{ M_checkItInverse = P_value; } + void CAction::setVarId (int P_value) + { M_varId = P_value; } + void CAction::setVarInId (int P_value) + { M_varInId = P_value; } ++void CAction::setVarIn2Id (int P_value) ++{ M_varIn2Id = P_value; } + void CAction::setCaseIndep (bool P_value) + { M_caseIndep = P_value; } + void CAction::setOccurence (int P_value) +@@ -328,41 +335,22 @@ int CAction::executeRegExp(char* P_string, VariableTable *P_callVarTable) + void CAction::setSubString(char** P_target, char* P_source, int P_start, int P_stop) + { + int sizeOf; +- int sourceLength; +- size_t L_size = 0; + + if(P_source != NULL) { + sizeOf = P_stop - P_start; +- if(sizeOf > 0) { +- L_size = (size_t) sizeOf; +- L_size += 1; +- (*P_target) = new char[L_size]; ++ (*P_target) = new char[sizeOf + 1]; + ++ if (sizeOf > 0) { + memcpy((*P_target), &(P_source[P_start]), sizeOf); ++ } + +- (*P_target)[sizeOf] = '\0'; +- } ++ (*P_target)[sizeOf] = '\0'; + } else { + *P_target = NULL ; + } + } + + +-void CAction::setCmdLine (char* P_value) +-{ +- if(M_cmdLine != NULL) +- { +- delete [] M_cmdLine; +- M_cmdLine = NULL; +- } +- +- if(P_value != NULL) +- { +- M_cmdLine_str = strdup(P_value); +- M_cmdLine = new SendingMessage(M_scenario, P_value, true /* skip sanity */); +- } +-} +- + #ifdef PCAPPLAY + void CAction::setPcapArgs (pcap_pkts * P_value) + { +@@ -426,6 +414,7 @@ void CAction::setAction(CAction P_action) + + setLookingChar ( P_action.getLookingChar() ); + setCheckIt ( P_action.getCheckIt() ); ++ setCheckItInverse ( P_action.getCheckItInverse() ); + setCaseIndep ( P_action.getCaseIndep() ); + setOccurence ( P_action.getOccurence() ); + setHeadersOnly ( P_action.getHeadersOnly() ); +@@ -433,7 +422,6 @@ void CAction::setAction(CAction P_action) + setMessage(P_action.M_message_str[L_i], L_i); + } + setRegExp ( P_action.M_regularExpression); +- setCmdLine ( P_action.M_cmdLine_str ); + setIntCmd ( P_action.M_IntCmd ); + #ifdef PCAPPLAY + setPcapArgs ( P_action.M_pcapArgs ); +@@ -445,12 +433,14 @@ CAction::CAction(scenario *scenario) + M_action = E_AT_NO_ACTION; + M_varId = 0; + M_varInId = 0; ++ M_varIn2Id = 0; + + M_nbSubVarId = 0; + M_maxNbSubVarId = 0; + M_subVarId = NULL; + + M_checkIt = false; ++ M_checkItInverse = false; + M_lookingPlace = E_LP_MSG; + M_lookingChar = NULL; + M_caseIndep = false; +@@ -460,7 +450,6 @@ CAction::CAction(scenario *scenario) + M_message[i] = NULL; + M_message_str[i] = NULL; + } +- M_cmdLine = NULL; + M_IntCmd = E_INTCMD_INVALID; + M_doubleValue = 0; + M_stringValue = NULL; +@@ -468,7 +457,6 @@ CAction::CAction(scenario *scenario) + #ifdef PCAPPLAY + M_pcapArgs = NULL; + #endif +- M_cmdLine_str = NULL; + M_scenario = scenario; + M_regExpSet = false; + M_regularExpression = NULL; +@@ -490,16 +478,6 @@ CAction::~CAction() + free(M_message_str[i]); + M_message_str[i] = NULL; + } +- if(M_cmdLine != NULL) +- { +- delete M_cmdLine; +- M_cmdLine = NULL; +- } +- if(M_cmdLine_str != NULL) +- { +- delete M_cmdLine_str; +- M_cmdLine_str = NULL; +- } + if(M_subVarId != NULL) + { + delete [] M_subVarId; +diff --git a/actions.hpp b/actions.hpp +index 42076a5..8e3f1b0 100644 +--- a/actions.hpp ++++ b/actions.hpp +@@ -31,7 +31,7 @@ class CSample; + #include "prepare_pcap.h" + #endif + +-#define MAX_ACTION_MESSAGE 2 ++#define MAX_ACTION_MESSAGE 3 + + class CAction + { +@@ -48,9 +48,12 @@ class CAction + E_AT_ASSIGN_FROM_GETTIMEOFDAY, + E_AT_JUMP, + E_AT_LOOKUP, ++ E_AT_INSERT, ++ E_AT_REPLACE, + E_AT_PAUSE_RESTORE, + E_AT_LOG_TO_FILE, + E_AT_LOG_WARNING, ++ E_AT_LOG_ERROR, + E_AT_EXECUTE_CMD, + E_AT_EXEC_INTCMD, + E_AT_VAR_ADD, +@@ -62,6 +65,8 @@ class CAction + E_AT_VAR_STRCMP, + E_AT_VAR_TRIM, + E_AT_VERIFY_AUTH, ++ E_AT_SET_DEST, ++ E_AT_CLOSE_CON, + #ifdef PCAPPLAY + E_AT_PLAY_PCAP_AUDIO, + E_AT_PLAY_PCAP_VIDEO, +@@ -73,6 +78,8 @@ class CAction + { + E_LP_MSG = 0, + E_LP_HDR, ++ E_LP_BODY, ++ E_LP_VAR, + E_LP_NB_LOOKING_PLACE + }; + +@@ -108,15 +115,16 @@ class CAction + T_LookingPlace getLookingPlace(); + T_Comparator getComparator(); + bool getCheckIt(); ++ bool getCheckItInverse(); + bool getCaseIndep(); + bool getHeadersOnly(); + int getVarId(); + int getVarInId(); ++ int getVarIn2Id(); + int getOccurence(); + char* getLookingChar(); + char* getRegularExpression(); + SendingMessage *getMessage(int n = 0); /* log specific function */ +- SendingMessage *getCmdLine(); /* exec specific function */ + T_IntCmdType getIntCmd(); /* exec specific function */ + #ifdef PCAPPLAY + pcap_pkts *getPcapPkts(); /* send_packets specific function */ +@@ -126,8 +134,10 @@ class CAction + void setLookingPlace (T_LookingPlace P_value); + void setComparator (T_Comparator P_value); + void setCheckIt (bool P_value); ++ void setCheckItInverse (bool P_value); + void setVarId (int P_value); + void setVarInId (int P_value); ++ void setVarIn2Id (int P_value); + void setLookingChar (char* P_value); + void setAction (CAction P_action); + void setCaseIndep (bool P_action); +@@ -137,7 +147,6 @@ class CAction + void setRegExp (char* P_value); /* ereg specific function. */ + int executeRegExp (char* P_string, VariableTable *P_callVarTable); + void setMessage (char* P_value, int n = 0); /* log specific function */ +- void setCmdLine (char* P_value); /* exec specific function */ + void setIntCmd (T_IntCmdType P_type ); /* exec specific function */ + void setDistribution (CSample * P_value); /* sample specific function */ + void setDoubleValue (double P_value); /* assign value specific function */ +@@ -164,10 +173,12 @@ class CAction + T_LookingPlace M_lookingPlace; + T_Comparator M_comp; + bool M_checkIt; ++ bool M_checkItInverse; + bool M_caseIndep; + bool M_headersOnly; + int M_varId; + int M_varInId; ++ int M_varIn2Id; + int M_occurence; + int M_nbSubVarId; + int M_maxNbSubVarId; +@@ -178,8 +189,6 @@ class CAction + SendingMessage *M_message[MAX_ACTION_MESSAGE]; + char * M_message_str[MAX_ACTION_MESSAGE]; + /* exec specific member */ +- SendingMessage *M_cmdLine; +- char* M_cmdLine_str; + T_IntCmdType M_IntCmd; + /* sample specific member. */ + CSample *M_distribution; +diff --git a/auth.c b/auth.c +index b504eec..3e96037 100644 +--- a/auth.c ++++ b/auth.c +@@ -22,7 +22,7 @@ + * Frederique Aurouet + */ + +-#if defined( __FreeBSD__) || defined(__DARWIN) ++#if defined( __FreeBSD__) || defined(__DARWIN) || defined(__SUNOS) + #include + #endif + #include +@@ -31,6 +31,7 @@ + #include + #include + #include "milenage.h" ++#include "screen.hpp" + + #define MAX_HEADER_LEN 2049 + #define MD5_HASH_SIZE 16 +@@ -219,7 +220,6 @@ int createAuthHeaderMD5(char * user, char * password, int password_len, char * m + unsigned char ha1_hex[HASH_HEX_SIZE+1], ha2_hex[HASH_HEX_SIZE+1]; + unsigned char resp_hex[HASH_HEX_SIZE+1], body_hex[HASH_HEX_SIZE+1]; + char tmp[MAX_HEADER_LEN], authtype[16], cnonce[32], nc[32], opaque[64]; +- char *start, *end; + static unsigned int mync = 1; + int has_opaque = 0; + MD5_CTX Md5Ctx; +@@ -318,45 +318,12 @@ int createAuthHeaderMD5(char * user, char * password, int password_len, char * m + return 1; + } + +-int verifyAuthHeader(char * user, char * password, char * method, char * auth) { +- char algo[MAX_HEADER_LEN]; +- char result[HASH_HEX_SIZE]; +- char response[HASH_HEX_SIZE + 2]; +- char realm[MAX_HEADER_LEN]; +- char nonce[MAX_HEADER_LEN]; +- char uri[MAX_HEADER_LEN]; +- char *start, *end; +- int len; +- +- if ((start = stristr(auth, "Digest")) == NULL) { +- WARNING("verifyAuthHeader: authentication must be digest is %s", auth); +- return 0; +- } +- +- len = getAuthParameter("algorithm", auth, algo, sizeof(algo)); +- if (algo[0] == '\0') { +- strcpy(algo, "MD5"); +- } +- if (strncasecmp(algo, "MD5", 3)==0) { +- getAuthParameter("realm", auth, realm, sizeof(realm)); +- getAuthParameter("uri", auth, uri, sizeof(uri)); +- getAuthParameter("nonce", auth, nonce, sizeof(nonce)); +- createAuthResponseMD5(user,password,strlen(password),method,uri,realm,nonce,result); +- getAuthParameter("response", auth, response, sizeof(response)); +- return !strcmp(result, response); +- }else{ +- WARNING("createAuthHeader: authentication must use MD5 or AKAv1-MD5, value is '%s'", algo); +- return 0; +- } +-} +- + int createAuthResponseMD5(char * user, char * password, int password_len, char * method, +- char * uri, char * realm, char *nonce, char * result) { ++ char * uri, char * realm, char *nonce, unsigned char * result) { + unsigned char ha1[MD5_HASH_SIZE], ha2[MD5_HASH_SIZE]; + unsigned char resp[MD5_HASH_SIZE]; + unsigned char ha1_hex[HASH_HEX_SIZE+1], ha2_hex[HASH_HEX_SIZE+1]; + char tmp[MAX_HEADER_LEN]; +- char *start, *end; + MD5_CTX Md5Ctx; + + // Load in A1 +@@ -395,6 +362,39 @@ int createAuthResponseMD5(char * user, char * password, int password_len, char * + return 1; + } + ++int verifyAuthHeader(char * user, char * password, char * method, char * auth) { ++ char algo[MAX_HEADER_LEN]; ++ unsigned char result[HASH_HEX_SIZE]; ++ char response[HASH_HEX_SIZE + 2]; ++ char realm[MAX_HEADER_LEN]; ++ char nonce[MAX_HEADER_LEN]; ++ char uri[MAX_HEADER_LEN]; ++ char *start; ++ int len; ++ ++ if ((start = stristr(auth, "Digest")) == NULL) { ++ WARNING("verifyAuthHeader: authentication must be digest is %s", auth); ++ return 0; ++ } ++ ++ len = getAuthParameter("algorithm", auth, algo, sizeof(algo)); ++ if (algo[0] == '\0') { ++ strcpy(algo, "MD5"); ++ } ++ if (strncasecmp(algo, "MD5", 3)==0) { ++ getAuthParameter("realm", auth, realm, sizeof(realm)); ++ getAuthParameter("uri", auth, uri, sizeof(uri)); ++ getAuthParameter("nonce", auth, nonce, sizeof(nonce)); ++ createAuthResponseMD5(user,password,strlen(password),method,uri,realm,nonce,result); ++ getAuthParameter("response", auth, response, sizeof(response)); ++ return !strcmp((char *)result, response); ++ }else{ ++ WARNING("createAuthHeader: authentication must use MD5 or AKAv1-MD5, value is '%s'", algo); ++ return 0; ++ } ++} ++ ++ + + /*" + ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";*/ +diff --git a/call.cpp b/call.cpp +index 4ba59b6..6830304 100644 +--- a/call.cpp ++++ b/call.cpp +@@ -33,6 +33,9 @@ + * Ben Evans from Open Cloud + * Marc Van Diest from Belgacom + * Michael Dwyer from Cibation ++ * Roland Meub ++ * Andy Aicken ++ * Martin H. VanLeeuwen + */ + + #include +@@ -49,6 +52,8 @@ + #include "deadcall.hpp" + #include "assert.h" + ++#define callDebug(args...) do { if (useCallDebugf) { _callDebug( args ); } } while (0) ++ + #ifdef _USE_OPENSSL + extern SSL *ssl_list[]; + extern struct pollfd pollfiles[]; +@@ -61,6 +66,10 @@ extern map map_perip_fd; + /* send_packets pthread wrapper */ + void *send_wrapper(void *); + #endif ++int call::dynamicId = 0; ++int call::maxDynamicId = 10000+2000*4; // FIXME both param to be in command line !!!! ++int call::startDynamicId = 10000; // FIXME both param to be in command line !!!! ++int call::stepDynamicId = 4; // FIXME both param to be in command line !!!! + + /************** Call map and management routines **************/ + static unsigned int next_number = 1; +@@ -142,13 +151,13 @@ uint32_t get_remote_ip_media(char *msg) + * Look for "c=IN IP6 " pattern in the message and extract the following value + * which should be IPv6 address + */ +-uint8_t get_remote_ipv6_media(char *msg, struct in6_addr addr) ++uint8_t get_remote_ipv6_media(char *msg, struct in6_addr *addr) + { + char pattern[] = "c=IN IP6 "; + char *begin, *end; + char ip[128]; + +- memset(&addr, 0, sizeof(addr)); ++ memset(addr, 0, sizeof(*addr)); + memset(ip, 0, 128); + + begin = strstr(msg, pattern); +@@ -161,7 +170,7 @@ uint8_t get_remote_ipv6_media(char *msg, struct in6_addr addr) + if (!end) + return 0; + strncpy(ip, begin, end - begin); +- if (!inet_pton(AF_INET6, ip, &addr)) { ++ if (!inet_pton(AF_INET6, ip, addr)) { + return 0; + } + return 1; +@@ -177,7 +186,7 @@ uint16_t get_remote_port_media(char *msg, int pattype) + { + char *pattern; + char *begin, *end; +- char number[6]; ++ char number[7]; + + if (pattype == PAT_AUDIO) { + pattern = "m=audio "; +@@ -208,7 +217,7 @@ void call::get_remote_media_addr(char *msg) { + uint16_t video_port, audio_port; + if (media_ip_is_ipv6) { + struct in6_addr ip_media; +- if (get_remote_ipv6_media(msg, ip_media)) { ++ if (get_remote_ipv6_media(msg, &ip_media)) { + audio_port = get_remote_port_media(msg, PAT_AUDIO); + if (audio_port) { + /* We have audio in the SDP: set the to_audio addr */ +@@ -257,27 +266,61 @@ void call::get_remote_media_addr(char *msg) { + + /******* Very simple hash for retransmission detection *******/ + +-unsigned long hash(char * msg) { ++unsigned long call::hash(char * msg) { + unsigned long hash = 0; + int c; + +- while (c = *msg++) +- hash = c + (hash << 6) + (hash << 16) - hash; ++ if (rtcheck == RTCHECK_FULL) { ++ while ((c = *msg++)) ++ hash = c + (hash << 6) + (hash << 16) - hash; ++ } else if (rtcheck == RTCHECK_LOOSE) { ++ /* Based on section 11.5 (bullet 2) of RFC2543 we only take into account ++ * the To, From, Call-ID, and CSeq values. */ ++ char *hdr = get_header_content(msg,"To:"); ++ while ((c = *hdr++)) ++ hash = c + (hash << 6) + (hash << 16) - hash; ++ hdr = get_header_content(msg,"From:"); ++ while ((c = *hdr++)) ++ hash = c + (hash << 6) + (hash << 16) - hash; ++ hdr = get_header_content(msg,"Call-ID:"); ++ while ((c = *hdr++)) ++ hash = c + (hash << 6) + (hash << 16) - hash; ++ hdr = get_header_content(msg,"CSeq:"); ++ while ((c = *hdr++)) ++ hash = c + (hash << 6) + (hash << 16) - hash; ++ /* For responses, we should also consider the code and body (if any), ++ * because they are not nearly as well defined as the request retransmission. */ ++ if (!strncmp(msg, "SIP/2.0", strlen("SIP/2.0"))) { ++ /* Add the first line into the hash. */ ++ hdr = msg + strlen("SIP/2.0"); ++ while ((c = *hdr++) && (c != '\r')) ++ hash = c + (hash << 6) + (hash << 16) - hash; ++ /* Add the body (if any) into the hash. */ ++ hdr = strstr(msg, "\r\n\r\n"); ++ if (hdr) { ++ hdr += strlen("\r\n\r\n"); ++ while ((c = *hdr++)) ++ hash = c + (hash << 6) + (hash << 16) - hash; ++ } ++ } ++ } else { ++ ERROR("Internal error: Invalid rtcheck %d\n", rtcheck); ++ } + + return hash; + } + + /******************* Call class implementation ****************/ + call::call(char *p_id, bool use_ipv6, int userId, struct sockaddr_storage *dest) : listener(p_id, true) { +- init(main_scenario, NULL, dest, p_id, userId, use_ipv6, false); ++ init(main_scenario, NULL, dest, p_id, userId, use_ipv6, false, false); + } + + call::call(char *p_id, struct sipp_socket *socket, struct sockaddr_storage *dest) : listener(p_id, true) { +- init(main_scenario, socket, dest, p_id, 0 /* No User. */, socket->ss_ipv6, false /* Not Auto. */); ++ init(main_scenario, socket, dest, p_id, 0 /* No User. */, socket->ss_ipv6, false /* Not Auto. */, false); + } + +-call::call(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic) : listener(p_id, true) { +- init(call_scenario, socket, dest, p_id, userId, ipv6, isAutomatic); ++call::call(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitialization) : listener(p_id, true) { ++ init(call_scenario, socket, dest, p_id, userId, ipv6, isAutomatic, isInitialization); + } + + call *call::add_call(int userId, bool ipv6, struct sockaddr_storage *dest) +@@ -312,17 +355,22 @@ call *call::add_call(int userId, bool ipv6, struct sockaddr_storage *dest) + } + call_id[count] = 0; + +- return new call(main_scenario, NULL, dest, call_id, userId, ipv6, false /* Not Auto. */); ++ return new call(main_scenario, NULL, dest, call_id, userId, ipv6, false /* Not Auto. */, false); + } + + +-void call::init(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic) ++void call::init(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitCall) + { + this->call_scenario = call_scenario; + zombie = false; ++ ++ debugBuffer = NULL; ++ debugLength = 0; ++ + msg_index = 0; + last_send_index = 0; + last_send_msg = NULL; ++ last_send_len = 0; + + last_recv_hash = 0; + last_recv_index = -1; +@@ -412,16 +460,24 @@ void call::init(scenario * call_scenario, struct sipp_socket *socket, struct soc + userVars->putTable(); + } + +- if (call_scenario->maxTxnUsed > 0) { +- txnID = (char **)malloc(sizeof(char *) * call_scenario->maxTxnUsed); +- memset(txnID, 0, sizeof(char *) * call_scenario->maxTxnUsed); ++ if (call_scenario->transactions.size() > 0) { ++ transactions = (struct txnInstanceInfo *)malloc(sizeof(txnInstanceInfo) * call_scenario->transactions.size()); ++ memset(transactions, 0, sizeof(struct txnInstanceInfo) * call_scenario->transactions.size()); + } else { +- txnID = NULL; ++ transactions = NULL; + } + + // If not updated by a message we use the start time + // information to compute rtd information +- for (i = 0; i < MAX_RTD_INFO_LENGTH; i++) { ++ start_time_rtd = (unsigned long long *)malloc(sizeof(unsigned long long) * call_scenario->stats->nRtds()); ++ if (!start_time_rtd) { ++ ERROR("Could not allocate RTD times!"); ++ } ++ rtd_done = (bool *)malloc(sizeof(bool) * call_scenario->stats->nRtds()); ++ if (!start_time_rtd) { ++ ERROR("Could not allocate RTD done!"); ++ } ++ for (i = 0; i < call_scenario->stats->nRtds(); i++) { + start_time_rtd[i] = getmicroseconds(); + rtd_done[i] = false; + } +@@ -445,6 +501,7 @@ void call::init(scenario * call_scenario, struct sipp_socket *socket, struct soc + } else { + m_lineNumber = NULL; + } ++ this->initCall = isInitCall; + + #ifdef PCAPPLAY + memset(&(play_args_a.to), 0, sizeof(struct sockaddr_storage)); +@@ -483,17 +540,46 @@ void call::init(scenario * call_scenario, struct sipp_socket *socket, struct soc + } + } + ++ callDebug("Starting call %s\n", id); ++ + setRunning(); + } + ++int call::_callDebug(char *fmt, ...) { ++ va_list ap; ++ ++ if (!useCallDebugf) { ++ return 0; ++ } ++ ++ /* First we figure out how much to allocate. */ ++ va_start(ap, fmt); ++ int ret = vsnprintf(NULL, 0, fmt, ap); ++ va_end(ap); ++ ++ debugBuffer = (char *)realloc(debugBuffer, debugLength + ret + TIME_LENGTH + 2); ++ if (!debugBuffer) { ++ ERROR("Could not allocate buffer (%d bytes) for callDebug file!", debugLength + ret + TIME_LENGTH + 2); ++ } ++ ++ struct timeval now; ++ gettimeofday(&now, NULL); ++ debugLength += snprintf(debugBuffer + debugLength, TIME_LENGTH + 2, "%s ", CStat::formatTime(&now)); ++ ++ va_start(ap, fmt); ++ debugLength += vsnprintf(debugBuffer + debugLength, ret + 1, fmt, ap); ++ va_end(ap); ++ ++ return ret; ++} ++ + call::~call() + { + computeStat(CStat::E_ADD_CALL_DURATION, clock_tick - start_time); + + if(comp_state) { comp_free(&comp_state); } + +- +- if (call_remote_socket) { ++ if (call_remote_socket && (call_remote_socket != main_remote_socket)) { + sipp_close_socket(call_remote_socket); + } + +@@ -505,18 +591,14 @@ call::~call() + delete m_lineNumber; + } + if (userId) { +- if (call_scenario->stats->GetStat(CStat::CPT_C_CurrentCall) >= open_calls_allowed) { +- retiredUsers.push_front(userId); +- } else { +- freeUsers.push_front(userId); +- } ++ opentask::freeUser(userId); + } + +- if (txnID) { +- for (int i = 0; i < call_scenario->maxTxnUsed; i++) { +- free(txnID[i]); ++ if (transactions) { ++ for (unsigned int i = 0; i < call_scenario->transactions.size(); i++) { ++ free(transactions[i].txnID); + } +- free(txnID); ++ free(transactions); + } + + if(last_recv_msg) { free(last_recv_msg); } +@@ -541,17 +623,38 @@ call::~call() + if (use_tdmmap) { + tdm_map[tdm_map_number] = false; + } ++ ++# ifdef PCAPPLAY ++ if (media_thread != 0) { ++ pthread_cancel(media_thread); ++ pthread_join(media_thread, NULL); ++ } ++#endif ++ ++ ++ free(start_time_rtd); ++ free(rtd_done); ++ free(debugBuffer); + } + + void call::computeStat (CStat::E_Action P_action) { ++ if (initCall) { ++ return; ++ } + call_scenario->stats->computeStat(P_action); + } + + void call::computeStat (CStat::E_Action P_action, unsigned long P_value) { ++ if (initCall) { ++ return; ++ } + call_scenario->stats->computeStat(P_action, P_value); + } + + void call::computeStat (CStat::E_Action P_action, unsigned long P_value, int which) { ++ if (initCall) { ++ return; ++ } + call_scenario->stats->computeStat(P_action, P_value, which); + } + +@@ -560,16 +663,16 @@ void call::dump() { + char s[MAX_HEADER_LEN]; + sprintf(s, "%s: State %d", id, msg_index); + if (next_retrans) { +- sprintf(s, "%s (next retrans %ld)", s, next_retrans); ++ sprintf(s, "%s (next retrans %u)", s, next_retrans); + } + if (paused_until) { +- sprintf(s, "%s (paused until %ld)", s, paused_until); ++ sprintf(s, "%s (paused until %u)", s, paused_until); + } + if (recv_timeout) { +- sprintf(s, "%s (recv timeout %ld)", s, recv_timeout); ++ sprintf(s, "%s (recv timeout %u)", s, recv_timeout); + } + if (send_timeout) { +- sprintf(s, "%s (send timeout %ld)", s, send_timeout); ++ sprintf(s, "%s (send timeout %u)", s, send_timeout); + } + WARNING("%s", s); + } +@@ -584,13 +687,13 @@ bool call::connect_socket_if_needed() + if(transport == T_UDP) { + struct sockaddr_storage saddr; + +- if(toolMode != MODE_CLIENT) ++ if(sendMode != MODE_CLIENT) + return true; + + char peripaddr[256]; + if (!peripsocket) { + if ((associate_socket(new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) { +- ERROR_NO("Unable to get a UDP socket"); ++ ERROR_NO("Unable to get a UDP socket (1)"); + } + } else { + char *tmp = peripaddr; +@@ -600,7 +703,7 @@ bool call::connect_socket_if_needed() + if (i == map_perip_fd.end()) { + // Socket does not exist + if ((associate_socket(new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) { +- ERROR_NO("Unable to get a UDP socket"); ++ ERROR_NO("Unable to get a UDP socket (2)"); + } else { + /* Ensure that it stays persistent, because it is recorded in the map. */ + call_socket->ss_count++; +@@ -729,10 +832,12 @@ bool call::lost(int index) + return (((double)rand() / (double)RAND_MAX) < (percent / 100.0)); + } + +-int call::send_raw(char * msg, int index) ++int call::send_raw(char * msg, int index, int len) + { + struct sipp_socket *sock; + int rc; ++ ++ callDebug("Sending %s message for call %s (index %d, hash %u):\n%s\n\n", TRANSPORT_TO_STRING(transport), id, index, hash(msg), msg); + + if (useShortMessagef == 1) { + struct timeval currentTime; +@@ -744,6 +849,7 @@ int call::send_raw(char * msg, int index) + + if((index!=-1) && (lost(index))) { + TRACE_MSG("%s message voluntary lost (while sending).", TRANSPORT_TO_STRING(transport)); ++ callDebug("%s message voluntary lost (while sending) (index %d, hash %u).\n", TRANSPORT_TO_STRING(transport), index, hash(msg)); + + if(comp_state) { comp_free(&comp_state); } + call_scenario->messages[index] -> nb_lost++; +@@ -752,31 +858,48 @@ int call::send_raw(char * msg, int index) + + sock = call_socket; + +- if ((use_remote_sending_addr) && (toolMode == MODE_SERVER)) { ++ if ((use_remote_sending_addr) && (sendMode == MODE_SERVER)) { + if (!call_remote_socket) { +- struct sockaddr_storage *L_dest = &remote_sending_sockaddr; ++ if (multisocket || !main_remote_socket) { ++ struct sockaddr_storage *L_dest = &remote_sending_sockaddr; + +- if((call_remote_socket= new_sipp_socket(use_ipv6, transport)) == NULL) { +- ERROR_NO("Unable to get a socket for rsa option"); +- } ++ if((call_remote_socket= new_sipp_socket(use_ipv6, transport)) == NULL) { ++ ERROR_NO("Unable to get a socket for rsa option"); ++ } + +- sipp_customize_socket(call_remote_socket); ++ sipp_customize_socket(call_remote_socket); + +- if(transport != T_UDP) { +- if (sipp_connect_socket(call_remote_socket, L_dest)) { +- if(errno == EINVAL){ +- /* This occurs sometime on HPUX but is not a true INVAL */ +- ERROR("Unable to connect a %s socket for rsa option, remote peer error", TRANSPORT_TO_STRING(transport)); +- } else { +- ERROR_NO("Unable to connect a socket for rsa option"); ++ if(transport != T_UDP) { ++ if (sipp_connect_socket(call_remote_socket, L_dest)) { ++ if(errno == EINVAL){ ++ /* This occurs sometime on HPUX but is not a true INVAL */ ++ ERROR("Unable to connect a %s socket for rsa option, remote peer error", TRANSPORT_TO_STRING(transport)); ++ } else { ++ ERROR_NO("Unable to connect a socket for rsa option"); ++ } + } + } ++ if (!multisocket) { ++ main_remote_socket = call_remote_socket; ++ } ++ } ++ ++ if (!multisocket) { ++ call_remote_socket = main_remote_socket; ++ main_remote_socket->ss_count++; + } + } + sock=call_remote_socket ; + } + +- rc = write_socket(sock, msg, strlen(msg), WS_BUFFER, &call_peer); ++ // If the length hasn't been explicitly specified, treat the message as a string ++ if (len==0) { ++ len = strlen(msg); ++ } ++ ++ assert(sock); ++ ++ rc = write_socket(sock, msg, len, WS_BUFFER, &call_peer); + if(rc == -1 && errno == EWOULDBLOCK) { + return -1; + } +@@ -792,10 +915,10 @@ int call::send_raw(char * msg, int index) + + /* This method is used to send messages that are not */ + /* part of the XML scenario */ +-void call::sendBuffer(char * msg) ++void call::sendBuffer(char * msg, int len) + { + /* call send_raw but with a special scenario index */ +- if (send_raw(msg, -1) < 0) { ++ if (send_raw(msg, -1, len) < 0) { + if (sendbuffer_warn) { + ERROR_NO("Error sending raw message"); + } else { +@@ -905,7 +1028,7 @@ char * call::get_header(char* message, char * name, bool content) + src = message; + dest = last_header; + +- while(src = strcasestr2(src, src_tmp)) { ++ while((src = strcasestr2(src, src_tmp))) { + if (content || !first_time) { + /* just want the header's content */ + src += strlen(name) + 1; +@@ -1078,14 +1201,15 @@ char * call::get_last_request_uri () + + } + +-char * call::send_scene(int index, int *send_status) ++char * call::send_scene(int index, int *send_status, int *len) + { +- static char msg_buffer[SIPP_MAX_MSG_SIZE]; +- + #define MAX_MSG_NAME_SIZE 30 + static char msg_name[MAX_MSG_NAME_SIZE]; + char *L_ptr1 ; + char *L_ptr2 ; ++ int uselen = 0; ++ ++ assert(send_status); + + /* Socket port must be known before string substitution */ + if (!connect_socket_if_needed()) { +@@ -1102,13 +1226,16 @@ char * call::send_scene(int index, int *send_status) + + assert(call_scenario->messages[index]->send_scheme); + ++ if (!len) { ++ len = &uselen; ++ } ++ + char * dest; +- dest = createSendingMessage(call_scenario->messages[index] -> send_scheme, index); +- strcpy(msg_buffer, dest); ++ dest = createSendingMessage(call_scenario->messages[index] -> send_scheme, index, len); + + if (dest) { + L_ptr1=msg_name ; +- L_ptr2=msg_buffer ; ++ L_ptr2=dest ; + while ((*L_ptr2 != ' ') && (*L_ptr2 != '\n') && (*L_ptr2 != '\t')) { + *L_ptr1 = *L_ptr2; + L_ptr1 ++; +@@ -1122,27 +1249,23 @@ char * call::send_scene(int index, int *send_status) + ack_is_pending = false ; + } + +- if(send_status) { +- *send_status = send_raw(msg_buffer, index); +- } else { +- send_raw(msg_buffer, index); +- } ++ *send_status = send_raw(dest, index, *len); + +- return msg_buffer; ++ return dest; + } + +-void call::do_bookkeeping(int index) { ++void call::do_bookkeeping(message *curmsg) { + /* If this message increments a counter, do it now. */ +- if(int counter = call_scenario->messages[index] -> counter) { ++ if(int counter = curmsg -> counter) { + computeStat(CStat::E_ADD_GENERIC_COUNTER, 1, counter - 1); + } + + /* If this message can be used to compute RTD, do it now */ +- if(int rtd = call_scenario->messages[index] -> start_rtd) { ++ if(int rtd = curmsg -> start_rtd) { + start_time_rtd[rtd - 1] = getmicroseconds(); + } + +- if(int rtd = call_scenario->messages[index] -> stop_rtd) { ++ if(int rtd = curmsg -> stop_rtd) { + if (!rtd_done[rtd - 1]) { + unsigned long long start = start_time_rtd[rtd - 1]; + unsigned long long end = getmicroseconds(); +@@ -1154,7 +1277,7 @@ void call::do_bookkeeping(int index) { + computeStat(CStat::E_ADD_RESPONSE_TIME_DURATION, + (end - start) / 1000, rtd - 1); + +- if (!call_scenario->messages[index] -> repeat_rtd) { ++ if (!curmsg -> repeat_rtd) { + rtd_done[rtd - 1] = true; + } + } +@@ -1165,7 +1288,7 @@ void call::tcpClose() { + terminate(CStat::E_FAILED_TCP_CLOSED); + } + +-bool call::terminate(CStat::E_Action reason) { ++void call::terminate(CStat::E_Action reason) { + char reason_str[100]; + + stopListening(); +@@ -1176,19 +1299,35 @@ bool call::terminate(CStat::E_Action reason) { + case call::E_AR_REGEXP_DOESNT_MATCH: + computeStat(CStat::E_CALL_FAILED); + computeStat(CStat::E_FAILED_REGEXP_DOESNT_MATCH); +- if (deadcall_wait) { ++ if (deadcall_wait && !initCall) { + sprintf(reason_str, "regexp match failure at index %d", msg_index); + new deadcall(id, reason_str); + } + break; ++ case call::E_AR_REGEXP_SHOULDNT_MATCH: ++ computeStat(CStat::E_CALL_FAILED); ++ computeStat(CStat::E_FAILED_REGEXP_SHOULDNT_MATCH); ++ if (deadcall_wait && !initCall) { ++ sprintf(reason_str, "regexp matched, but shouldn't at index %d", msg_index); ++ new deadcall(id, reason_str); ++ } ++ break; + case call::E_AR_HDR_NOT_FOUND: + computeStat(CStat::E_CALL_FAILED); + computeStat(CStat::E_FAILED_REGEXP_HDR_NOT_FOUND); +- if (deadcall_wait) { ++ if (deadcall_wait && !initCall) { + sprintf(reason_str, "regexp header not found at index %d", msg_index); + new deadcall(id, reason_str); + } + break; ++ case E_AR_CONNECT_FAILED: ++ computeStat(CStat::E_CALL_FAILED); ++ computeStat(CStat::E_FAILED_TCP_CONNECT); ++ if (deadcall_wait && !initCall) { ++ sprintf(reason_str, "connection failed %d", msg_index); ++ new deadcall(id, reason_str); ++ } ++ break; + case call::E_AR_NO_ERROR: + case call::E_AR_STOP_CALL: + /* Do nothing. */ +@@ -1197,7 +1336,7 @@ bool call::terminate(CStat::E_Action reason) { + } else { + if (reason == CStat::E_CALL_SUCCESSFULLY_ENDED || timewait) { + computeStat(CStat::E_CALL_SUCCESSFULLY_ENDED); +- if (deadcall_wait) { ++ if (deadcall_wait && !initCall) { + new deadcall(id, "successful"); + } + } else { +@@ -1205,7 +1344,7 @@ bool call::terminate(CStat::E_Action reason) { + if (reason != CStat::E_NO_ACTION) { + computeStat(reason); + } +- if (deadcall_wait) { ++ if (deadcall_wait && !initCall) { + sprintf(reason_str, "failed at index %d", msg_index); + new deadcall(id, reason_str); + } +@@ -1216,24 +1355,29 @@ bool call::terminate(CStat::E_Action reason) { + + bool call::next() + { +- int test = call_scenario->messages[msg_index]->test; ++ msgvec * msgs = &call_scenario->messages; ++ if (initCall) { ++ msgs = &call_scenario->initmessages; ++ } ++ ++ int test = (*msgs)[msg_index]->test; + /* What is the next message index? */ + /* Default without branching: use the next message */ + int new_msg_index = msg_index+1; + /* If branch needed, overwrite this default */ +- if ( (call_scenario->messages[msg_index]->next >= 0) && ++ if ( ((*msgs)[msg_index]->next >= 0) && + ((test == -1) || M_callVariableTable->getVar(test)->isSet()) + ) { + /* Branching possible, check the probability */ +- int chance = call_scenario->messages[msg_index]->chance; ++ int chance = (*msgs)[msg_index]->chance; + if ((chance <= 0) || (rand() > chance )) { + /* Branch == overwrite with the 'next' attribute value */ +- new_msg_index = call_scenario->messages[msg_index]->next; ++ new_msg_index = (*msgs)[msg_index]->next; + } + } + msg_index=new_msg_index; + recv_timeout = 0; +- if(msg_index >= call_scenario->length) { ++ if(msg_index >= (int)((*msgs).size())) { + terminate(CStat::E_CALL_SUCCESSFULLY_ENDED); + return false; + } +@@ -1241,100 +1385,8 @@ bool call::next() + return run(); + } + +-bool call::run() +-{ +- bool bInviteTransaction = false; +- int actionResult = 0; +- +- assert(running); +- +- if (zombie) { +- delete this; +- return false; +- } +- +- clock_tick = getmilliseconds(); +- +- if(msg_index >= call_scenario->length) { +- ERROR("Scenario overrun for call %s (%p) (index = %d)\n", +- id, this, msg_index); +- } +- +- message *curmsg = call_scenario->messages[msg_index]; +- +- /* Manages retransmissions or delete if max retrans reached */ +- if(next_retrans && (next_retrans < clock_tick)) { +- nb_retrans++; +- +- if ( (0 == strncmp (last_send_msg, "INVITE", 6)) ) +- { +- bInviteTransaction = true; +- } +- +- if((nb_retrans > (bInviteTransaction ? max_invite_retrans : max_non_invite_retrans)) || +- (nb_retrans > max_udp_retrans)) { +- call_scenario->messages[last_send_index] -> nb_timeout ++; +- if (call_scenario->messages[last_send_index]->on_timeout >= 0) { // action on timeout +- WARNING("Call-Id: %s, timeout on max UDP retrans for message %d, jumping to label %d ", +- id, msg_index, call_scenario->messages[last_send_index]->on_timeout); +- msg_index = call_scenario->messages[last_send_index]->on_timeout; +- next_retrans = 0; +- recv_timeout = 0; +- if (msg_index < call_scenario->length) { +- return true; +- } +- +- // here if asked to go to the last label delete the call +- computeStat(CStat::E_CALL_FAILED); +- computeStat(CStat::E_FAILED_MAX_UDP_RETRANS); +- if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { +- // Abort the call by sending proper SIP message +- return(abortCall()); +- } else { +- // Just delete existing call +- delete this; +- return false; +- } +- } +- computeStat(CStat::E_CALL_FAILED); +- computeStat(CStat::E_FAILED_MAX_UDP_RETRANS); +- if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { +- // Abort the call by sending proper SIP message +- WARNING("Aborting call on UDP retransmission timeout for Call-ID '%s'", id); +- return(abortCall()); +- } else { +- // Just delete existing call +- delete this; +- return false; +- } +- } else { +- nb_last_delay *= 2; +- if (DEFAULT_T2_TIMER_VALUE < nb_last_delay) +- { +- if (!bInviteTransaction) +- { +- nb_last_delay = DEFAULT_T2_TIMER_VALUE; +- } +- } +- if(send_raw(last_send_msg, last_send_index) < -1) { +- return false; +- } +- call_scenario->messages[last_send_index] -> nb_sent_retrans++; +- computeStat(CStat::E_RETRANSMISSION); +- next_retrans = clock_tick + nb_last_delay; +- } +- } +- +- if(paused_until) { +- /* Process a pending pause instruction until delay expiration */ +- if(paused_until > clock_tick) { +- setPaused(); +- return true; +- } +- /* Our pause is over. */ +- paused_until = 0; +- return next(); +- } else if(curmsg -> pause_distribution || curmsg->pause_variable != -1) { ++bool call::executeMessage(message *curmsg) { ++ if(curmsg -> pause_distribution || curmsg->pause_variable != -1) { + unsigned int pause; + if (curmsg->pause_distribution) { + pause = (int)(curmsg -> pause_distribution -> sample()); +@@ -1357,9 +1409,11 @@ bool call::run() + + /* Increment the number of sessions in pause state */ + curmsg->sessions++; +- do_bookkeeping(msg_index); +- actionResult = executeAction(NULL, msg_index); +- return run(); /* In case delay is 0 */ ++ do_bookkeeping(curmsg); ++ executeAction(NULL, curmsg); ++ callDebug("Pausing call until %d (is now %d).\n", paused_until, clock_tick); ++ setPaused(); ++ return true; + } + else if(curmsg -> M_type == MSG_TYPE_SENDCMD) { + int send_status; +@@ -1368,7 +1422,7 @@ bool call::run() + return true; + } + +- send_status = sendCmdMessage(msg_index); ++ send_status = sendCmdMessage(curmsg); + + if(send_status != 0) { /* Send error */ + return false; /* call deleted */ +@@ -1376,18 +1430,20 @@ bool call::run() + curmsg -> M_nbCmdSent++; + next_retrans = 0; + +- do_bookkeeping(msg_index); +- actionResult = executeAction(NULL, msg_index); ++ do_bookkeeping(curmsg); ++ executeAction(NULL, curmsg); + return(next()); + } + else if(curmsg -> M_type == MSG_TYPE_NOP) { +- do_bookkeeping(msg_index); +- actionResult = executeAction(NULL, msg_index); ++ callDebug("Executing NOP at index %d.\n", curmsg->index); ++ do_bookkeeping(curmsg); ++ executeAction(NULL, curmsg); + return(next()); + } + + else if(curmsg -> send_scheme) { + char * msg_snd; ++ int msgLen; + int send_status; + + /* Do not send a new message until the previous one which had +@@ -1399,7 +1455,7 @@ bool call::run() + } + + /* Handle counters and RTDs for this message. */ +- do_bookkeeping(msg_index); ++ do_bookkeeping(curmsg); + + /* decide whether to increment cseq or not + * basically increment for anything except response, ACK or CANCEL +@@ -1415,19 +1471,19 @@ bool call::run() + incr_cseq = 1; + } + +- msg_snd = send_scene(msg_index, &send_status); ++ msg_snd = send_scene(msg_index, &send_status, &msgLen); + if(send_status == -1 && errno == EWOULDBLOCK) { + if (incr_cseq) --cseq; + /* Have we set the timeout yet? */ + if (send_timeout) { + /* If we have actually timed out. */ + if (clock_tick > send_timeout) { +- WARNING("Call-Id: %s, send timeout on message %d: aborting call", +- id, msg_index); ++ WARNING("Call-Id: %s, send timeout on message %s:%d: aborting call", ++ id, curmsg->desc, curmsg->index); + computeStat(CStat::E_CALL_FAILED); + computeStat(CStat::E_FAILED_TIMEOUT_ON_SEND); + if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { +- return (abortCall()); ++ return (abortCall(true)); + } else { + delete this; + return false; +@@ -1442,21 +1498,25 @@ bool call::run() + } + return true; /* No step, nothing done, retry later */ + } else if(send_status < 0) { /* Send error */ +- /* The timeout will not be sent, so the timeout is no longer needed. */ +- send_timeout = 0; +- /* The call was already deleted by connect_socket_if_needed or send_raw. */ ++ /* The call was already deleted by connect_socket_if_needed or send_raw, ++ * so we should no longer access members. */ + return false; + } + /* We have sent the message, so the timeout is no longer needed. */ + send_timeout = 0; + +- last_send_index = msg_index; +- last_send_msg = (char *) realloc(last_send_msg, strlen(msg_snd) + 1); +- strcpy(last_send_msg, msg_snd); ++ last_send_index = curmsg->index; ++ last_send_len = msgLen; ++ last_send_msg = (char *) realloc(last_send_msg, msgLen+1); ++ memcpy(last_send_msg, msg_snd, msgLen); ++ last_send_msg[msgLen] = '\0'; + + if (curmsg->start_txn) { +- txnID[curmsg->start_txn - 1] = (char *)realloc(txnID[curmsg->start_txn - 1], MAX_HEADER_LEN); +- extract_transaction(txnID[curmsg->start_txn - 1], last_send_msg); ++ transactions[curmsg->start_txn - 1].txnID = (char *)realloc(transactions[curmsg->start_txn - 1].txnID, MAX_HEADER_LEN); ++ extract_transaction(transactions[curmsg->start_txn - 1].txnID, last_send_msg); ++ } ++ if (curmsg->ack_txn) { ++ transactions[curmsg->ack_txn - 1].ackIndex = curmsg->index; + } + + if(last_recv_index >= 0) { +@@ -1464,7 +1524,9 @@ bool call::run() + * chance that we will be asked to retransmit this message */ + recv_retrans_hash = last_recv_hash; + recv_retrans_recv_index = last_recv_index; +- recv_retrans_send_index = msg_index; ++ recv_retrans_send_index = curmsg->index; ++ ++ callDebug("Set Retransmission Hash: %u (recv index %d, send index %d)\n", recv_retrans_hash, recv_retrans_recv_index, recv_retrans_send_index); + + /* Prevent from detecting the cause relation between send and recv + * in the next valid send */ +@@ -1482,7 +1544,7 @@ bool call::run() + next_retrans = 0; + } + +- actionResult = executeAction(msg_snd, msg_index); ++ executeAction(msg_snd, curmsg); + + /* Update scenario statistics */ + curmsg -> nb_sent++; +@@ -1498,7 +1560,7 @@ bool call::run() + free(msg); + return ret; + } else if (recv_timeout) { +- if(recv_timeout > clock_tick || recv_timeout > getmilliseconds()) { ++ if(recv_timeout > getmilliseconds()) { + setPaused(); + return true; + } +@@ -1506,27 +1568,29 @@ bool call::run() + curmsg->nb_timeout++; + if (curmsg->on_timeout < 0) { + // if you set a timeout but not a label, the call is aborted +- WARNING("Call-Id: %s, receive timeout on message %d without label to jump to (ontimeout attribute): aborting call", +- id, msg_index); ++ WARNING("Call-Id: %s, receive timeout on message %s:%d without label to jump to (ontimeout attribute): aborting call", ++ id, curmsg->desc, curmsg->index); + computeStat(CStat::E_CALL_FAILED); + computeStat(CStat::E_FAILED_TIMEOUT_ON_RECV); + if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { +- return (abortCall()); ++ return (abortCall(true)); + } else { + delete this; + return false; + } + } +- WARNING("Call-Id: %s, receive timeout on message %d, jumping to label %d", +- id, msg_index, curmsg->on_timeout); ++ WARNING("Call-Id: %s, receive timeout on message %s:%d, jumping to label %d", ++ id, curmsg->desc, curmsg->index, curmsg->on_timeout); ++ /* FIXME: We should do something like set index here, but it probably ++ * does not matter too much as only nops are allowed in the init stanza. */ + msg_index = curmsg->on_timeout; + recv_timeout = 0; +- if (msg_index < call_scenario->length) return true; ++ if (msg_index < (int)call_scenario->messages.size()) return true; + // special case - the label points to the end - finish the call + computeStat(CStat::E_CALL_FAILED); + computeStat(CStat::E_FAILED_TIMEOUT_ON_RECV); + if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { +- return (abortCall()); ++ return (abortCall(true)); + } else { + delete this; + return false; +@@ -1543,10 +1607,133 @@ bool call::run() + /* We are going to wait forever. */ + setPaused(); + } ++ } else { ++ WARNING("Unknown message type at %s:%d: %d", curmsg->desc, curmsg->index, curmsg->M_type); + } + return true; + } + ++bool call::run() ++{ ++ bool bInviteTransaction = false; ++ ++ assert(running); ++ ++ if (zombie) { ++ delete this; ++ return false; ++ } ++ ++ getmilliseconds(); ++ ++ message *curmsg; ++ if (initCall) { ++ if(msg_index >= (int)call_scenario->initmessages.size()) { ++ ERROR("Scenario initialization overrun for call %s (%p) (index = %d)\n", id, this, msg_index); ++ } ++ curmsg = call_scenario->initmessages[msg_index]; ++ } else { ++ if(msg_index >= (int)call_scenario->messages.size()) { ++ ERROR("Scenario overrun for call %s (%p) (index = %d)\n", id, this, msg_index); ++ } ++ curmsg = call_scenario->messages[msg_index]; ++ } ++ ++ callDebug("Processing message %d of type %d for call %s at %u.\n", msg_index, curmsg->M_type, id, clock_tick); ++ ++ if (curmsg->condexec != -1) { ++ bool exec = M_callVariableTable->getVar(curmsg->condexec)->isSet(); ++ if (curmsg->condexec_inverse) { ++ exec = !exec; ++ } ++ if (!exec) { ++ callDebug("Conditional variable %s %s set, so skipping message %d.\n", call_scenario->allocVars->getName(curmsg->condexec), curmsg->condexec_inverse ? "" : "not", msg_index); ++ return next(); ++ } ++ } ++ ++ /* Manages retransmissions or delete if max retrans reached */ ++ if(next_retrans && (next_retrans < clock_tick)) { ++ nb_retrans++; ++ ++ if ( (0 == strncmp (last_send_msg, "INVITE", 6)) ) ++ { ++ bInviteTransaction = true; ++ } ++ ++ int rtAllowed = min(bInviteTransaction ? max_invite_retrans : max_non_invite_retrans, max_udp_retrans); ++ ++ callDebug("Retransmisison required (%d retransmissions, max %d)\n", nb_retrans, rtAllowed); ++ ++ if(nb_retrans > rtAllowed) { ++ call_scenario->messages[last_send_index] -> nb_timeout ++; ++ if (call_scenario->messages[last_send_index]->on_timeout >= 0) { // action on timeout ++ WARNING("Call-Id: %s, timeout on max UDP retrans for message %d, jumping to label %d ", ++ id, msg_index, call_scenario->messages[last_send_index]->on_timeout); ++ msg_index = call_scenario->messages[last_send_index]->on_timeout; ++ next_retrans = 0; ++ recv_timeout = 0; ++ if (msg_index < (int)call_scenario->messages.size()) { ++ return true; ++ } ++ ++ // here if asked to go to the last label delete the call ++ computeStat(CStat::E_CALL_FAILED); ++ computeStat(CStat::E_FAILED_MAX_UDP_RETRANS); ++ if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { ++ // Abort the call by sending proper SIP message ++ return(abortCall(true)); ++ } else { ++ // Just delete existing call ++ delete this; ++ return false; ++ } ++ } ++ computeStat(CStat::E_CALL_FAILED); ++ computeStat(CStat::E_FAILED_MAX_UDP_RETRANS); ++ if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { ++ // Abort the call by sending proper SIP message ++ WARNING("Aborting call on UDP retransmission timeout for Call-ID '%s'", id); ++ return(abortCall(true)); ++ } else { ++ // Just delete existing call ++ delete this; ++ return false; ++ } ++ } else { ++ nb_last_delay *= 2; ++ if (DEFAULT_T2_TIMER_VALUE < nb_last_delay) ++ { ++ if (!bInviteTransaction) ++ { ++ nb_last_delay = DEFAULT_T2_TIMER_VALUE; ++ } ++ } ++ if(send_raw(last_send_msg, last_send_index, last_send_len) < -1) { ++ return false; ++ } ++ call_scenario->messages[last_send_index] -> nb_sent_retrans++; ++ computeStat(CStat::E_RETRANSMISSION); ++ next_retrans = clock_tick + nb_last_delay; ++ } ++ } ++ ++ if(paused_until) { ++ /* Process a pending pause instruction until delay expiration */ ++ if(paused_until > clock_tick) { ++ callDebug("Call is paused until %d (now %d).\n", paused_until, clock_tick); ++ setPaused(); ++ callDebug("Running: %d (wake %d).\n", running, wake()); ++ return true; ++ } ++ /* Our pause is over. */ ++ callDebug("Pause complete, waking up.\n"); ++ paused_until = 0; ++ return next(); ++ } ++ return executeMessage(curmsg); ++} ++ + char *default_message_names[] = { + "3pcc_abort", + "ack", +@@ -1695,6 +1882,9 @@ bool call::process_unexpected(char * msg) + TRANSPORT_TO_STRING(transport), + msg); + ++ callDebug("Unexpected %s message received (index %d, hash %u):\n\n%s\n", ++ TRANSPORT_TO_STRING(transport), msg_index, hash(msg), msg); ++ + if (default_behaviors & DEFAULT_BEHAVIOR_ABORTUNEXP) { + // if twin socket call => reset the other part here + if (twinSippSocket && (msg_index > 0)) { +@@ -1708,7 +1898,7 @@ bool call::process_unexpected(char * msg) + computeStat(CStat::E_CALL_FAILED); + computeStat(CStat::E_FAILED_UNEXPECTED_MSG); + if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { +- return (abortCall()); ++ return (abortCall(true)); + } else { + delete this; + return false; +@@ -1721,27 +1911,27 @@ bool call::process_unexpected(char * msg) + + void call::abort() { + WARNING("Aborted call with Call-ID '%s'", id); +- abortCall(); ++ abortCall(false); + } + +-bool call::abortCall() ++bool call::abortCall(bool writeLog) + { + int is_inv; + +- char * src_send = NULL ; + char * src_recv = NULL ; + ++ callDebug("Aborting call %s (index %d).\n", id, msg_index); ++ + if (last_send_msg != NULL) { + is_inv = !strncmp(last_send_msg, "INVITE", 6); + } else { + is_inv = false; + } +- if ((toolMode != MODE_SERVER) && (msg_index > 0)) { ++ if ((creationMode != MODE_SERVER) && (msg_index > 0)) { + if ((call_established == false) && (is_inv)) { + src_recv = last_recv_msg ; + char L_msg_buffer[SIPP_MAX_MSG_SIZE]; + L_msg_buffer[0] = '\0'; +- char * L_param = L_msg_buffer; + + // Answer unexpected errors (4XX, 5XX and beyond) with an ACK + // Contributed by F. Tarek Rogers +@@ -1751,8 +1941,6 @@ bool call::abortCall() + /* Call is not established and the reply is not a 4XX, 5XX */ + /* And we already received a message. */ + if (ack_is_pending == true) { +- char * cseq = NULL; +- + /* If an ACK is expected from the other side, send it + * and send a BYE afterwards */ + ack_is_pending = false; +@@ -1777,17 +1965,21 @@ bool call::abortCall() + * because the earlier check depends on the first message being an INVITE + * (although it could be something like a message message, therefore we + * check that we received a message. */ +- char * src_recv = last_recv_msg ; + char L_msg_buffer[SIPP_MAX_MSG_SIZE]; + L_msg_buffer[0] = '\0'; +- char * L_param = L_msg_buffer; + sendBuffer(createSendingMessage(get_default_message("bye"), -1)); + } + } + ++ if (writeLog && useCallDebugf) { ++ TRACE_CALLDEBUG ("-------------------------------------------------------------------------------\n", id); ++ TRACE_CALLDEBUG ("Call debugging information for call %s:\n", id); ++ TRACE_CALLDEBUG("%s", debugBuffer); ++ } ++ + stopListening(); + deadcall *deadcall_ptr = NULL; +- if (deadcall_wait) { ++ if (deadcall_wait && !initCall) { + char reason[100]; + sprintf(reason, "aborted at index %d", msg_index); + deadcall_ptr = new deadcall(id, reason); +@@ -1806,7 +1998,7 @@ bool call::rejectCall() + } + + +-int call::sendCmdMessage(int index) ++int call::sendCmdMessage(message *curmsg) + { + char * dest; + char delimitor[2]; +@@ -1817,8 +2009,6 @@ int call::sendCmdMessage(int index) + char * peer_dest; + struct sipp_socket **peer_socket; + +- message *curmsg = call_scenario->messages[index]; +- + if(curmsg -> M_sendCmdData) { + // WARNING("---PREPARING_TWIN_CMD---%s---", scenario[index] -> M_sendCmdData); + dest = createSendingMessage(curmsg -> M_sendCmdData, -1); +@@ -1874,12 +2064,12 @@ int call::sendCmdBuffer(char* cmd) + } + + +-char* call::createSendingMessage(SendingMessage *src, int P_index) { ++char* call::createSendingMessage(SendingMessage *src, int P_index, int *msgLen) { + static char msg_buffer[SIPP_MAX_MSG_SIZE+2]; +- return createSendingMessage(src, P_index, msg_buffer, sizeof(msg_buffer)); ++ return createSendingMessage(src, P_index, msg_buffer, sizeof(msg_buffer), msgLen); + } + +-char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buffer, int buf_len) ++char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buffer, int buf_len, int *msgLen) + { + char * length_marker = NULL; + char * auth_marker = NULL; +@@ -1902,7 +2092,9 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf + dest += snprintf(dest, left, "%s", ptr); + supresscrlf = false; + } else { +- dest += snprintf(dest, left, "%s", comp->literal); ++ memcpy(dest, comp->literal, comp->literalLen); ++ dest += comp->literalLen; ++ *dest = '\0'; + } + break; + case E_Message_Remote_IP: +@@ -1919,7 +2111,7 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf + break; + case E_Message_Local_Port: + int port; +- if((transport == T_UDP) && (multisocket) && (toolMode != MODE_SERVER)) { ++ if((transport == T_UDP) && (multisocket) && (sendMode != MODE_SERVER)) { + port = call_port; + } else { + port = local_port; +@@ -2001,6 +2193,12 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf + case E_Message_Call_Number: + dest += snprintf(dest, left, "%u", number); + break; ++ case E_Message_DynamicId: ++ dest += snprintf(dest, left, "%u", call::dynamicId); ++ // increment at each request ++ dynamicId += stepDynamicId; ++ if ( this->dynamicId > maxDynamicId ) { call::dynamicId = call::startDynamicId; } ; ++ break; + case E_Message_Call_ID: + dest += snprintf(dest, left, "%s", id); + break; +@@ -2068,6 +2266,9 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf + case E_Message_UserID: + dest += snprintf(dest, left, "%d", userId); + break; ++ case E_Message_SippVersion: ++ dest += snprintf(dest, left, "%s", SIPP_VERSION); ++ break; + case E_Message_Variable: { + int varId = comp->varId; + CCallVariable *var = M_callVariableTable->getVar(varId); +@@ -2084,6 +2285,9 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf + } else if (var->isBool()) { + dest += sprintf(dest, "false"); + } ++ if (*(dest - 1) == '\n') { ++ supresscrlf = true; ++ } + break; + } + case E_Message_Fill: { +@@ -2103,6 +2307,24 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf + *dest = '\0'; + break; + } ++ case E_Message_File: { ++ char buffer[MAX_HEADER_LEN]; ++ createSendingMessage(comp->comp_param.filename, -2, buffer, sizeof(buffer)); ++ FILE *f = fopen(buffer, "r"); ++ if (!f) { ++ ERROR("Could not open '%s': %s\n", buffer, strerror(errno)); ++ } ++ int ret; ++ while ((ret = fread(dest, 1, left, f)) > 0) { ++ left -= ret; ++ dest += ret; ++ } ++ if (ret < 0) { ++ ERROR("Error reading '%s': %s\n", buffer, strerror(errno)); ++ } ++ fclose(f); ++ break; ++ } + case E_Message_Injection: { + char *orig_dest = dest; + getFieldFromInputFile(comp->comp_param.field_param.filename, comp->comp_param.field_param.field, comp->comp_param.field_param.line, dest); +@@ -2137,6 +2359,10 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf + } + break; + } ++ case E_Message_Custom: { ++ dest += comp->comp_param.fxn(this, comp, dest, left); ++ break; ++ } + case E_Message_Last_Message: + if(last_recv_msg && strlen(last_recv_msg)) { + dest += sprintf(dest, "%s", last_recv_msg); +@@ -2175,8 +2401,12 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf + } + /* Need the body for length and auth-int calculation */ + char *body; ++ char *auth_body = NULL; + if (length_marker || auth_marker) { + body = strstr(msg_buffer, "\r\n\r\n"); ++ if (body) { ++ auth_body += strlen("\r\n\r\n"); ++ } + } + + /* Fix up the length. */ +@@ -2187,7 +2417,7 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf + + if (body && dest - body > 4 && dest - body < 100004) { + char tmp = length_marker[5]; +- sprintf(length_marker, "%5u", dest - body - 4 + len_offset); ++ sprintf(length_marker, "%5u", (unsigned)(dest - body - 4 + len_offset)); + length_marker[5] = tmp; + } else { + // Other cases: Content-Length is 0 +@@ -2195,6 +2425,10 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf + } + } + ++ if (msgLen) { ++ *msgLen = dest - msg_buffer; ++ } ++ + /* + * The authentication substitution must be done outside the above + * loop because auth-int will use the body (which must have already +@@ -2224,8 +2458,8 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf + while(isspace(*tmp) || isdigit(*tmp)) tmp++; + sscanf(tmp,"%s", method); + +- if (!body) { +- body = ""; ++ if (!auth_body) { ++ auth_body = ""; + } + + /* Determine the type of credentials. */ +@@ -2254,7 +2488,7 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf + createSendingMessage(auth_comp->comp_param.auth_param.aka_AMF, -2, my_aka_AMF, sizeof(my_aka_AMF)); + createSendingMessage(auth_comp->comp_param.auth_param.aka_OP, -2, my_aka_OP, sizeof(my_aka_OP)); + +- if (createAuthHeader(my_auth_user, my_auth_pass, method, uri, body, dialog_authentication, ++ if (createAuthHeader(my_auth_user, my_auth_pass, method, uri, auth_body, dialog_authentication, + my_aka_OP, my_aka_AMF, my_aka_K, result + authlen) == 0) { + ERROR("%s", result + authlen); + } +@@ -2264,6 +2498,9 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf + memmove(auth_marker + authlen, auth_marker + auth_marker_len, strlen(auth_marker + auth_marker_len) + 1); + /* Copy our result into the hole. */ + memcpy(auth_marker, result, authlen); ++ if (msgLen) { ++ *msgLen += (authlen - auth_marker_len); ++ } + #endif + } + +@@ -2280,12 +2517,14 @@ bool call::process_twinSippCom(char * msg) + bool found = false; + T_ActionResult actionResult; + ++ callDebug("Processing incoming command for call-ID %s:\n%s\n\n", id, msg); ++ + setRunning(); + + if (checkInternalCmd(msg) == false) { + + for(search_index = msg_index; +- search_index < call_scenario->length; ++ search_index < (int)call_scenario->messages.size(); + search_index++) { + if(call_scenario->messages[search_index] -> M_type != MSG_TYPE_RECVCMD) { + if(call_scenario->messages[search_index] -> optional) { +@@ -2293,6 +2532,7 @@ bool call::process_twinSippCom(char * msg) + } + /* The received message is different from the expected one */ + TRACE_MSG("Unexpected control message received (I was expecting a different type of message):\n%s\n", msg); ++ callDebug("Unexpected control message received (I was expecting a different type of message):\n%s\n\n", msg); + return rejectCall(); + } else { + if(extendedTwinSippMode){ // 3pcc extended mode +@@ -2313,7 +2553,7 @@ bool call::process_twinSippCom(char * msg) + + if (found) { + call_scenario->messages[search_index]->M_nbCmdRecv ++; +- do_bookkeeping(search_index); ++ do_bookkeeping(call_scenario->messages[search_index]); + + // variable treatment + // Remove \r, \n at the end of a received command +@@ -2322,7 +2562,7 @@ bool call::process_twinSippCom(char * msg) + (msg[strlen(msg)-2] == '\r') ) { + msg[strlen(msg)-2] = 0; + } +- actionResult = executeAction(msg, search_index); ++ actionResult = executeAction(msg, call_scenario->messages[search_index]); + + if(actionResult != call::E_AR_NO_ERROR) { + // Store last action result if it is an error +@@ -2330,10 +2570,14 @@ bool call::process_twinSippCom(char * msg) + call::last_action_result = actionResult; + if (actionResult == E_AR_STOP_CALL) { + return rejectCall(); +- } ++ } else if (actionResult == E_AR_CONNECT_FAILED) { ++ terminate(CStat::E_FAILED_TCP_CONNECT); ++ return false; ++ } + } + } else { + TRACE_MSG("Unexpected control message received (no such message found):\n%s\n", msg); ++ callDebug("Unexpected control message received (no such message found):\n%s\n\n", msg); + return rejectCall(); + } + msg_index = search_index; //update the state machine +@@ -2367,7 +2611,7 @@ bool call::checkInternalCmd(char * cmd) + + if (strcmp(L_ptr1, "abort_call") == 0) { + *L_ptr2 = L_backup; +- abortCall(); ++ abortCall(true); + computeStat(CStat::E_CALL_FAILED); + return (true); + } +@@ -2409,10 +2653,10 @@ bool call::check_peer_src(char * msg, int search_index) + void call::extract_cseq_method (char* method, char* msg) + { + char* cseq ; +- if (cseq = strstr (msg, "CSeq")) ++ if ((cseq = strstr (msg, "CSeq"))) + { + char * value ; +- if ( value = strchr (cseq, ':')) ++ if (( value = strchr (cseq, ':'))) + { + value++; + while ( isspace(*value)) value++; // ignore any white spaces after the : +@@ -2430,7 +2674,6 @@ void call::extract_cseq_method (char* method, char* msg) + + void call::extract_transaction (char* txn, char* msg) + { +- char *otxn = txn; + char *via = get_header_content(msg, "via:"); + if (!via) { + txn[0] = '\0'; +@@ -2599,7 +2842,6 @@ void call::computeRouteSetAndRemoteTargetUri (char* rr, char* contact, bool bReq + + bool call::matches_scenario(unsigned int index, int reply_code, char * request, char * responsecseqmethod, char *txn) + { +- int result; + message *curmsg = call_scenario->messages[index]; + + if ((curmsg -> recv_request)) { +@@ -2618,7 +2860,7 @@ bool call::matches_scenario(unsigned int index, int reply_code, char * request, + } else if (curmsg->recv_response && (curmsg->recv_response == reply_code)) { + /* This is a potential candidate, we need to match transactions. */ + if (curmsg->response_txn) { +- if (txnID[curmsg->response_txn - 1] && !strcmp(txnID[curmsg->response_txn - 1], txn)) { ++ if (transactions[curmsg->response_txn - 1].txnID && !strcmp(transactions[curmsg->response_txn - 1].txnID, txn)) { + return true; + } else { + return false; +@@ -2643,7 +2885,7 @@ void call::queue_up(char *msg) { + queued_msg = strdup(msg); + } + +-bool call::process_incoming(char * msg) ++bool call::process_incoming(char * msg, struct sockaddr_storage *src) + { + int reply_code; + static char request[65]; +@@ -2655,11 +2897,19 @@ bool call::process_incoming(char * msg) + bool found = false; + T_ActionResult actionResult; + ++ getmilliseconds(); ++ callDebug("Processing %d byte incoming message for call-ID %s (hash %u):\n%s\n\n", strlen(msg), id, hash(msg), msg); ++ + setRunning(); + + /* Ignore the messages received during a pause if -pause_msg_ign is set */ + if(call_scenario->messages[msg_index] -> M_type == MSG_TYPE_PAUSE && pause_msg_ign) return(true); + ++ /* Get our destination if we have none. */ ++ if (call_peer.ss_family == AF_UNSPEC && src) { ++ memcpy(&call_peer, src, SOCK_ADDR_SIZE(src)); ++ } ++ + /* Authorize nop as a first command, even in server mode */ + if((msg_index == 0) && (call_scenario->messages[msg_index] -> M_type == MSG_TYPE_NOP)) { + queue_up (msg); +@@ -2680,6 +2930,7 @@ bool call::process_incoming(char * msg) + if(lost(recv_retrans_recv_index)) { + TRACE_MSG("%s message (retrans) lost (recv).", + TRANSPORT_TO_STRING(transport)); ++ callDebug("%s message (retrans) lost (recv) (hash %u)\n", TRANSPORT_TO_STRING(transport), hash(msg)); + + if(comp_state) { comp_free(&comp_state); } + call_scenario->messages[recv_retrans_recv_index] -> nb_lost++; +@@ -2688,9 +2939,9 @@ bool call::process_incoming(char * msg) + + call_scenario->messages[recv_retrans_recv_index] -> nb_recv_retrans++; + +- send_scene(recv_retrans_send_index, &status); ++ send_scene(recv_retrans_send_index, &status, NULL); + +- if(status == 0) { ++ if(status >= 0) { + call_scenario->messages[recv_retrans_send_index] -> nb_sent_retrans++; + computeStat(CStat::E_RETRANSMISSION); + } else if(status < 0) { +@@ -2755,7 +3006,7 @@ bool call::process_incoming(char * msg) + // extract the cseq method from the response + extract_cseq_method (responsecseqmethod, msg); + extract_transaction (txn, msg); +- } else if(ptr = strchr(msg, ' ')) { ++ } else if((ptr = strchr(msg, ' '))) { + if((ptr - msg) < 64) { + memcpy(request, msg, ptr - msg); + request[ptr - msg] = 0; +@@ -2787,7 +3038,7 @@ bool call::process_incoming(char * msg) + /* Try to find it in the expected non mandatory responses + * until the first mandatory response in the scenario */ + for(search_index = msg_index; +- search_index < call_scenario->length; ++ search_index < (int)call_scenario->messages.size(); + search_index++) { + if(!matches_scenario(search_index, reply_code, request, responsecseqmethod, txn)) { + if(call_scenario->messages[search_index] -> optional) { +@@ -2819,22 +3070,37 @@ bool call::process_incoming(char * msg) + } else { + if (int checkTxn = call_scenario->messages[search_index]->response_txn) { + /* This is a reply to an old transaction. */ +- if (!strcmp(txnID[checkTxn - 1], txn)) { ++ if (!strcmp(transactions[checkTxn - 1].txnID, txn)) { + /* This reply is provisional, so it should have no effect if we recieve it out-of-order. */ + if (reply_code >= 100 && reply_code <= 199) { + TRACE_MSG("-----------------------------------------------\n" + "Ignoring provisional %s message for transaction %s:\n\n%s\n", +- TRANSPORT_TO_STRING(transport), call_scenario->txnRevMap[checkTxn - 1], msg); ++ TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, msg); ++ callDebug("Ignoring provisional %s message for transaction %s (hash %u):\n\n%s\n", ++ TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, hash(msg), msg); + return true; +- } else if (call_scenario->messages[search_index + 1]->M_type == MSG_TYPE_SEND && call_scenario->messages[search_index + 1]->send_scheme->isAck()) { ++ } else if (int ackIndex = transactions[checkTxn - 1].ackIndex) { + /* This is the message before an ACK, so verify that this is an invite transaction. */ +- if (!strcmp(responsecseqmethod, "INVITE")) { +- sendBuffer(createSendingMessage(call_scenario->messages[search_index+1] -> send_scheme, (search_index+1))); ++ assert (call_scenario->transactions[checkTxn - 1].isInvite); ++ sendBuffer(createSendingMessage(call_scenario->messages[ackIndex] -> send_scheme, ackIndex)); ++ return true; ++ } else { ++ assert (!call_scenario->transactions[checkTxn - 1].isInvite); ++ /* This is a non-provisional message for the transaction, and ++ * we have already gotten our allowable response. Just make sure ++ * that it is not a retransmission of the final response. */ ++ if (transactions[checkTxn - 1].txnResp == hash(msg)) { ++ /* We have gotten this retransmission out-of-order, let's just ignore it. */ ++ TRACE_MSG("-----------------------------------------------\n" ++ "Ignoring final %s message for transaction %s:\n\n%s\n", ++ TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, msg); ++ callDebug("Ignoring final %s message for transaction %s (hash %u):\n\n%s\n", ++ TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, hash(msg), msg); ++ WARNING("Ignoring final %s message for transaction %s (hash %u):\n\n%s\n", ++ TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, hash(msg), msg); + return true; + } + } +- /* This is a non-provisional message for the transaction, and +- * we have already gotten our allowable response. */ + } + } else { + /* +@@ -2902,14 +3168,21 @@ bool call::process_incoming(char * msg) + if(lost(search_index)) { + TRACE_MSG("%s message lost (recv).", + TRANSPORT_TO_STRING(transport)); ++ callDebug("%s message lost (recv) (hash %u).\n", ++ TRANSPORT_TO_STRING(transport), hash(msg)); + if(comp_state) { comp_free(&comp_state); } + call_scenario->messages[search_index] -> nb_lost++; + return true; + } + ++ /* If we are part of a transaction, mark this as the final response. */ ++ if (int checkTxn = call_scenario->messages[search_index]->response_txn) { ++ transactions[checkTxn - 1].txnResp = hash(msg); ++ } ++ + + /* Handle counters and RTDs for this message. */ +- do_bookkeeping(search_index); ++ do_bookkeeping(call_scenario->messages[search_index]); + + /* Increment the recv counter */ + call_scenario->messages[search_index] -> nb_recv++; +@@ -2918,7 +3191,7 @@ bool call::process_incoming(char * msg) + if (found) { + //WARNING("---EXECUTE_ACTION_ON_MSG---%s---", msg); + +- actionResult = executeAction(msg, search_index); ++ actionResult = executeAction(msg, call_scenario->messages[search_index]); + + if(actionResult != call::E_AR_NO_ERROR) { + // Store last action result if it is an error +@@ -2926,6 +3199,9 @@ bool call::process_incoming(char * msg) + call::last_action_result = actionResult; + if (actionResult == E_AR_STOP_CALL) { + return rejectCall(); ++ } else if (actionResult == E_AR_CONNECT_FAILED) { ++ terminate(CStat::E_FAILED_TCP_CONNECT); ++ return false; + } + } + } +@@ -2976,7 +3252,7 @@ bool call::process_incoming(char * msg) + if (call_scenario->messages[search_index] -> bShouldRecordRoutes && + NULL == dialog_route_set ) { + +- next_req_url = (char*) calloc(1, MAX_HEADER_LEN); ++ next_req_url = (char*) realloc(next_req_url, MAX_HEADER_LEN); + + char rr[MAX_HEADER_LEN]; + memset(rr, 0, sizeof(rr)); +@@ -3039,6 +3315,7 @@ bool call::process_incoming(char * msg) + * in our messages. */ + last_recv_index = search_index; + last_recv_hash = cookie; ++ callDebug("Set Last Recv Hash: %u (recv index %d)\n", last_recv_hash, last_recv_index); + last_recv_msg = (char *) realloc(last_recv_msg, strlen(msg) + 1); + strcpy(last_recv_msg, msg); + +@@ -3063,7 +3340,7 @@ bool call::process_incoming(char * msg) + /* We are just waiting for a message to be received, if any of the + * potential messages have a timeout we set it as our timeout. We + * start from the next message and go until any non-receives. */ +- for(search_index++; search_index < call_scenario->length; search_index++) { ++ for(search_index++; search_index < (int)call_scenario->messages.size(); search_index++) { + if(call_scenario->messages[search_index] -> M_type != MSG_TYPE_RECV) { + break; + } +@@ -3092,275 +3369,464 @@ double call::get_rhs(CAction *currentAction) { + } + } + +-call::T_ActionResult call::executeAction(char * msg, int scenarioIndex) ++call::T_ActionResult call::executeAction(char * msg, message *curmsg) + { + CActions* actions; + CAction* currentAction; + +- actions = call_scenario->messages[scenarioIndex]->M_actions; ++ actions = curmsg->M_actions; + // looking for action to do on this message +- if(actions != NULL) { +- for(int i=0; igetActionSize(); i++) { +- currentAction = actions->getAction(i); +- if(currentAction != NULL) { +- if(currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_REGEXP) { +- char msgPart[MAX_SUB_MESSAGE_LENGTH]; +- +- /* Where to look. */ +- char *haystack; +- +- if(currentAction->getLookingPlace() == CAction::E_LP_HDR) { +- extractSubMessage (msg, +- currentAction->getLookingChar(), +- msgPart, +- currentAction->getCaseIndep(), +- currentAction->getOccurence(), +- currentAction->getHeadersOnly()); +- if(currentAction->getCheckIt() == true && (strlen(msgPart) < 0)) { +- // the sub message is not found and the checking action say it +- // MUST match --> Call will be marked as failed but will go on +- WARNING("Failed regexp match: header %s not found in message %s\n", currentAction->getLookingChar(), msg); +- return(call::E_AR_HDR_NOT_FOUND); +- } +- haystack = msgPart; +- } else { +- haystack = msg; +- } +- currentAction->executeRegExp(haystack, M_callVariableTable); +- +- if( (!(M_callVariableTable->getVar(currentAction->getVarId())->isSet())) && (currentAction->getCheckIt() == true) ) { +- // the message doesn't match and the checkit action say it MUST match +- // Allow easier regexp debugging +- WARNING("Failed regexp match: looking in '%s', with regexp '%s'", +- haystack, currentAction->getRegularExpression()); +- return(call::E_AR_REGEXP_DOESNT_MATCH); ++ if(actions == NULL) { ++ return(call::E_AR_NO_ERROR); ++ } ++ ++ for(int i=0; igetActionSize(); i++) { ++ currentAction = actions->getAction(i); ++ if(currentAction == NULL) { ++ continue; ++ } ++ ++ if(currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_REGEXP) { ++ char msgPart[MAX_SUB_MESSAGE_LENGTH]; ++ ++ /* Where to look. */ ++ char *haystack; ++ ++ if(currentAction->getLookingPlace() == CAction::E_LP_HDR) { ++ extractSubMessage (msg, ++ currentAction->getLookingChar(), ++ msgPart, ++ currentAction->getCaseIndep(), ++ currentAction->getOccurence(), ++ currentAction->getHeadersOnly()); ++ if(currentAction->getCheckIt() == true && (strlen(msgPart) < 0)) { ++ // the sub message is not found and the checking action say it ++ // MUST match --> Call will be marked as failed but will go on ++ WARNING("Failed regexp match: header %s not found in message %s\n", currentAction->getLookingChar(), msg); ++ return(call::E_AR_HDR_NOT_FOUND); ++ } ++ haystack = msgPart; ++ } else if(currentAction->getLookingPlace() == CAction::E_LP_BODY) { ++ haystack = strstr(msg, "\r\n\r\n"); ++ if (!haystack) { ++ if (currentAction->getCheckIt() == true) { ++ WARNING("Failed regexp match: body not found in message %s\n", msg); ++ return(call::E_AR_HDR_NOT_FOUND); + } +- } else /* end action == E_AT_ASSIGN_FROM_REGEXP */ +- if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_VALUE) { +- double operand = get_rhs(currentAction); +- M_callVariableTable->getVar(currentAction->getVarId())->setDouble(operand); +- } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_INDEX) { +- M_callVariableTable->getVar(currentAction->getVarId())->setDouble(msg_index); +- } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_GETTIMEOFDAY) { +- struct timeval tv; +- gettimeofday(&tv, NULL); +- M_callVariableTable->getVar(currentAction->getVarId())->setDouble((double)tv.tv_sec); +- M_callVariableTable->getVar(currentAction->getSubVarId(0))->setDouble((double)tv.tv_usec); +- } else if (currentAction->getActionType() == CAction::E_AT_LOOKUP) { +- /* Create strings from the sending messages. */ +- char *file = strdup(createSendingMessage(currentAction->getMessage(0), -2)); +- char *key = strdup(createSendingMessage(currentAction->getMessage(1), -2)); +- double value = -1; +- +- str_int_map::iterator index_it = infIndex[file]->find(key); +- if (index_it != infIndex[file]->end()) { +- value = index_it->second; ++ msgPart[0] = '\0'; ++ haystack = msgPart; ++ } ++ haystack += strlen("\r\n\r\n"); ++ } else if(currentAction->getLookingPlace() == CAction::E_LP_MSG) { ++ haystack = msg; ++ } else if(currentAction->getLookingPlace() == CAction::E_LP_VAR) { ++ /* Get the input variable. */ ++ haystack = M_callVariableTable->getVar(currentAction->getVarInId())->getString(); ++ if (!haystack) { ++ if (currentAction->getCheckIt() == true) { ++ WARNING("Failed regexp match: variable $%d not set\n", currentAction->getVarInId()); ++ return(call::E_AR_HDR_NOT_FOUND); + } +- M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value); +- free(file); +- free(key); +-#ifdef _USE_OPENSSL +- } else if (currentAction->getActionType() == CAction::E_AT_VERIFY_AUTH) { +- bool result; +- char *lf; +- char *end; +- +- lf = strchr(msg, '\n'); +- end = strchr(msg, ' '); +- +- if (!lf || !end) { +- result = false; +- } else if (lf < end) { +- result = false; ++ } ++ } else { ++ ERROR("Invalid looking place: %d\n", currentAction->getLookingPlace()); ++ } ++ currentAction->executeRegExp(haystack, M_callVariableTable); ++ ++ if( (!(M_callVariableTable->getVar(currentAction->getVarId())->isSet())) && (currentAction->getCheckIt() == true) ) { ++ // the message doesn't match and the checkit action say it MUST match ++ // Allow easier regexp debugging ++ WARNING("Failed regexp match: looking in '%s', with regexp '%s'", ++ haystack, currentAction->getRegularExpression()); ++ return(call::E_AR_REGEXP_DOESNT_MATCH); ++ } else if ( ((M_callVariableTable->getVar(currentAction->getVarId())->isSet())) && ++ (currentAction->getCheckItInverse() == true) ) ++ { ++ // The inverse of the above ++ WARNING("Regexp matched but should not: looking in '%s', with regexp '%s'", ++ haystack, currentAction->getRegularExpression()); ++ return(call::E_AR_REGEXP_SHOULDNT_MATCH); ++ } ++ } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_VALUE) { ++ double operand = get_rhs(currentAction); ++ M_callVariableTable->getVar(currentAction->getVarId())->setDouble(operand); ++ } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_INDEX) { ++ M_callVariableTable->getVar(currentAction->getVarId())->setDouble(msg_index); ++ } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_GETTIMEOFDAY) { ++ struct timeval tv; ++ gettimeofday(&tv, NULL); ++ M_callVariableTable->getVar(currentAction->getVarId())->setDouble((double)tv.tv_sec); ++ M_callVariableTable->getVar(currentAction->getSubVarId(0))->setDouble((double)tv.tv_usec); ++ } else if (currentAction->getActionType() == CAction::E_AT_LOOKUP) { ++ /* Create strings from the sending messages. */ ++ char *file = strdup(createSendingMessage(currentAction->getMessage(0), -2)); ++ char *key = strdup(createSendingMessage(currentAction->getMessage(1), -2)); ++ ++ if (inFiles.find(file) == inFiles.end()) { ++ ERROR("Invalid injection file for insert: %s", file); ++ } ++ ++ double value = inFiles[file]->lookup(key); ++ ++ M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value); ++ free(file); ++ free(key); ++ } else if (currentAction->getActionType() == CAction::E_AT_INSERT) { ++ /* Create strings from the sending messages. */ ++ char *file = strdup(createSendingMessage(currentAction->getMessage(0), -2)); ++ char *value = strdup(createSendingMessage(currentAction->getMessage(1), -2)); ++ ++ if (inFiles.find(file) == inFiles.end()) { ++ ERROR("Invalid injection file for insert: %s", file); ++ } ++ ++ inFiles[file]->insert(value); ++ ++ free(file); ++ free(value); ++ } else if (currentAction->getActionType() == CAction::E_AT_REPLACE) { ++ /* Create strings from the sending messages. */ ++ char *file = strdup(createSendingMessage(currentAction->getMessage(0), -2)); ++ char *line = strdup(createSendingMessage(currentAction->getMessage(1), -2)); ++ char *value = strdup(createSendingMessage(currentAction->getMessage(2), -2)); ++ ++ if (inFiles.find(file) == inFiles.end()) { ++ ERROR("Invalid injection file for replace: %s", file); ++ } ++ ++ char *endptr; ++ int lineNum = (int)strtod(line, &endptr); ++ if (*endptr) { ++ ERROR("Invalid line number for replace: %s", line); ++ } ++ ++ inFiles[file]->replace(lineNum, value); ++ ++ free(file); ++ free(line); ++ free(value); ++ } else if (currentAction->getActionType() == CAction::E_AT_CLOSE_CON) { ++ if (call_socket) { ++ sipp_socket_invalidate(call_socket); ++ sipp_close_socket(call_socket); ++ call_socket = NULL; ++ } ++ } else if (currentAction->getActionType() == CAction::E_AT_SET_DEST) { ++ /* Change the destination for this call. */ ++ char *str_host = strdup(createSendingMessage(currentAction->getMessage(0), -2)); ++ char *str_port = strdup(createSendingMessage(currentAction->getMessage(1), -2)); ++ char *str_protocol = strdup(createSendingMessage(currentAction->getMessage(2), -2)); ++ ++ char *endptr; ++ int port = (int)strtod(str_port, &endptr); ++ if (*endptr) { ++ ERROR("Invalid port for setdest: %s", str_port); ++ } ++ ++ int protocol; ++ if (!strcmp(str_protocol, "udp") || !strcmp(str_protocol, "UDP")) { ++ protocol = T_UDP; ++ } else if (!strcmp(str_protocol, "tcp") || !strcmp(str_protocol, "TCP")) { ++ protocol = T_TCP; ++ } else if (!strcmp(str_protocol, "tls") || !strcmp(str_protocol, "TLS")) { ++ protocol = T_TLS; ++ } else { ++ ERROR("Unknown transport for setdest: '%s'", str_protocol); ++ } ++ ++ if (!call_socket && protocol == T_TCP && transport == T_TCP) { ++ bool existing; ++ if ((associate_socket(new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) { ++ ERROR_NO("Unable to get a TCP socket"); ++ } ++ ++ if (!existing) { ++ sipp_customize_socket(call_socket); ++ } ++ } ++ ++ ++ if (protocol != call_socket->ss_transport) { ++ ERROR("Can not switch protocols during setdest."); ++ } ++ ++ if (protocol == T_UDP) { ++ /* Nothing to do. */ ++ } else if (protocol == T_TLS) { ++ ERROR("Changing destinations is not supported for TLS."); ++ } else if (protocol == T_TCP) { ++ if (!multisocket) { ++ ERROR("Changing destinations for TCP requires multisocket mode."); ++ } ++ if (call_socket->ss_count > 1) { ++ ERROR("Can not change destinations for a TCP socket that has more than one user."); ++ } ++ } ++ ++ struct addrinfo hints; ++ struct addrinfo * local_addr; ++ memset((char*)&hints, 0, sizeof(hints)); ++ hints.ai_flags = AI_PASSIVE; ++ hints.ai_family = PF_UNSPEC; ++ is_ipv6 = false; ++ ++ if (getaddrinfo(str_host, NULL, &hints, &local_addr) != 0) { ++ ERROR("Unknown host '%s' for setdest", str_host); ++ } ++ if (_RCAST(struct sockaddr_storage *, local_addr->ai_addr)->ss_family != call_peer.ss_family) { ++ ERROR("Can not switch between IPv4 and IPV6 using setdest!"); ++ } ++ memcpy(&call_peer, local_addr->ai_addr, SOCK_ADDR_SIZE(_RCAST(struct sockaddr_storage *,local_addr->ai_addr))); ++ if (call_peer.ss_family == AF_INET) { ++ (_RCAST(struct sockaddr_in *,&call_peer))->sin_port = htons(port); ++ } else { ++ (_RCAST(struct sockaddr_in6 *,&call_peer))->sin6_port = htons(port); ++ } ++ memcpy(&call_socket->ss_dest, &call_peer, SOCK_ADDR_SIZE(_RCAST(struct sockaddr_storage *,&call_peer))); ++ ++ free(str_host); ++ free(str_port); ++ free(str_protocol); ++ ++ if (protocol == T_TCP) { ++ close(call_socket->ss_fd); ++ call_socket->ss_fd = -1; ++ call_socket->ss_changed_dest = true; ++ if (sipp_reconnect_socket(call_socket)) { ++ if (reconnect_allowed()) { ++ if(errno == EINVAL){ ++ /* This occurs sometime on HPUX but is not a true INVAL */ ++ WARNING("Unable to connect a TCP socket, remote peer error"); ++ } else { ++ WARNING("Unable to connect a TCP socket"); ++ } ++ /* This connection failed. We must be in multisocket mode, because ++ * otherwise we would already have a call_socket. This call can not ++ * succeed, but does not affect any of our other calls. We do decrement ++ * the reconnection counter however. */ ++ if (reset_number != -1) { ++ reset_number--; ++ } ++ ++ return E_AR_CONNECT_FAILED; + } else { +- char *auth = get_header(msg, "Authorization:", true); +- char *method = (char *)malloc(end - msg + 1); +- strncpy(method, msg, end - msg); +- method[end - msg] = '\0'; +- +- /* Generate the username to verify it against. */ +- char *tmp = createSendingMessage(currentAction->getMessage(0), -2 /* do not add crlf*/); +- char *username = strdup(tmp); +- /* Generate the password to verify it against. */ +- tmp= createSendingMessage(currentAction->getMessage(1), -2 /* do not add crlf*/); +- char *password = strdup(tmp); +- +- result = verifyAuthHeader(username, password, method, auth); +- +- free(username); +- free(password); ++ if(errno == EINVAL){ ++ /* This occurs sometime on HPUX but is not a true INVAL */ ++ ERROR("Unable to connect a TCP socket, remote peer error"); ++ } else { ++ ERROR_NO("Unable to connect a TCP socket"); ++ } + } ++ } ++ } ++#ifdef _USE_OPENSSL ++ } else if (currentAction->getActionType() == CAction::E_AT_VERIFY_AUTH) { ++ bool result; ++ char *lf; ++ char *end; ++ ++ lf = strchr(msg, '\n'); ++ end = strchr(msg, ' '); ++ ++ if (!lf || !end) { ++ result = false; ++ } else if (lf < end) { ++ result = false; ++ } else { ++ char *auth = get_header(msg, "Authorization:", true); ++ char *method = (char *)malloc(end - msg + 1); ++ strncpy(method, msg, end - msg); ++ method[end - msg] = '\0'; ++ ++ /* Generate the username to verify it against. */ ++ char *tmp = createSendingMessage(currentAction->getMessage(0), -2 /* do not add crlf*/); ++ char *username = strdup(tmp); ++ /* Generate the password to verify it against. */ ++ tmp= createSendingMessage(currentAction->getMessage(1), -2 /* do not add crlf*/); ++ char *password = strdup(tmp); ++ ++ result = verifyAuthHeader(username, password, method, auth); ++ ++ free(username); ++ free(password); ++ } + +- M_callVariableTable->getVar(currentAction->getVarId())->setBool(result); ++ M_callVariableTable->getVar(currentAction->getVarId())->setBool(result); + #endif +- } else if (currentAction->getActionType() == CAction::E_AT_JUMP) { +- double operand = get_rhs(currentAction); +- msg_index = (int)operand - 1; +- } else if (currentAction->getActionType() == CAction::E_AT_PAUSE_RESTORE) { +- double operand = get_rhs(currentAction); +- paused_until = (int)operand; +- } else if (currentAction->getActionType() == CAction::E_AT_VAR_ADD) { +- double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble(); +- double operand = get_rhs(currentAction); +- M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value + operand); +- } else if (currentAction->getActionType() == CAction::E_AT_VAR_SUBTRACT) { +- double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble(); +- double operand = get_rhs(currentAction); +- M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value - operand); +- } else if (currentAction->getActionType() == CAction::E_AT_VAR_MULTIPLY) { +- double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble(); +- double operand = get_rhs(currentAction); +- M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value * operand); +- } else if (currentAction->getActionType() == CAction::E_AT_VAR_DIVIDE) { +- double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble(); +- double operand = get_rhs(currentAction); +- if (operand == 0) { +- WARNING("Action failure: Can not divide by zero ($%d/$%d)!\n", currentAction->getVarId(), currentAction->getVarInId()); +- } else { +- M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value / operand); +- } +- } else if (currentAction->getActionType() == CAction::E_AT_VAR_TEST) { +- double value = currentAction->compare(M_callVariableTable); +- M_callVariableTable->getVar(currentAction->getVarId())->setBool(value); +- } else if (currentAction->getActionType() == CAction::E_AT_VAR_STRCMP) { +- char *rhs = M_callVariableTable->getVar(currentAction->getVarInId())->getString(); +- char *lhs = currentAction->getStringValue(); +- int value = strcmp(rhs, lhs); +- M_callVariableTable->getVar(currentAction->getVarId())->setDouble((double)value); +- } else if (currentAction->getActionType() == CAction::E_AT_VAR_TRIM) { +- CCallVariable *var = M_callVariableTable->getVar(currentAction->getVarId()); +- char *in = var->getString(); +- char *p = in; +- while (isspace(*p)) { +- p++; +- } +- char *q = strdup(p); +- var->setString(q); +- int l = strlen(q); +- for (int i = l - 1; i >= 0 & isspace(q[i]); i--) { +- q[i] = '\0'; +- } +- } else if (currentAction->getActionType() == CAction::E_AT_VAR_TO_DOUBLE) { +- double value; ++ } else if (currentAction->getActionType() == CAction::E_AT_JUMP) { ++ double operand = get_rhs(currentAction); ++ msg_index = (int)operand - 1; ++ } else if (currentAction->getActionType() == CAction::E_AT_PAUSE_RESTORE) { ++ double operand = get_rhs(currentAction); ++ paused_until = (int)operand; ++ } else if (currentAction->getActionType() == CAction::E_AT_VAR_ADD) { ++ double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble(); ++ double operand = get_rhs(currentAction); ++ M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value + operand); ++ } else if (currentAction->getActionType() == CAction::E_AT_VAR_SUBTRACT) { ++ double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble(); ++ double operand = get_rhs(currentAction); ++ M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value - operand); ++ } else if (currentAction->getActionType() == CAction::E_AT_VAR_MULTIPLY) { ++ double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble(); ++ double operand = get_rhs(currentAction); ++ M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value * operand); ++ } else if (currentAction->getActionType() == CAction::E_AT_VAR_DIVIDE) { ++ double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble(); ++ double operand = get_rhs(currentAction); ++ if (operand == 0) { ++ WARNING("Action failure: Can not divide by zero ($%d/$%d)!\n", currentAction->getVarId(), currentAction->getVarInId()); ++ } else { ++ M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value / operand); ++ } ++ } else if (currentAction->getActionType() == CAction::E_AT_VAR_TEST) { ++ double value = currentAction->compare(M_callVariableTable); ++ M_callVariableTable->getVar(currentAction->getVarId())->setBool(value); ++ } else if (currentAction->getActionType() == CAction::E_AT_VAR_STRCMP) { ++ char *rhs = M_callVariableTable->getVar(currentAction->getVarInId())->getString(); ++ char *lhs; ++ if (currentAction->getVarIn2Id()) { ++ lhs = M_callVariableTable->getVar(currentAction->getVarIn2Id())->getString(); ++ } else { ++ lhs = currentAction->getStringValue(); ++ } ++ int value = strcmp(rhs, lhs); ++ M_callVariableTable->getVar(currentAction->getVarId())->setDouble((double)value); ++ } else if (currentAction->getActionType() == CAction::E_AT_VAR_TRIM) { ++ CCallVariable *var = M_callVariableTable->getVar(currentAction->getVarId()); ++ char *in = var->getString(); ++ char *p = in; ++ while (isspace(*p)) { ++ p++; ++ } ++ char *q = strdup(p); ++ var->setString(q); ++ int l = strlen(q); ++ for (int i = l - 1; i >= 0 & isspace(q[i]); i--) { ++ q[i] = '\0'; ++ } ++ } else if (currentAction->getActionType() == CAction::E_AT_VAR_TO_DOUBLE) { ++ double value; ++ ++ if (M_callVariableTable->getVar(currentAction->getVarInId())->toDouble(&value)) { ++ M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value); ++ } else { ++ WARNING("Invalid double conversion from $%d to $%d", currentAction->getVarInId(), currentAction->getVarId()); ++ } ++ } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_SAMPLE) { ++ double value = currentAction->getDistribution()->sample(); ++ M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value); ++ } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_STRING) { ++ char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/); ++ char *str = strdup(x); ++ if (!str) { ++ ERROR("Out of memory duplicating string for assignment!"); ++ } ++ M_callVariableTable->getVar(currentAction->getVarId())->setString(str); ++ } else if (currentAction->getActionType() == CAction::E_AT_LOG_TO_FILE) { ++ char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/); ++ LOG_MSG("%s\n", x); ++ } else if (currentAction->getActionType() == CAction::E_AT_LOG_WARNING) { ++ char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/); ++ WARNING("%s", x); ++ } else if (currentAction->getActionType() == CAction::E_AT_LOG_ERROR) { ++ char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/); ++ ERROR("%s", x); ++ } else if (currentAction->getActionType() == CAction::E_AT_EXECUTE_CMD) { ++ char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/); ++ // TRACE_MSG("Trying to execute [%s]", x); ++ pid_t l_pid; ++ switch(l_pid = fork()) ++ { ++ case -1: ++ // error when forking ! ++ ERROR_NO("Forking error main"); ++ break; + +- if (M_callVariableTable->getVar(currentAction->getVarInId())->toDouble(&value)) { +- M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value); ++ case 0: ++ // first child process - execute the command ++ if((l_pid = fork()) < 0) { ++ ERROR_NO("Forking error child"); + } else { +- WARNING("Invalid double conversion from $%d to $%d", currentAction->getVarInId(), currentAction->getVarId()); ++ if( l_pid == 0){ ++ int ret; ++ ret = system(x); // second child runs ++ if(ret == -1) { ++ WARNING("system call error for %s",x); ++ } ++ } ++ exit(EXIT_OTHER); + } +- } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_SAMPLE) { +- double value = currentAction->getDistribution()->sample(); +- M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value); +- } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_STRING) { +- char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/); +- char *str = strdup(x); +- if (!str) { +- ERROR("Out of memory duplicating string for assignment!"); ++ break; ++ default: ++ // parent process continue ++ // reap first child immediately ++ pid_t ret; ++ while ((ret=waitpid(l_pid, NULL, 0)) != l_pid) { ++ if (ret != -1) { ++ ERROR("waitpid returns %1d for child %1d",ret,l_pid); + } +- M_callVariableTable->getVar(currentAction->getVarId())->setString(str); +- } else if (currentAction->getActionType() == CAction::E_AT_LOG_TO_FILE) { +- char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/); +- LOG_MSG("%s\n", x); +- } else if (currentAction->getActionType() == CAction::E_AT_LOG_WARNING) { +- char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/); +- WARNING("%s", x); +- } else if (currentAction->getActionType() == CAction::E_AT_EXECUTE_CMD) { +- +- if (currentAction->getCmdLine()) { +- char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/); +- // TRACE_MSG("Trying to execute [%s]", x); +- pid_t l_pid; +- switch(l_pid = fork()) +- { +- case -1: +- // error when forking ! +- ERROR_NO("Forking error main"); +- break; +- +- case 0: +- // first child process - execute the command +- if((l_pid = fork()) < 0) { +- ERROR_NO("Forking error child"); +- } else { +- if( l_pid == 0){ +- int ret; +- ret = system(x); // second child runs +- if(ret == -1) { +- WARNING("system call error for %s",x); +- } +- } +- exit(EXIT_OTHER); +- } +- break; +- default: +- // parent process continue +- // reap first child immediately +- pid_t ret; +- while ((ret=waitpid(l_pid, NULL, 0)) != l_pid) { +- if (ret != -1) { +- ERROR("waitpid returns %1d for child %1d",ret,l_pid); +- } +- } +- break; +- } +- } +- } else /* end action == E_AT_EXECUTE_CMD */ +- if (currentAction->getActionType() == CAction::E_AT_EXEC_INTCMD) { +- switch (currentAction->getIntCmd()) +- { +- case CAction::E_INTCMD_STOP_ALL: +- quitting = 1; +- break; +- case CAction::E_INTCMD_STOP_NOW: +- screen_exit(EXIT_TEST_RES_INTERNAL); +- break; +- case CAction::E_INTCMD_STOPCALL: +- default: +- return(call::E_AR_STOP_CALL); +- break; +- } ++ } ++ break; ++ } ++ } else if (currentAction->getActionType() == CAction::E_AT_EXEC_INTCMD) { ++ switch (currentAction->getIntCmd()) ++ { ++ case CAction::E_INTCMD_STOP_ALL: ++ quitting = 1; ++ break; ++ case CAction::E_INTCMD_STOP_NOW: ++ screen_exit(EXIT_TEST_RES_INTERNAL); ++ break; ++ case CAction::E_INTCMD_STOPCALL: ++ default: ++ return(call::E_AR_STOP_CALL); ++ break; ++ } + #ifdef PCAPPLAY +- } else if ((currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) || +- (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO)) { +- play_args_t *play_args; +- if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) { +- play_args = &(this->play_args_a); +- } else if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO) { +- play_args = &(this->play_args_v); +- } +- play_args->pcap = currentAction->getPcapPkts(); +- /* port number is set in [auto_]media_port interpolation */ +- if (media_ip_is_ipv6) { +- struct sockaddr_in6 *from = (struct sockaddr_in6 *)(void *) &(play_args->from); +- from->sin6_family = AF_INET6; +- inet_pton(AF_INET6, media_ip, &(from->sin6_addr)); +- } +- else { +- struct sockaddr_in *from = (struct sockaddr_in *)(void *) &(play_args->from); +- from->sin_family = AF_INET; +- from->sin_addr.s_addr = inet_addr(media_ip); +- } +- /* Create a thread to send RTP packets */ +- pthread_attr_t attr; +- pthread_attr_init(&attr); ++ } else if ((currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) || ++ (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO)) { ++ play_args_t *play_args; ++ if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) { ++ play_args = &(this->play_args_a); ++ } else if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO) { ++ play_args = &(this->play_args_v); ++ } ++ play_args->pcap = currentAction->getPcapPkts(); ++ /* port number is set in [auto_]media_port interpolation */ ++ if (media_ip_is_ipv6) { ++ struct sockaddr_in6 *from = (struct sockaddr_in6 *)(void *) &(play_args->from); ++ from->sin6_family = AF_INET6; ++ inet_pton(AF_INET6, media_ip, &(from->sin6_addr)); ++ } ++ else { ++ struct sockaddr_in *from = (struct sockaddr_in *)(void *) &(play_args->from); ++ from->sin_family = AF_INET; ++ from->sin_addr.s_addr = inet_addr(media_ip); ++ } ++ /* Create a thread to send RTP packets */ ++ pthread_attr_t attr; ++ pthread_attr_init(&attr); + #ifndef PTHREAD_STACK_MIN + #define PTHREAD_STACK_MIN 16384 + #endif +- //pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN); +- pthread_attr_setdetachstate(&attr, +- PTHREAD_CREATE_DETACHED); +- int ret = pthread_create(&media_thread, &attr, send_wrapper, +- (void *) play_args); +- if(ret) +- ERROR("Can create thread to send RTP packets"); +- pthread_attr_destroy(&attr); ++ //pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN); ++ if (media_thread != 0) { ++ // If a media_thread is already active, kill it before starting a new one ++ pthread_cancel(media_thread); ++ pthread_join(media_thread, NULL); ++ media_thread = 0; ++ } ++ int ret = pthread_create(&media_thread, &attr, send_wrapper, ++ (void *) play_args); ++ if(ret) ++ ERROR("Can create thread to send RTP packets"); ++ pthread_attr_destroy(&attr); + #endif +- } else { +- ERROR("call::executeAction unknown action"); +- } +- } // end if current action != null +- } // end for +- } ++ } else { ++ ERROR("call::executeAction unknown action"); ++ } ++ } // end for + return(call::E_AR_NO_ERROR); + } + +@@ -3449,9 +3915,6 @@ void call::getFieldFromInputFile(const char *fileName, int field, SendingMessage + } + + call::T_AutoMode call::checkAutomaticResponseMode(char * P_recv) { +- +- int L_res = E_AM_DEFAULT ; +- + if (strcmp(P_recv, "BYE")==0) { + return E_AM_UNEXP_BYE; + } else if (strcmp(P_recv, "CANCEL") == 0) { +diff --git a/call.hpp b/call.hpp +index 6e86799..b09d45b 100644 +--- a/call.hpp ++++ b/call.hpp +@@ -16,6 +16,7 @@ + * Author : Richard GAYRAUD - 04 Nov 2003 + * From Hewlett Packard Company. + * Charles P. Wright from IBM Research ++ * Andy Aicken + */ + + #ifndef __CALL__ +@@ -46,6 +47,10 @@ + #define DEFAULT_T2_TIMER_VALUE 4000 + #define SIP_TRANSACTION_TIMEOUT 32000 + ++/* Retransmission check methods. */ ++#define RTCHECK_FULL 1 ++#define RTCHECK_LOOSE 2 ++ + #ifdef __HPUX + extern int createAuthHeader(char * user, char * password, char * method, char * uri, char * msgbody, char * auth, char * aka_OP, char * aka_AMF, char * aka_K, char * result); + #else +@@ -53,6 +58,12 @@ + extern "C" { int verifyAuthHeader(char * user, char * password, char * method, char * auth); } + #endif + ++struct txnInstanceInfo { ++ char *txnID; ++ unsigned long txnResp; ++ int ackIndex; ++}; ++ + class call : virtual public task, virtual public listener, public virtual socketowner { + public: + /* These are wrappers for various circumstances, (private) init does the real work. */ +@@ -60,21 +71,21 @@ public: + call(char *p_id, bool use_ipv6, int userId, struct sockaddr_storage *dest); + call(char *p_id, struct sipp_socket *socket, struct sockaddr_storage *dest); + static call *add_call(int userId, bool ipv6, struct sockaddr_storage *dest); +- call(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic); ++ call(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitCall); + + virtual ~call(); + +- virtual bool process_incoming(char * msg); ++ virtual bool process_incoming(char * msg, struct sockaddr_storage *src = NULL); + virtual bool process_twinSippCom(char * msg); + + virtual bool run(); + /* Terminate this call, depending on action results and timewait. */ +- virtual bool terminate(CStat::E_Action reason); ++ virtual void terminate(CStat::E_Action reason); + virtual void tcpClose(); + + /* When should this call wake up? */ + virtual unsigned int wake(); +- virtual bool abortCall(); // call aborted with BYE or CANCEL ++ virtual bool abortCall(bool writeLog); // call aborted with BYE or CANCEL + virtual void abort(); + + /* Dump call info to error log. */ +@@ -93,15 +104,27 @@ public: + + void setLastMsg(const char *msg); + bool automaticResponseMode(T_AutoMode P_case, char* P_recv); ++ const char *getLastReceived() { return last_recv_msg; }; + + private: + /* This is the core constructor function. */ +- void init(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic); ++ void init(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitCall); ++ /* This this call for initialization? */ ++ bool initCall; + + struct sockaddr_storage call_peer; + + scenario *call_scenario; + unsigned int number; ++ ++public: ++ static int maxDynamicId; // max value for dynamicId; this value is reached ! ++ static int startDynamicId; // offset for first dynamicId FIXME:in CmdLine ++ static int stepDynamicId; // step of increment for dynamicId ++ static int dynamicId; // a counter for general use, incrementing by stepDynamicId starting at startDynamicId wrapping at maxDynamicId GLOBALY ++private: ++ ++ + unsigned int tdm_map_number; + + int msg_index; +@@ -112,6 +135,7 @@ private: + * are kept in this index.) */ + int last_send_index; + char * last_send_msg; ++ int last_send_len; + + /* How long until sending this message times out. */ + unsigned int send_timeout; +@@ -128,8 +152,8 @@ private: + * a cause relationship, so the next time this cookie will be recvd, + * we will retransmit the same message we sent this time */ + unsigned long recv_retrans_hash; +- unsigned int recv_retrans_recv_index; +- unsigned int recv_retrans_send_index; ++ int recv_retrans_recv_index; ++ int recv_retrans_send_index; + unsigned int recv_timeout; + + /* holds the route set */ +@@ -160,10 +184,9 @@ private: + unsigned int paused_until; + + unsigned long start_time; +- unsigned long long start_time_rtd[MAX_RTD_INFO_LENGTH]; ++ unsigned long long *start_time_rtd; ++ bool *rtd_done; + +- bool rtd_done[MAX_RTD_INFO_LENGTH]; +- + char *peer_tag; + + struct sipp_socket *call_remote_socket; +@@ -186,14 +209,16 @@ private: + VariableTable *M_callVariableTable; + + /* Our transaction IDs. */ +- char **txnID; ++ struct txnInstanceInfo *transactions; + + /* result of execute action */ + enum T_ActionResult + { + E_AR_NO_ERROR = 0, + E_AR_REGEXP_DOESNT_MATCH, ++ E_AR_REGEXP_SHOULDNT_MATCH, + E_AR_STOP_CALL, ++ E_AR_CONNECT_FAILED, + E_AR_HDR_NOT_FOUND + }; + +@@ -206,7 +231,8 @@ private: + void computeRouteSetAndRemoteTargetUri (char* rrList, char* contact, bool bRequestIncoming); + bool matches_scenario(unsigned int index, int reply_code, char * request, char * responsecseqmethod, char *txn); + +- T_ActionResult executeAction(char * msg, int scenarioIndex); ++ bool executeMessage(message *curmsg); ++ T_ActionResult executeAction(char * msg, message *message); + void extractSubMessage(char * msg, char * matchingString, char* result, bool case_indep, + int occurrence, bool headers); + bool rejectCall(); +@@ -215,9 +241,9 @@ private: + // P_index use for message index in scenario and ctrl of CRLF + // P_index = -2 No ctrl of CRLF + // P_index = -1 Add crlf to end of message +- char* createSendingMessage(SendingMessage *src, int P_index); ++ char* createSendingMessage(SendingMessage *src, int P_index, int *msgLen=NULL); + char* createSendingMessage(char * src, int P_index, bool skip_sanity = false); +- char* createSendingMessage(SendingMessage *src, int P_index, char *msg_buffer, int buflen); ++ char* createSendingMessage(SendingMessage *src, int P_index, char *msg_buffer, int buflen, int *msgLen=NULL); + + // method for the management of unexpected messages + bool checkInternalCmd(char* cmd); // check of specific internal command +@@ -228,12 +254,12 @@ private: + int search_index); // 3pcc extended mode:check if + // the twin message received + // comes from the expected sender +- void sendBuffer(char *buf); // send a message out of a scenario ++ void sendBuffer(char *buf, int len = 0); // send a message out of a scenario + // execution + + T_AutoMode checkAutomaticResponseMode(char * P_recv); + +- int sendCmdMessage(int index); // 3PCC ++ int sendCmdMessage(message *curmsg); // 3PCC + + int sendCmdBuffer(char* cmd); // for 3PCC, send a command out of a + // scenario execution +@@ -252,13 +278,13 @@ private: + /* rc == true means call not deleted by processing */ + bool next(); + bool process_unexpected(char * msg); +- void do_bookkeeping(int index); ++ void do_bookkeeping(message *curmsg); + + void extract_cseq_method (char* responseCseq, char* msg); + void extract_transaction (char* txn, char* msg); + +- int send_raw(char * msg, int index); +- char * send_scene(int index, int *send_status); ++ int send_raw(char * msg, int index, int len); ++ char * send_scene(int index, int *send_status, int *msgLen); + bool connect_socket_if_needed(); + + char * compute_cseq(char * src); +@@ -268,6 +294,7 @@ private: + char * get_header(char* message, char * name, bool content); + char * get_first_line(char* message); + char * get_last_request_uri(); ++ unsigned long hash(char * msg); + + typedef std::map file_line_map; + file_line_map *m_lineNumber; +@@ -292,8 +319,13 @@ private: + SSL_CTX *m_ctx_ssl ; + BIO *m_bio ; + #endif ++ ++ int _callDebug(char *fmt, ...); ++ char *debugBuffer; ++ int debugLength; + }; + ++ + /* Default Message Functions. */ + void init_default_messages(); + void free_default_messages(); +diff --git a/deadcall.cpp b/deadcall.cpp +index 67c57d0..3b4c08e 100644 +--- a/deadcall.cpp ++++ b/deadcall.cpp +@@ -62,7 +62,7 @@ deadcall::~deadcall() { + free(reason); + } + +-bool deadcall::process_incoming(char * msg) { ++bool deadcall::process_incoming(char * msg, struct sockaddr_storage *src) { + char buffer[MAX_HEADER_LEN]; + + CStat::globalStat(CStat::E_DEAD_CALL_MSGS); +@@ -97,10 +97,6 @@ bool deadcall::run() { + } + } + +-void deadcall::abort() { +- delete this; +-} +- + unsigned int deadcall::wake() { + return expiration; + } +diff --git a/deadcall.hpp b/deadcall.hpp +index 780507c..5492e4c 100644 +--- a/deadcall.hpp ++++ b/deadcall.hpp +@@ -5,7 +5,7 @@ public: + deadcall(char *id, char * reason); + ~deadcall(); + +- virtual bool process_incoming(char * msg); ++ virtual bool process_incoming(char * msg, struct sockaddr_storage *); + virtual bool process_twinSippCom(char * msg); + + virtual bool run(); +@@ -13,12 +13,10 @@ public: + /* When should this call wake up? */ + virtual unsigned int wake(); + +- virtual void abort(); +- + /* Dump call info to error log. */ + virtual void dump(); + + protected: +- int expiration; ++ unsigned long expiration; + char *reason; + }; +diff --git a/fortune.cpp b/fortune.cpp +new file mode 100644 +index 0000000..000e47e +--- /dev/null ++++ b/fortune.cpp +@@ -0,0 +1,85 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Author : Richard GAYRAUD - 04 Nov 2003 ++ * Olivier Jacques ++ * From Hewlett Packard Company. ++ * Charles P Wright from IBM Research ++ */ ++#include "sipp.hpp" ++ ++/* This is a fun sample of creating your own extensible keyword. */ ++int fortune(call *call, MessageComponent *comp, char *buf, int len) { ++ int pipes[2]; ++ char localbuf[SIPP_MAX_MSG_SIZE]; ++ char *p = localbuf; ++ int ret; ++ int written = 0; ++ ++ if (pipe(pipes) == -1) { ++ ERROR("Could not create pipes!\n"); ++ } ++ ++ switch (fork()) { ++ case -1: ++ ERROR("Fork failed: %s\n", strerror(errno)); ++ case 0: ++ /* We are the child. */ ++ close(pipes[0]); ++ dup2(pipes[1], fileno(stdout)); ++ dup2(pipes[1], fileno(stderr)); ++ close(fileno(stdin)); ++ system("/usr/bin/fortune"); ++ exit (127); ++ default: ++ /* We are the parent*/ ++ close(pipes[1]); ++ while ((ret = read(pipes[0], p, sizeof(localbuf) - (p - localbuf))) > 0) { ++ p += ret; ++ } ++ *p = '\0'; ++ close(pipes[0]); ++ ++ if (len > p - localbuf) { ++ len = p -localbuf; ++ } ++ ++ p = localbuf; ++ while(len-- > 0) { ++ if (*p == '\n') { ++ if (len < 3) { ++ break; ++ } ++ *buf++ = '\r'; ++ *buf++ = '\n'; ++ *buf++ = ' '; ++ written += 3; ++ p++; ++ } else { ++ *buf++ = *p++; ++ written++; ++ } ++ } ++ break; ++ } ++ ++ return written; ++} ++ ++/* On initialization we register our keywords. */ ++extern "C" int init(void) { ++ registerKeyword("fortune", fortune); ++ return 0; ++} +diff --git a/infile.cpp b/infile.cpp +index afdcef7..cbdbbfd 100644 +--- a/infile.cpp ++++ b/infile.cpp +@@ -130,6 +130,9 @@ FileContents::FileContents(const char *fileName) { + } + + delete inFile; ++ ++ indexMap = NULL; ++ indexField = -1; + } + + int FileContents::getLine(int line, char *dest, int len) { +@@ -275,3 +278,89 @@ void FileContents::dump(void) + WARNING("%s:%d reads [%s]", fileName, i, fileLines[i].c_str()); + } + } ++ ++void FileContents::index(int field) { ++ this->indexField = field; ++ ++ indexMap = new str_int_map; ++ for (int line = 0; line < numLines(); line++) { ++ reIndex(line); ++ } ++} ++ ++int FileContents::lookup(char *key) { ++ if (indexField == -1) { ++ ERROR("Invalid Index File: %s", fileName); ++ } ++ if (!indexMap) { ++ ERROR("Invalid Index File: %s", fileName); ++ } ++ ++ str_int_map::iterator index_it = indexMap->find(key); ++ if (index_it == indexMap->end()) { ++ return -1; ++ } ++ return index_it->second; ++} ++ ++ ++void FileContents::insert(char *value) { ++ if (printfFile) { ++ ERROR("Can not insert or replace into a printf file: %s", fileName); ++ } ++ fileLines.push_back(value); ++ realLinesInFile++; ++ numLinesInFile++; ++ if (indexField != -1) { ++ reIndex(realLinesInFile - 1); ++ } ++ char line[1024]; ++ getLine(realLinesInFile - 1, line, sizeof(line)); ++ char tmp[1024]; ++ getField(realLinesInFile - 1, 0, tmp, sizeof(tmp)); ++} ++ ++void FileContents::replace(int line, char *value) { ++ if (printfFile) { ++ ERROR("Can not insert or replace into a printf file: %s", fileName); ++ } ++ if (line >= realLinesInFile || line < 0) { ++ ERROR("Invalid line number (%d) for file: %s (%d lines)", line, fileName, realLinesInFile); ++ } ++ deIndex(line); ++ fileLines[line] = value; ++ reIndex(line); ++} ++ ++void FileContents::reIndex(int line) { ++ if (indexField == -1) { ++ return; ++ } ++ assert(line >= 0); ++ assert(line < realLinesInFile); ++ ++ char tmp[SIPP_MAX_MSG_SIZE]; ++ getField(line, indexField, tmp, SIPP_MAX_MSG_SIZE); ++ str_int_map::iterator index_it = indexMap->find(str_int_map::key_type(tmp)); ++ if (index_it != indexMap->end()) { ++ indexMap->erase(index_it); ++ } ++ indexMap->insert(pair(str_int_map::key_type(tmp), line)); ++} ++ ++void FileContents::deIndex(int line) { ++ if (indexField == -1) { ++ return; ++ } ++ assert(line >= 0); ++ assert(line < realLinesInFile); ++ ++ char tmp[SIPP_MAX_MSG_SIZE]; ++ getField(line, indexField, tmp, SIPP_MAX_MSG_SIZE); ++ str_int_map::iterator index_it = indexMap->find(str_int_map::key_type(tmp)); ++ if (index_it != indexMap->end()) { ++ if (index_it->second == line) { ++ indexMap->erase(index_it); ++ } ++ } ++} +diff --git a/infile.hpp b/infile.hpp +index fa6a9fb..894d305 100644 +--- a/infile.hpp ++++ b/infile.hpp +@@ -32,7 +32,14 @@ public: + int numLines(); + int nextLine(int userId); + void dump(); ++ void index(int field); ++ int lookup(char *key); ++ void insert(char *value); ++ void replace(int line, char *value); + private: ++ void reIndex(int line); ++ void deIndex(int line); ++ + typedef enum { + InputFileSequentialOrder = 0, + InputFileRandomOrder, +@@ -48,6 +55,9 @@ private: + int printfMultiple; + int numLinesInFile; + int realLinesInFile; ++ ++ int indexField; ++ str_int_map *indexMap; + }; + + #endif +diff --git a/listener.hpp b/listener.hpp +index d1d7746..ba89585 100644 +--- a/listener.hpp ++++ b/listener.hpp +@@ -34,7 +34,7 @@ public: + listener(char *id, bool listening); + virtual ~listener(); + char *getId(); +- virtual bool process_incoming(char * msg) = 0; ++ virtual bool process_incoming(char * msg, struct sockaddr_storage *src) = 0; + virtual bool process_twinSippCom(char * msg) = 0; + + protected: +diff --git a/message.cpp b/message.cpp +index 68f73d0..f94eed4 100644 +--- a/message.cpp ++++ b/message.cpp +@@ -33,6 +33,7 @@ + * Ben Evans from Open Cloud + * Marc Van Diest from Belgacom + * Stefan Esser ++ * Andy Aicken + */ + + #include "sipp.hpp" +@@ -43,6 +44,9 @@ struct KeywordMap { + MessageCompType type; + }; + ++typedef std::map kw_map; ++kw_map keyword_map; ++ + /* These keywords take no parameters. */ + struct KeywordMap SimpleKeywords[] = { + {"remote_ip", E_Message_Remote_IP }, +@@ -60,6 +64,7 @@ struct KeywordMap SimpleKeywords[] = { + {"media_port", E_Message_Media_Port }, + {"media_ip_type", E_Message_Media_IP_Type }, + {"call_number", E_Message_Call_Number }, ++ {"dynamic_id", E_Message_DynamicId }, // wrapping global counter + {"call_id", E_Message_Call_ID }, + {"cseq", E_Message_CSEQ }, + {"pid", E_Message_PID }, +@@ -69,7 +74,6 @@ struct KeywordMap SimpleKeywords[] = { + {"next_url", E_Message_Next_Url }, + {"len", E_Message_Len }, + {"peer_tag_param", E_Message_Peer_Tag_Param }, +- {"last_peer_tag_param", E_Message_Last_Peer_Tag_Param }, + {"last_Request_URI", E_Message_Last_Request_URI }, + {"last_cseq_number", E_Message_Last_CSeq_Number }, + {"last_message", E_Message_Last_Message }, +@@ -79,18 +83,17 @@ struct KeywordMap SimpleKeywords[] = { + {"users", E_Message_Users }, + {"userid", E_Message_UserID }, + {"timestamp", E_Message_Timestamp }, ++ {"sipp_version", E_Message_SippVersion }, + }; + + #define KEYWORD_SIZE 256 + + SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sanity) { +- char *osrc = src; ++ char *osrc = src; + char * literal; ++ int literalLen; + char * dest; + char * key; +- char * length_marker = NULL; +- int offset = 0; +- int len_offset = 0; + char current_line[MAX_HEADER_LEN]; + char * line_mark = NULL; + char * tsrc; +@@ -99,6 +102,7 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani + this->msg_scenario = msg_scenario; + + dest = literal = (char *)malloc(strlen(src) + num_cr + 1); ++ literalLen = 0; + + current_line[0] = '\0'; + *dest = 0; +@@ -134,22 +138,29 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani + *dest++ = *src++; + } else { + /* We have found a keyword, store the literal that we have been generating. */ +- *dest = '\0'; +- literal = (char *)realloc(literal, strlen(literal) + 1); +- if (!literal) { ERROR("Out of memory!"); } ++ literalLen = dest - literal; ++ if (literalLen) { ++ *dest = '\0'; ++ literal = (char *)realloc(literal, literalLen + 1); ++ if (!literal) { ERROR("Out of memory!"); } + +- MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent)); +- if (!newcomp) { ERROR("Out of memory!"); } ++ MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent)); ++ if (!newcomp) { ERROR("Out of memory!"); } + +- newcomp->type = E_Message_Literal; +- newcomp->literal = literal; +- messageComponents.push_back(newcomp); ++ newcomp->type = E_Message_Literal; ++ newcomp->literal = literal; ++ newcomp->literalLen = literalLen; // length without the terminator ++ messageComponents.push_back(newcomp); ++ } else { ++ free(literal); ++ } + + dest = literal = (char *)malloc(strlen(src) + num_cr + 1); ++ literalLen = 0; + *dest = '\0'; + + /* Now lets determine which keyword we have. */ +- newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent)); ++ MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent)); + if (!newcomp) { ERROR("Out of memory!"); } + + char keyword [KEYWORD_SIZE+1]; +@@ -161,6 +172,9 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani + do { + tsrc++; + } while(*tsrc && *tsrc != '\"'); ++ if (!*tsrc) { ++ break; ++ } + } + if (*tsrc == '[') + break; +@@ -206,8 +220,26 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani + } + } + ++ char *spc = NULL; ++ char ospc; ++ if ((spc = strchr(keyword, ' '))) { ++ ospc = *spc; ++ *spc = '\0'; ++ } ++ kw_map::iterator it = keyword_map.find(keyword); ++ if (spc) { ++ *spc = ospc; ++ } ++ ++ if (it != keyword_map.end()) { ++ newcomp->type = E_Message_Custom; ++ newcomp->comp_param.fxn = it->second; ++ messageComponents.push_back(newcomp); ++ continue; ++ } ++ + bool simple_keyword = false; +- for (int i = 0; i < sizeof(SimpleKeywords)/sizeof(SimpleKeywords[0]); i++) { ++ for (unsigned int i = 0; i < sizeof(SimpleKeywords)/sizeof(SimpleKeywords[0]); i++) { + if (!strcmp(keyword, SimpleKeywords[i].keyword)) { + newcomp->type = SimpleKeywords[i].type; + simple_keyword = true; +@@ -246,6 +278,17 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani + /* Turn this into a new message component. */ + newcomp->comp_param.field_param.line = new SendingMessage(msg_scenario, line, true); + } ++ } else if(!strncmp(keyword, "file", strlen("file"))) { ++ newcomp->type = E_Message_File; ++ ++ /* Parse out the interesting things like file and number. */ ++ char fileName[KEYWORD_SIZE]; ++ getKeywordParam(keyword, "name=", fileName); ++ if (fileName[0] == '\0') { ++ ERROR("No name specified for 'file' keyword!\n"); ++ } ++ /* Turn this into a new message component. */ ++ newcomp->comp_param.filename = new SendingMessage(msg_scenario, fileName, true); + } else if(*keyword == '$') { + newcomp->type = E_Message_Variable; + if (!msg_scenario) { +@@ -264,6 +307,7 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani + getKeywordParam(keyword, "variable=", varName); + + newcomp->literal = strdup(filltext); ++ newcomp->literalLen = strlen(newcomp->literal); + if (!msg_scenario) { + ERROR("SendingMessage with variable usage outside of scenario!"); + } +@@ -271,6 +315,7 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani + } else if(!strncmp(keyword, "last_", strlen("last_"))) { + newcomp->type = E_Message_Last_Header; + newcomp->literal = strdup(keyword + strlen("last_")); ++ newcomp->literalLen = strlen(newcomp->literal); + } else if(!strncmp(keyword, "authentication", strlen("authentication"))) { + parseAuthenticationKeyword(msg_scenario, newcomp, keyword); + } +@@ -297,6 +342,7 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani + if(!strcmp(keyword, msg1)) { + newcomp->type = E_Message_Literal; + newcomp->literal = strdup(msg2); ++ newcomp->literalLen = strlen(newcomp->literal); + break; + } + ++i; +@@ -312,7 +358,8 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani + } + if (literal[0]) { + *dest++ = '\0'; +- literal = (char *)realloc(literal, strlen(literal) + 1); ++ literalLen = dest - literal; ++ literal = (char *)realloc(literal, literalLen); + if (!literal) { ERROR("Out of memory!"); } + + MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent)); +@@ -320,6 +367,7 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani + + newcomp->type = E_Message_Literal; + newcomp->literal = literal; ++ newcomp->literalLen = literalLen-1; + messageComponents.push_back(newcomp); + } else { + free(literal); +@@ -436,7 +484,7 @@ void SendingMessage::getKeywordParam(char * src, char * param, char * output) + + len = 0; + key = NULL; +- if(tmp = strstr(src, param)) { ++ if ((tmp = strstr(src, param))) { + tmp += strlen(param); + key = tmp; + if ((*key == '0') && (*(key+1) == 'x')) { +@@ -532,3 +580,12 @@ int SendingMessage::numComponents() { + struct MessageComponent *SendingMessage::getComponent(int i) { + return messageComponents[i]; + } ++ ++/* This is very simplistic and does not yet allow any arguments, but it is a start. */ ++int registerKeyword(char *keyword, customKeyword fxn) { ++ if (keyword_map.find(keyword) != keyword_map.end()) { ++ ERROR("Can not register keyword '%s', already registered!\n", keyword); ++ } ++ keyword_map[keyword] = fxn; ++ return 0; ++} +diff --git a/message.hpp b/message.hpp +index e1225ae..65dd9ac 100644 +--- a/message.hpp ++++ b/message.hpp +@@ -32,6 +32,7 @@ + * Jan Andres from Freenet + * Ben Evans from Open Cloud + * Marc Van Diest from Belgacom ++ * Andy Aicken + */ + + #ifndef __MESSAGE__ +@@ -58,6 +59,7 @@ typedef enum { + E_Message_Media_Port, + E_Message_Media_IP_Type, + E_Message_Call_Number, ++ E_Message_DynamicId, // general usage, global, autoincrementing and wrapping counter + E_Message_Call_ID, + E_Message_CSEQ, + E_Message_PID, +@@ -67,7 +69,6 @@ typedef enum { + E_Message_Next_Url, + E_Message_Len, + E_Message_Peer_Tag_Param, +- E_Message_Last_Peer_Tag_Param, + E_Message_Routes, + E_Message_Variable, + E_Message_Fill, +@@ -82,6 +83,9 @@ typedef enum { + E_Message_Users, + E_Message_UserID, + E_Message_Timestamp, ++ E_Message_SippVersion, ++ E_Message_File, ++ E_Message_Custom, + } MessageCompType; + + class SendingMessage { +@@ -119,10 +123,17 @@ class SendingMessage { + static void getKeywordParam(char * src, char * param, char * output); + }; + ++/* Custom Keyword Function Type. */ ++struct MessageComponent; ++class call; ++typedef int (*customKeyword)(call *, struct MessageComponent *, char *, int); ++/* Custom Keyword Registration Function. */ ++int registerKeyword(char *keyword, customKeyword fxn); + + struct MessageComponent { + MessageCompType type; + char *literal; ++ int literalLen; + int offset; + int varId; + union u { +@@ -140,6 +151,8 @@ struct MessageComponent { + int field; + SendingMessage *line; + } field_param; ++ SendingMessage *filename; ++ customKeyword fxn; + } comp_param; + }; + +diff --git a/opentask.cpp b/opentask.cpp +new file mode 100644 +index 0000000..fda9f2f +--- /dev/null ++++ b/opentask.cpp +@@ -0,0 +1,257 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Author : Richard GAYRAUD - 04 Nov 2003 ++ * Marc LAMBERTON ++ * Olivier JACQUES ++ * Herve PELLAN ++ * David MANSUTTI ++ * Francois-Xavier Kowalski ++ * Gerard Lyonnaz ++ * From Hewlett Packard Company. ++ * F. Tarek Rogers ++ * Peter Higginson ++ * Vincent Luba ++ * Shriram Natarajan ++ * Guillaume Teissier from FTR&D ++ * Clement Chen ++ * Wolfgang Beck ++ * Charles P Wright from IBM Research ++ */ ++#include "sipp.hpp" ++ ++class opentask *opentask::instance = NULL; ++unsigned long opentask::calls_since_last_rate_change = 0; ++unsigned long opentask::last_rate_change_time = 0; ++ ++void opentask::initialize() { ++ assert(instance == NULL); ++ instance = new opentask(); ++} ++ ++opentask::opentask() { ++ setRunning(); ++} ++ ++opentask::~opentask() { ++ instance = NULL; ++} ++ ++void opentask::dump() { ++ WARNING("Uniform rate call generation task: %d", rate); ++} ++ ++unsigned int opentask::wake() { ++ if (paused) { ++ return 0; ++ } else if (users >= 0) { ++ /* We need to wait until another call is terminated. */ ++ return 0; ++ } else { ++ /* We need to compute when the next call is going to be opened. */ ++ return (unsigned long) (last_rate_change_time + ((calls_since_last_rate_change + 1) / (rate/rate_period_ms))); ++ } ++} ++ ++bool opentask::run() { ++ int calls_to_open = 0; ++ ++ if (quitting) { ++ delete this; ++ return false; ++ } ++ ++ if (paused) { ++ setPaused(); ++ return true; ++ } ++ ++ long l=0; ++ unsigned long long current_calls = main_scenario->stats->GetStat(CStat::CPT_C_CurrentCall); ++ unsigned long long total_calls = main_scenario->stats->GetStat(CStat::CPT_C_IncomingCallCreated) + main_scenario->stats->GetStat(CStat::CPT_C_OutgoingCallCreated); ++ ++ if (users >= 0) { ++ calls_to_open = ((l = (users - current_calls)) > 0) ? l : 0; ++ } else { ++ calls_to_open = (unsigned int) ++ ((l=(long)floor(((clock_tick - last_rate_change_time) * rate/rate_period_ms) ++ - calls_since_last_rate_change))>0?l:0); ++ } ++ ++ if (total_calls + calls_to_open > stop_after) { ++ calls_to_open = stop_after - total_calls; ++ } ++ ++ if (open_calls_allowed && (current_calls + calls_to_open > open_calls_allowed)) { ++ calls_to_open = open_calls_allowed - current_calls; ++ } ++ ++ if (calls_to_open < 0) { ++ calls_to_open = 0; ++ } ++ ++ unsigned int start_clock = getmilliseconds(); ++ ++ ++ while(calls_to_open--) ++ { ++ /* Associate a user with this call, if we are in users mode. */ ++ int userid = 0; ++ if (users >= 0) { ++ userid = freeUsers.back(); ++ freeUsers.pop_back(); ++ } ++ ++ // adding a new OUTGOING CALL ++ main_scenario->stats->computeStat(CStat::E_CREATE_OUTGOING_CALL); ++ call * call_ptr = call::add_call(userid, is_ipv6, use_remote_sending_addr ? &remote_sending_sockaddr : &remote_sockaddr); ++ if(!call_ptr) { ++ ERROR("Out of memory allocating call!"); ++ } ++ ++ calls_since_last_rate_change++; ++ ++ outbound_congestion = false; ++ ++ if (!multisocket) { ++ switch(transport) { ++ case T_UDP: ++ call_ptr->associate_socket(main_socket); ++ main_socket->ss_count++; ++ break; ++ case T_TCP: ++ case T_TLS: ++ call_ptr->associate_socket(tcp_multiplex); ++ tcp_multiplex->ss_count++; ++ break; ++ } ++ } ++ if (getmilliseconds() > start_clock) { ++ break; ++ } ++ } ++ ++ /* We can pause. */ ++ if (calls_to_open <= 0) { ++ setPaused(); ++ } else { ++ /* Stay running. */ ++ } ++ ++ // Quit after asked number of calls is reached ++ if(total_calls >= stop_after) { ++ quitting = 1; ++ return false; ++ } ++ ++ return true; ++} ++ ++void opentask::set_paused(bool new_paused) ++{ ++ if (!instance) { ++ /* Doesn't do anything, we must be in server mode. */ ++ return; ++ } ++ if (new_paused) { ++ instance->setPaused(); ++ } else { ++ instance->setRunning(); ++ if (users >= 0) { ++ set_users(users); ++ } else { ++ set_rate(rate); ++ } ++ } ++ paused = new_paused; ++} ++ ++void opentask::set_rate(double new_rate) ++{ ++ if (!instance) { ++ /* Doesn't do anything, we must be in server mode. */ ++ } ++ ++ rate = new_rate; ++ if(rate < 0) { ++ rate = 0; ++ } ++ ++ last_rate_change_time = getmilliseconds(); ++ calls_since_last_rate_change = 0; ++ ++ if(!open_calls_user_setting) { ++ ++ int call_duration_min = main_scenario->duration; ++ ++ if(duration > call_duration_min) call_duration_min = duration; ++ ++ if(call_duration_min < 1000) call_duration_min = 1000; ++ ++ open_calls_allowed = (int)((3.0 * rate * call_duration_min) / (double)rate_period_ms); ++ if(!open_calls_allowed) { ++ open_calls_allowed = 1; ++ } ++ } ++} ++ ++void opentask::set_users(int new_users) ++{ ++ if (!instance) { ++ /* Doesn't do anything, we must be in server mode. */ ++ return; ++ } ++ ++ if (new_users < 0) { ++ new_users = 0; ++ } ++ assert(users >= 0); ++ ++ if (users < new_users ) { ++ while (users < new_users) { ++ int userid; ++ if (!retiredUsers.empty()) { ++ userid = retiredUsers.back(); ++ retiredUsers.pop_back(); ++ } else { ++ userid = users + 1; ++ userVarMap[userid] = new VariableTable(userVariables); ++ } ++ freeUsers.push_front(userid); ++ users++; ++ } ++ } ++ ++ users = open_calls_allowed = new_users; ++ ++ last_rate_change_time = clock_tick; ++ calls_since_last_rate_change = 0; ++ ++ assert(open_calls_user_setting); ++ ++ instance->setRunning(); ++} ++ ++void opentask::freeUser(int userId) { ++ if (main_scenario->stats->GetStat(CStat::CPT_C_CurrentCall) > open_calls_allowed) { ++ retiredUsers.push_front(userId); ++ } else { ++ freeUsers.push_front(userId); ++ /* Wake up the call creation thread. */ ++ if (instance) { ++ instance->setRunning(); ++ } ++ } ++} +diff --git a/opentask.hpp b/opentask.hpp +new file mode 100644 +index 0000000..3e70fa3 +--- /dev/null ++++ b/opentask.hpp +@@ -0,0 +1,62 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Author : Richard GAYRAUD - 04 Nov 2003 ++ * Marc LAMBERTON ++ * Olivier JACQUES ++ * Herve PELLAN ++ * David MANSUTTI ++ * Francois-Xavier Kowalski ++ * Gerard Lyonnaz ++ * From Hewlett Packard Company. ++ * F. Tarek Rogers ++ * Peter Higginson ++ * Vincent Luba ++ * Shriram Natarajan ++ * Guillaume Teissier from FTR&D ++ * Clement Chen ++ * Wolfgang Beck ++ * Charles P Wright from IBM Research ++ */ ++ ++#ifndef OPENTASK_HPP ++#define OPENTASK_HPP ++ ++#include "task.hpp" ++ ++class opentask : public task { ++public: ++ static void initialize(); ++ static void set_rate(double new_rate); ++ static void set_users(int new_users); ++ static void set_paused(bool paused); ++ static void freeUser(int userId); ++ ++ bool run(); ++ void dump(); ++ ++protected: ++ unsigned int wake(); ++ ++private: ++ opentask(); ++ virtual ~opentask(); ++ ++ static class opentask *instance; ++ static unsigned long calls_since_last_rate_change; ++ static unsigned long last_rate_change_time; ++}; ++ ++#endif +diff --git a/reporttask.cpp b/reporttask.cpp +new file mode 100644 +index 0000000..f89393a +--- /dev/null ++++ b/reporttask.cpp +@@ -0,0 +1,117 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Author : Richard GAYRAUD - 04 Nov 2003 ++ * Marc LAMBERTON ++ * Olivier JACQUES ++ * Herve PELLAN ++ * David MANSUTTI ++ * Francois-Xavier Kowalski ++ * Gerard Lyonnaz ++ * From Hewlett Packard Company. ++ * F. Tarek Rogers ++ * Peter Higginson ++ * Vincent Luba ++ * Shriram Natarajan ++ * Guillaume Teissier from FTR&D ++ * Clement Chen ++ * Wolfgang Beck ++ * Charles P Wright from IBM Research ++ */ ++#include "sipp.hpp" ++ ++class stattask *stattask::instance = NULL; ++class screentask *screentask::instance = NULL; ++ ++void stattask::initialize() { ++ assert(instance == NULL); ++ if (dumpInFile || useCountf || rate_increase) { ++ instance = new stattask(); ++ } ++} ++ ++void screentask::initialize() { ++ assert(instance == NULL); ++ if (report_freq) { ++ instance = new screentask(); ++ } ++} ++ ++void stattask::dump() { ++ WARNING("Statistics reporting task."); ++} ++void screentask::dump() { ++ WARNING("Screen update task."); ++} ++ ++void screentask::report(bool last) { ++ print_statistics(last); ++ display_scenario->stats->computeStat(CStat::E_RESET_PD_COUNTERS); ++ last_report_time = getmilliseconds(); ++ scheduling_loops = 0; ++} ++ ++bool screentask::run() { ++ if (quitting > 11) { ++ delete this; ++ return false; ++ } ++ ++ if (getmilliseconds() - last_report_time >= report_freq) { ++ report(false); ++ } ++ ++ setPaused(); ++ return true; ++} ++ ++unsigned int screentask::wake() { ++ return last_report_time + report_freq; ++} ++ ++void stattask::report() { ++ if(dumpInFile) { ++ main_scenario->stats->dumpData(); ++ } ++ if (useCountf) { ++ print_count_file(countf, 0); ++ } ++ ++ main_scenario->stats->computeStat(CStat::E_RESET_PL_COUNTERS); ++ last_dump_time = clock_tick; ++} ++ ++bool stattask::run() { ++ /* Statistics Logs. */ ++ if((getmilliseconds() - last_dump_time) >= report_freq_dumpLog) { ++ if (rate_increase) { ++ rate += rate_increase; ++ if (rate_max && (rate > rate_max)) { ++ rate = rate_max; ++ if (rate_quit) { ++ quitting += 10; ++ } ++ } ++ opentask::set_rate(rate); ++ } ++ report(); ++ } ++ setPaused(); ++ return true; ++} ++ ++unsigned int stattask::wake() { ++ return last_dump_time + report_freq_dumpLog; ++} +diff --git a/reporttask.hpp b/reporttask.hpp +new file mode 100644 +index 0000000..0e834be +--- /dev/null ++++ b/reporttask.hpp +@@ -0,0 +1,61 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Author : Richard GAYRAUD - 04 Nov 2003 ++ * Marc LAMBERTON ++ * Olivier JACQUES ++ * Herve PELLAN ++ * David MANSUTTI ++ * Francois-Xavier Kowalski ++ * Gerard Lyonnaz ++ * From Hewlett Packard Company. ++ * F. Tarek Rogers ++ * Peter Higginson ++ * Vincent Luba ++ * Shriram Natarajan ++ * Guillaume Teissier from FTR&D ++ * Clement Chen ++ * Wolfgang Beck ++ * Charles P Wright from IBM Research ++ */ ++ ++#ifndef REPORTTASK_HPP ++#define REPORTTASK_HPP ++ ++#include "task.hpp" ++ ++class screentask : public task { ++public: ++ unsigned int wake(); ++ static void report(bool last); ++ static void initialize(); ++ bool run(); ++ void dump(); ++private: ++ static class screentask *instance; ++}; ++ ++class stattask : public task { ++public: ++ unsigned int wake(); ++ static void report(); ++ static void initialize(); ++ bool run(); ++ void dump(); ++private: ++ static class stattask *instance; ++}; ++ ++#endif +diff --git a/scenario.cpp b/scenario.cpp +index bb6433b..d65cbf9 100644 +--- a/scenario.cpp ++++ b/scenario.cpp +@@ -24,6 +24,7 @@ + * Wolfgang Beck + * Marc Van Diest from Belgacom + * Charles P. Wright from IBM Research ++ * Michael Stovenour + */ + + #include +@@ -36,9 +37,10 @@ + + /************************ Class Constructor *************************/ + +-message::message() ++message::message(int index, const char *desc) + { +- //ugly memset(this, 0, sizeof(message)); ++ this->index = index; ++ this->desc = desc; + pause_distribution = NULL; + pause_variable = -1; + pause_desc = NULL; +@@ -68,9 +70,13 @@ message::message() + hide = 0; + display_str = NULL; + test = -1; ++ condexec = -1; ++ condexec_inverse = false; + chance = 0;/* meaning always */ + next = -1; ++ nextLabel = NULL; + on_timeout = -1; ++ onTimeoutLabel = NULL; + timewait = false; + + /* 3pcc extended mode */ +@@ -100,6 +106,7 @@ message::message() + /* How to match responses to this message. */ + start_txn = 0; + response_txn = 0; ++ ack_txn = 0; + recv_response_for_cseq_method_list = NULL; + } + +@@ -107,73 +114,45 @@ message::~message() + { + if(M_actions != NULL) + delete(M_actions); +- M_actions = NULL; + + if(send_scheme != NULL) + delete send_scheme; +- send_scheme = NULL; + + if(recv_request != NULL) + free (recv_request); +- recv_request = NULL; + + if(regexp_compile != NULL) + regfree(regexp_compile); + free(regexp_compile); +- regexp_compile = NULL; +- +- if(peer_dest != NULL) +- free (peer_dest); +- peer_dest = NULL; + +- if(peer_src != NULL) +- delete (peer_src); +- peer_src = NULL; +- +- if(pause_desc != NULL) +- free(pause_desc); +- pause_desc = NULL; + + if (pause_distribution) { + delete pause_distribution; + } + +- if(display_str != NULL) +- free(display_str); +- display_str = NULL; +- + if(M_sendCmdData != NULL) + delete M_sendCmdData; +- M_sendCmdData = NULL; + ++ free(display_str); ++ free(peer_dest); ++ free(peer_src); ++ free(pause_desc); + free(recv_response_for_cseq_method_list); + } + +-void scenario::expand(int length) { +- assert(length >= this->length); +- if (length == this->length) { +- return; +- } +- +- messages = (message **)realloc(messages, sizeof(message *) * length); +- if (!messages) { +- ERROR("Out of memory allocating scenario messages."); +- } +- +- for (int i = this->length; i < length; i++) { +- messages[i] = NULL; +- } +-} +- + /******** Global variables which compose the scenario file **********/ + + scenario *main_scenario; + scenario *ooc_scenario; + scenario *display_scenario; + +-int toolMode = MODE_CLIENT; +-bool rtd_stopped[MAX_RTD_INFO_LENGTH]; +-bool rtd_started[MAX_RTD_INFO_LENGTH]; ++/* This mode setting refers to whether we open calls autonomously (MODE_CLIENT) ++ * or in response to requests (MODE_SERVER). */ ++int creationMode = MODE_CLIENT; ++/* Send mode. Do we send to a fixed address or to the last one we got. */ ++int sendMode = MODE_CLIENT; ++/* This describes what our 3PCC behavior is. */ ++int thirdPartyMode = MODE_3PCC_NONE; + + /*************** Helper functions for various types *****************/ + long get_long(const char *ptr, const char *what) { +@@ -338,7 +317,7 @@ double xp_get_bool(const char *name, const char *what, bool defval) { + return xp_get_bool(name, what); + } + +-int scenario::get_txn(const char *txnName, const char *what, bool start) { ++int scenario::get_txn(const char *txnName, const char *what, bool start, bool isInvite, bool isAck) { + /* Check the name's validity. */ + if (txnName[0] == '\0') { + ERROR("Variable names may not be empty for %s\n", what); +@@ -351,26 +330,42 @@ int scenario::get_txn(const char *txnName, const char *what, bool start) { + str_int_map::iterator txn_it = txnMap.find(txnName); + if (txn_it != txnMap.end()) { + if (start) { +- txnStarted[txn_it->second]++; ++ /* We need to fill in the invite field. */ ++ transactions[txn_it->second - 1].started++; ++ } else if (isAck) { ++ transactions[txn_it->second - 1].acks++; + } else { +- txnResponses[txn_it->second]++; ++ transactions[txn_it->second - 1].responses++; + } + return txn_it->second; + } + + /* Assign this variable the next slot. */ +- int txnNum = ++maxTxnUsed; ++ struct txnControlInfo transaction; + +- txnMap[txnName] = txnNum; +- txnRevMap[txnNum] = strdup(txnName); ++ transaction.name = strdup(txnName); + if (start) { +- txnStarted[txnNum] = 1; +- txnResponses[txnNum] = 0; ++ transaction.started = 1; ++ transaction.responses = 0; ++ transaction.acks = 0; ++ transaction.isInvite = isInvite; ++ } else if (isAck) { ++ /* Does not start or respond to this txn. */ ++ transaction.started = 0; ++ transaction.responses = 0; ++ transaction.acks = 1; ++ transaction.isInvite = false; + } else { +- txnStarted[txnNum] = 0; +- txnResponses[txnNum] = 1; ++ transaction.started = 0; ++ transaction.responses = 1; ++ transaction.acks = 0; ++ transaction.isInvite = false; + } + ++ transactions.push_back(transaction); ++ int txnNum = transactions.size(); ++ txnMap[txnName] = txnNum; ++ + return txnNum; + } + +@@ -392,7 +387,6 @@ int scenario::get_var(const char *varName, const char *what) { + + int scenario::xp_get_var(const char *name, const char *what) { + char *ptr; +- char *helptext; + + if (!(ptr = xp_get_value(name))) { + ERROR("%s is missing the required '%s' variable parameter.", what, name); +@@ -401,9 +395,29 @@ int scenario::xp_get_var(const char *name, const char *what) { + return get_var(ptr, what); + } + ++int xp_get_optional(const char *name, const char *what) { ++ char *ptr = xp_get_value(name); ++ ++ if (!(ptr = xp_get_value(name))) { ++ return OPTIONAL_FALSE; ++ } ++ ++ if(!strcmp(ptr, "true")) { ++ return OPTIONAL_TRUE; ++ } else if(!strcmp(ptr, "global")) { ++ return OPTIONAL_GLOBAL; ++ } else if(!strcmp(ptr, "false")) { ++ return OPTIONAL_FALSE; ++ } else { ++ ERROR("Could not understand optional value for %s: %s", what, ptr); ++ } ++ ++ return OPTIONAL_FALSE; ++} ++ ++ + int scenario::xp_get_var(const char *name, const char *what, int defval) { + char *ptr; +- char *helptext; + + if (!(ptr = xp_get_value(name))) { + return defval; +@@ -495,93 +509,72 @@ char *double_time_string(double ms) { + + /* For backwards compatibility, we assign "true" to slot 1, false to 0, and + * allow other valid integers. */ +-int get_rtd(const char *ptr) { +- char *endptr; +- int ret; +- +- if(!strcmp(ptr, (char *)"true")) +- return 1; ++int scenario::get_rtd(const char *ptr, bool start) { + if(!strcmp(ptr, (char *)"false")) + return 0; + +- ret = strtol(ptr, &endptr, 0); +- if (*endptr) { +- ERROR("rtd \"%s\" is not a valid integer!\n", ptr); +- } +- +- if (ret > MAX_RTD_INFO_LENGTH) { +- ERROR("rtd %d exceeds MAX_RTD_INFO_LENGTH %d!\n", ret, MAX_RTD_INFO_LENGTH); +- } ++ if(!strcmp(ptr, (char *)"true")) ++ return stats->findRtd("1", start); + +- return ret; ++ return stats->findRtd(ptr, start); + } + + /* Get a counter */ +-long get_counter(const char *ptr, const char *what) { +- long ret; +- +- ret = get_long(ptr, what); +- if (ret < 1 || ret > MAX_COUNTER) { +- ERROR("Counter %ld exceeds MAX_COUNTER %d!\n", ret, MAX_COUNTER); ++int scenario::get_counter(const char *ptr, const char *what) { ++ /* Check the name's validity. */ ++ if (ptr[0] == '\0') { ++ ERROR("Counter names names may not be empty for %s\n", what); ++ } ++ if (strcspn(ptr, "$,") != strlen(ptr)) { ++ ERROR("Counter names may not contain $ or , for %s\n", what); + } + +- return ret; ++ return stats->findCounter(ptr, true); + } + + + /* Some validation functions. */ + +-/* If you start an RTD, then you should be interested in collecting statistics for it. */ +-void scenario::validate_rtds() { +- for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) { +- if (rtd_started[i] && !rtd_stopped[i]) { +- ERROR("You have started Response Time Duration %d, but have never stopped it!", i + 1); +- } +- } +-} +- + void scenario::validate_variable_usage() { + allocVars->validate(); + } + + void scenario::validate_txn_usage() { +- for (int i = 1; i <= maxTxnUsed; i++) { +- if(txnStarted[i] == 0) { +- ERROR("Transaction %s is never started!\n", txnRevMap[i]); +- } else if(txnResponses[i] == 0) { +- ERROR("Transaction %s has no responses defined!\n", txnRevMap[i]); ++ for (unsigned int i = 0; i < transactions.size(); i++) { ++ if(transactions[i].started == 0) { ++ ERROR("Transaction %s is never started!\n", transactions[i].name); ++ } else if(transactions[i].responses == 0) { ++ ERROR("Transaction %s has no responses defined!\n", transactions[i].name); ++ } ++ if (transactions[i].isInvite && transactions[i].acks == 0) { ++ ERROR("Transaction %s is an INVITE transaction without an ACK!\n", transactions[i].name); ++ } ++ if (!transactions[i].isInvite && (transactions[i].acks > 0)) { ++ ERROR("Transaction %s is a non-INVITE transaction with an ACK!\n", transactions[i].name); + } + } + } + + /* Apply the next and ontimeout labels according to our map. */ +-void scenario::apply_labels() { +- for (int i = 0; i <= length; i++) { +- int_str_map::iterator it; +- if ((it = nextLabels.find(i)) != nextLabels.end()) { +- str_int_map::iterator label_it = labelMap.find(it->second); +- if (label_it == labelMap.end()) { +- ERROR("The label '%s' was not defined (index %d, next attribute)\n", it->second, i); ++void scenario::apply_labels(msgvec v, str_int_map labels) { ++ for (unsigned int i = 0; i < v.size(); i++) { ++ if (v[i]->nextLabel) { ++ str_int_map::iterator label_it = labels.find(v[i]->nextLabel); ++ if (label_it == labels.end()) { ++ ERROR("The label '%s' was not defined (index %d, next attribute)\n", v[i]->nextLabel, i); + } +- messages[i]->next = label_it->second; ++ v[i]->next = label_it->second; + } +- if ((it = ontimeoutLabels.find(i)) != ontimeoutLabels.end()) { +- str_int_map::iterator label_it = labelMap.find(it->second); +- if (label_it == labelMap.end()) { +- ERROR("The label '%s' was not defined (index %d, ontimeout attribute)\n", it->second, i); ++ if (v[i]->onTimeoutLabel) { ++ str_int_map::iterator label_it = labels.find(v[i]->onTimeoutLabel); ++ if (label_it == labels.end()) { ++ ERROR("The label '%s' was not defined (index %d, ontimeout attribute)\n", v[i]->onTimeoutLabel, i); + } +- messages[i]->on_timeout = label_it->second; ++ v[i]->on_timeout = label_it->second; + } + } + } + +-void scenario::init_rtds() +-{ +- for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) { +- rtd_started[i] = rtd_stopped[i] = false; +- } +-} +- + int get_cr_number(char *src) + { + int res=0; +@@ -610,7 +603,7 @@ char *clean_cdata(char *ptr, int *removed_crlf = NULL) { + (*ptr == '\t') || + (*ptr == '\n'))) { + if(*ptr == '\n' && removed_crlf) { +- *removed_crlf++; ++ (*removed_crlf)++; + } + *ptr-- = 0; + } +@@ -622,16 +615,16 @@ char *clean_cdata(char *ptr, int *removed_crlf = NULL) { + if(ptr == msg) { + ERROR("Empty cdata in xml scenario file"); + } +- while(ptr = strstr(msg, "\n ")) { ++ while ((ptr = strstr(msg, "\n "))) { + memmove(ptr + 1, ptr + 2, strlen(ptr) - 1); + } +- while(ptr = strstr(msg, " \n")) { ++ while ((ptr = strstr(msg, " \n"))) { + memmove(ptr, ptr + 1, strlen(ptr)); + } +- while(ptr = strstr(msg, "\n\t")) { ++ while ((ptr = strstr(msg, "\n\t"))) { + memmove(ptr + 1, ptr + 2, strlen(ptr) - 1); + } +- while(ptr = strstr(msg, "\t\n")) { ++ while ((ptr = strstr(msg, "\t\n"))) { + memmove(ptr, ptr + 1, strlen(ptr)); + } + +@@ -642,16 +635,23 @@ char *clean_cdata(char *ptr, int *removed_crlf = NULL) { + + /********************** Scenario File analyser **********************/ + ++void scenario::checkOptionalRecv(char *elem, unsigned int scenario_file_cursor) { ++ if (last_recv_optional) { ++ ERROR(" before <%s> sequence without a mandatory message. Please remove one 'optional=true' (element %d).", elem, scenario_file_cursor); ++ } ++ last_recv_optional = false; ++} ++ + scenario::scenario(char * filename, int deflt) + { + char * elem; + char *method_list = NULL; + unsigned int scenario_file_cursor = 0; + int L_content_length = 0 ; +- unsigned int recv_count = 0; +- unsigned int recv_opt_count = 0; + char * peer; + ++ last_recv_optional = false; ++ + if(filename) { + if(!xp_set_xml_buffer_from_file(filename)) { + ERROR("Unable to load or parse '%s' xml scenario file", filename); +@@ -665,7 +665,6 @@ scenario::scenario(char * filename, int deflt) + stats = new CStat(); + allocVars = new AllocVariableTable(userVariables); + +- init_rtds(); + hidedefault = false; + + elem = xp_open_element(0); +@@ -682,26 +681,25 @@ scenario::scenario(char * filename, int deflt) + name = strdup(""); + } + +- length = 0; +- messages = NULL; + duration = 0; +- maxTxnUsed = 0; + found_timewait = false; + + scenario_file_cursor = 0; + +- while(elem = xp_open_element(scenario_file_cursor)) { ++ while ((elem = xp_open_element(scenario_file_cursor))) { + char * ptr; + scenario_file_cursor ++; + + if(!strcmp(elem, "CallLengthRepartition")) { +- ptr = xp_get_value((char *)"value"); ++ ptr = xp_get_string("value", "CallLengthRepartition"); + stats->setRepartitionCallLength(ptr); ++ free(ptr); + } else if(!strcmp(elem, "ResponseTimeRepartition")) { +- ptr = xp_get_value((char *)"value"); ++ ptr = xp_get_string("value", "ResponseTimeRepartition"); + stats->setRepartitionResponseTime(ptr); ++ free(ptr); + } else if(!strcmp(elem, "Global")) { +- ptr = xp_get_value((char *)"variables"); ++ ptr = xp_get_string("variables", "Global"); + + char ** currentTabVarName = NULL; + int currentNbVarNames; +@@ -711,8 +709,9 @@ scenario::scenario(char * filename, int deflt) + globalVariables->find(currentTabVarName[i], true); + } + freeStringTable(currentTabVarName, currentNbVarNames); ++ free(ptr); + } else if(!strcmp(elem, "User")) { +- ptr = xp_get_value((char *)"variables"); ++ ptr = xp_get_string("variables", "User"); + + char ** currentTabVarName = NULL; + int currentNbVarNames; +@@ -722,6 +721,22 @@ scenario::scenario(char * filename, int deflt) + userVariables->find(currentTabVarName[i], true); + } + freeStringTable(currentTabVarName, currentNbVarNames); ++ free(ptr); ++ } else if(!strcmp(elem, "Reference")) { ++ ptr = xp_get_string("variables", "Reference"); ++ ++ char ** currentTabVarName = NULL; ++ int currentNbVarNames; ++ ++ createStringTable(ptr, ¤tTabVarName, ¤tNbVarNames); ++ for (int i = 0; i < currentNbVarNames; i++) { ++ int id = allocVars->find(currentTabVarName[i], false); ++ if (id == -1) { ++ ERROR("Could not reference non-existant variable '%s'", currentTabVarName[i]); ++ } ++ } ++ freeStringTable(currentTabVarName, currentNbVarNames); ++ free(ptr); + } else if(!strcmp(elem, "DefaultMessage")) { + char *id = xp_get_string("id", "DefaultMessage"); + if(!(ptr = xp_get_cdata())) { +@@ -732,29 +747,45 @@ scenario::scenario(char * filename, int deflt) + free(id); + /* XXX: This should really be per scenario. */ + } else if(!strcmp(elem, "label")) { +- ptr = xp_get_value((char *)"id"); ++ ptr = xp_get_string("id", "label"); + if (labelMap.find(ptr) != labelMap.end()) { + ERROR("The label name '%s' is used twice.", ptr); + } +- labelMap[ptr] = length; ++ labelMap[ptr] = messages.size(); ++ free(ptr); ++ } else if (!strcmp(elem, "init")) { ++ /* We have an init section, which must be full of nops or labels. */ ++ int nop_cursor = 0; ++ char *initelem; ++ while ((initelem = xp_open_element(nop_cursor++))) { ++ if (!strcmp(initelem, "nop")) { ++ /* We should parse this. */ ++ message *nopmsg = new message(initmessages.size(), "scenario initialization"); ++ initmessages.push_back(nopmsg); ++ nopmsg->M_type = MSG_TYPE_NOP; ++ getCommonAttributes(nopmsg); ++ } else if (!strcmp(initelem, "label")) { ++ /* Add an init label. */ ++ ptr = xp_get_value((char *)"id"); ++ if (initLabelMap.find(ptr) != initLabelMap.end()) { ++ ERROR("The label name '%s' is used twice.", ptr); ++ } ++ initLabelMap[ptr] = initmessages.size(); ++ } else { ++ ERROR("Invalid element in an init stanza: '%s'", initelem); ++ } ++ xp_close_element(); ++ } + } else { /** Message Case */ + if (found_timewait) { + ERROR(" can only be the last message in a scenario!\n"); + } +- expand(length + 1); +- messages[length] = new message(); ++ message *curmsg = new message(messages.size(), name ? name : "unknown scenario"); ++ messages.push_back(curmsg); + + if(!strcmp(elem, "send")) { +- if (recv_count) { +- if (recv_count != recv_opt_count) { +- recv_count = 0; +- recv_opt_count = 0; +- } else { +- ERROR(" before sequence without a mandatory message. Please remove one 'optional=true' (element %d).", scenario_file_cursor); +- } +- } +- +- messages[length]->M_type = MSG_TYPE_SEND; ++ checkOptionalRecv(elem, scenario_file_cursor); ++ curmsg->M_type = MSG_TYPE_SEND; + /* Sent messages descriptions */ + if(!(ptr = xp_get_cdata())) { + ERROR("No CDATA in 'send' section of xml scenario file"); +@@ -769,11 +800,11 @@ scenario::scenario(char * filename, int deflt) + // the msg does not contain content-length field + break ; + case 0 : +- messages[length] -> content_length_flag = ++ curmsg -> content_length_flag = + message::ContentLengthValueZero; // Initialize to No present + break ; + default : +- messages[length] -> content_length_flag = ++ curmsg -> content_length_flag = + message::ContentLengthValueNoZero; // Initialize to No present + break ; + } +@@ -781,15 +812,28 @@ scenario::scenario(char * filename, int deflt) + if((msg[strlen(msg) - 1] != '\n') && (removed_clrf)) { + strcat(msg, "\n"); + } +- messages[length] -> send_scheme = new SendingMessage(this, msg); ++ char *tsrc = msg; ++ while(*tsrc++); ++ curmsg -> send_scheme = new SendingMessage(this, msg); + free(msg); + + // If this is a request we are sending, then store our transaction/method matching information. +- if (!messages[length]->send_scheme->isResponse()) { +- if (ptr = xp_get_value("start_txn")) { +- messages[length]->start_txn = get_txn(ptr, "start transaction", true); ++ if (!curmsg->send_scheme->isResponse()) { ++ char *method = curmsg->send_scheme->getMethod(); ++ bool isInvite = !strcmp(method, "INVITE"); ++ bool isAck = !strcmp(method, "ACK"); ++ ++ if ((ptr = xp_get_value("start_txn"))) { ++ if (isAck) { ++ ERROR("An ACK message can not start a transaction!"); ++ } ++ curmsg->start_txn = get_txn(ptr, "start transaction", true, isInvite, false); ++ } else if ((ptr = xp_get_value("ack_txn"))) { ++ if (!isAck) { ++ ERROR("The ack_txn attribute is valid only for ACK messages!"); ++ } ++ curmsg->ack_txn = get_txn(ptr, "ack transaction", false, false, true); + } else { +- char *method = messages[length]->send_scheme->getMethod(); + int len = method_list ? strlen(method_list) : 0; + method_list = (char *)realloc(method_list, len + strlen(method) + 1); + if (!method_list) { +@@ -798,75 +842,66 @@ scenario::scenario(char * filename, int deflt) + strcpy(method_list + len, method); + } + } else { +- if (ptr = xp_get_value("start_txn")) { ++ if ((ptr = xp_get_value("start_txn"))) { + ERROR("Responses can not start a transaction"); + } ++ if ((ptr = xp_get_value("ack_txn"))) { ++ ERROR("Responses can not ACK a transaction"); ++ } + } + +- if (ptr = xp_get_value("response_txn")) { ++ if ((ptr = xp_get_value("response_txn"))) { + ERROR("response_txn can only be used for recieved messages."); + } + +- messages[length] -> retrans_delay = xp_get_long("retrans", "retransmission timer", 0); +- messages[length] -> timeout = xp_get_long("timeout", "message send timeout", 0); ++ curmsg -> retrans_delay = xp_get_long("retrans", "retransmission timer", 0); ++ curmsg -> timeout = xp_get_long("timeout", "message send timeout", 0); + } else if(!strcmp(elem, (char *)"recv")) { +- recv_count++; +- messages[length]->M_type = MSG_TYPE_RECV; ++ curmsg->M_type = MSG_TYPE_RECV; + /* Received messages descriptions */ +- if(ptr = xp_get_value((char *)"response")) { +- messages[length] ->recv_response = get_long(ptr, "response code"); ++ if((ptr = xp_get_value((char *)"response"))) { ++ curmsg ->recv_response = get_long(ptr, "response code"); + if (method_list) { +- messages[length]->recv_response_for_cseq_method_list = strdup(method_list); ++ curmsg->recv_response_for_cseq_method_list = strdup(method_list); + } +- if (ptr = xp_get_value("response_txn")) { +- messages[length]->response_txn = get_txn(ptr, "transaction response", false); ++ if ((ptr = xp_get_value("response_txn"))) { ++ curmsg->response_txn = get_txn(ptr, "transaction response", false, false, false); + } + } + +- if(ptr = xp_get_value((char *)"request")) { +- messages[length] -> recv_request = strdup(ptr); +- if (ptr = xp_get_value("response_txn")) { ++ if((ptr = xp_get_value((char *)"request"))) { ++ curmsg -> recv_request = strdup(ptr); ++ if ((ptr = xp_get_value("response_txn"))) { + ERROR("response_txn can only be used for recieved responses."); + } + } + +- if (0 != (ptr = xp_get_value((char *)"optional"))) { +- if(!strcmp(ptr, "true")) { +- messages[length] -> optional = OPTIONAL_TRUE; +- ++recv_opt_count; +- } else if(!strcmp(ptr, "global")) { +- messages[length] -> optional = OPTIONAL_GLOBAL; +- ++recv_opt_count; +- } else if(!strcmp(ptr, "false")) { +- messages[length] -> optional = OPTIONAL_FALSE; +- } else { +- ERROR("Could not understand optional value: %s", ptr); +- } +- } +- messages[length]->advance_state = xp_get_bool("advance_state", "recv", true); +- if (!messages[length]->advance_state && messages[length]->optional == OPTIONAL_FALSE) { +- ERROR("advance_state is allowed only for optional messages (index = %d)\n", length); ++ curmsg->optional = xp_get_optional("optional", "recv"); ++ last_recv_optional = curmsg->optional; ++ curmsg->advance_state = xp_get_bool("advance_state", "recv", true); ++ if (!curmsg->advance_state && curmsg->optional == OPTIONAL_FALSE) { ++ ERROR("advance_state is allowed only for optional messages (index = %d)\n", messages.size() - 1); + } + + if (0 != (ptr = xp_get_value((char *)"regexp_match"))) { + if(!strcmp(ptr, "true")) { +- messages[length] -> regexp_match = 1; ++ curmsg -> regexp_match = 1; + } + } + +- messages[length]->timeout = xp_get_long("timeout", "message timeout", 0); ++ curmsg->timeout = xp_get_long("timeout", "message timeout", 0); + + /* record the route set */ + /* TODO disallow optional and rrs to coexist? */ +- if(ptr = xp_get_value((char *)"rrs")) { +- messages[length] -> bShouldRecordRoutes = get_bool(ptr, "record route set"); ++ if((ptr = xp_get_value((char *)"rrs"))) { ++ curmsg -> bShouldRecordRoutes = get_bool(ptr, "record route set"); + } + + /* record the authentication credentials */ +- if(ptr = xp_get_value((char *)"auth")) { ++ if((ptr = xp_get_value((char *)"auth"))) { + bool temp = get_bool(ptr, "message authentication"); + #ifdef _USE_OPENSSL +- messages[length] -> bShouldAuthenticate = temp; ++ curmsg -> bShouldAuthenticate = temp; + #else + if (temp) { + ERROR("Authentication requires OpenSSL support!"); +@@ -874,23 +909,16 @@ scenario::scenario(char * filename, int deflt) + #endif + } + } else if(!strcmp(elem, "pause") || !strcmp(elem, "timewait")) { +- if (recv_count) { +- if (recv_count != recv_opt_count) { +- recv_count = 0; +- recv_opt_count = 0; +- } else { +- ERROR(" before sequence without a mandatory message. Please remove one 'optional=true' (element %d).", scenario_file_cursor); +- } +- } +- messages[length]->M_type = MSG_TYPE_PAUSE; ++ checkOptionalRecv(elem, scenario_file_cursor); ++ curmsg->M_type = MSG_TYPE_PAUSE; + if (!strcmp(elem, "timewait")) { +- messages[length]->timewait = true; ++ curmsg->timewait = true; + found_timewait = true; + } + + int var; + if ((var = xp_get_var("variable", "pause", -1)) != -1) { +- messages[length]->pause_variable = var; ++ curmsg->pause_variable = var; + } else { + CSample *distribution = parse_distribution(true); + +@@ -907,40 +935,37 @@ scenario::scenario(char * filename, int deflt) + ERROR("The distribution %s has a 99th percentile of %s, which is larger than INT_MAX. You should chose different parameters.", desc, percentile); + } + +- messages[length]->pause_distribution = distribution; ++ curmsg->pause_distribution = distribution; + /* Update scenario duration with max duration */ + duration += (int)pause_duration; + } + } + else if(!strcmp(elem, "nop")) { ++ checkOptionalRecv(elem, scenario_file_cursor); + /* Does nothing at SIP level. This message type can be used to handle + * actions, increment counters, or for RTDs. */ +- messages[length]->M_type = MSG_TYPE_NOP; ++ curmsg->M_type = MSG_TYPE_NOP; + } + else if(!strcmp(elem, "recvCmd")) { +- recv_count++; +- messages[length]->M_type = MSG_TYPE_RECVCMD; ++ curmsg->M_type = MSG_TYPE_RECVCMD; ++ curmsg->optional = xp_get_optional("optional", "recv"); ++ last_recv_optional = curmsg->optional; + + /* 3pcc extended mode */ +- if(ptr = xp_get_value((char *)"src")) { +- messages[length] ->peer_src = strdup(ptr); +- } ++ if((ptr = xp_get_value((char *)"src"))) { ++ curmsg ->peer_src = strdup(ptr); ++ } else if (extendedTwinSippMode) { ++ ERROR("You must specify a 'src' for recvCmd when using extended 3pcc mode!"); ++ } + } else if(!strcmp(elem, "sendCmd")) { +- if (recv_count) { +- if (recv_count != recv_opt_count) { +- recv_count = 0; +- recv_opt_count = 0; +- } else { +- ERROR(" before sequence without a mandatory message. Please remove one 'optional=true' (element %d).", scenario_file_cursor); +- } +- } +- messages[length]->M_type = MSG_TYPE_SENDCMD; ++ checkOptionalRecv(elem, scenario_file_cursor); ++ curmsg->M_type = MSG_TYPE_SENDCMD; + /* Sent messages descriptions */ + + /* 3pcc extended mode */ +- if(ptr = xp_get_value((char *)"dest")) { ++ if((ptr = xp_get_value((char *)"dest"))) { + peer = strdup(ptr) ; +- messages[length] ->peer_dest = peer ; ++ curmsg ->peer_dest = peer ; + peer_map::iterator peer_it; + peer_it = peers.find(peer_map::key_type(peer)); + if(peer_it == peers.end()) /* the peer (slave or master) +@@ -952,22 +977,23 @@ scenario::scenario(char * filename, int deflt) + strcpy(infos.peer_host, get_peer_addr(peer)); + peers[std::string(peer)] = infos; + } ++ } else if (extendedTwinSippMode) { ++ ERROR("You must specify a 'dest' for sendCmd with extended 3pcc mode!"); + } + + if(!(ptr = xp_get_cdata())) { +- ERROR("No CDATA in 'send' section of xml scenario file"); ++ ERROR("No CDATA in 'sendCmd' section of xml scenario file"); + } + char *msg = clean_cdata(ptr); + +- messages[length] -> M_sendCmdData = new SendingMessage(this, msg, true /* skip sanity */); ++ curmsg -> M_sendCmdData = new SendingMessage(this, msg, true /* skip sanity */); + free(msg); + } + else { + ERROR("Unknown element '%s' in xml scenario file", elem); + } + +- getCommonAttributes(); +- length++; ++ getCommonAttributes(curmsg); + } /** end * Message case */ + xp_close_element(); + } // end while +@@ -984,13 +1010,11 @@ scenario::scenario(char * filename, int deflt) + pausedaddr = find_var("_unexp.pausedaddr", "unexpected paused until"); + + /* Patch up the labels. */ +- apply_labels(); ++ apply_labels(messages, labelMap); ++ apply_labels(initmessages, initLabelMap); + + /* Some post-scenario loading validation. */ +- validate_rtds(); +- if (length == 0) { +- ERROR("Did not find any messages inside of scenario!"); +- } ++ stats->validateRtds(); + + /* Make sure that all variables are used more than once. */ + validate_variable_usage(); +@@ -998,6 +1022,17 @@ scenario::scenario(char * filename, int deflt) + /* Make sure that all started transactions have responses, and vice versa. */ + validate_txn_usage(); + ++ if (messages.size() == 0) { ++ ERROR("Did not find any messages inside of scenario!"); ++ } ++} ++ ++void scenario::runInit() { ++ call *initcall; ++ if (initmessages.size() > 0) { ++ initcall = new call(main_scenario, NULL, NULL, "///main-init", 0, false, false, true); ++ initcall->run(); ++ } + } + + void clear_int_str(int_str_map m) { +@@ -1020,25 +1055,24 @@ void clear_int_int(int_int_map m) { + } + + scenario::~scenario() { +- for (int i = 0; i < length; i++) { +- delete messages[i]; ++ for (msgvec::iterator i = messages.begin(); i != messages.end(); i++) { ++ delete *i; + } +- free(messages); ++ messages.clear(); + + free(name); + + allocVars->putTable(); + delete stats; + +- clear_int_str(txnRevMap); +- clear_int_str(nextLabels); +- clear_int_str(ontimeoutLabels); ++ for (unsigned int i = 0; i < transactions.size(); i++) { ++ free(transactions[i].name); ++ } ++ transactions.clear(); + + clear_str_int(labelMap); ++ clear_str_int(initLabelMap); + clear_str_int(txnMap); +- +- clear_int_int(txnStarted); +- clear_int_int(txnResponses); + } + + CSample *parse_distribution(bool oldstyle = false) { +@@ -1050,23 +1084,23 @@ CSample *parse_distribution(bool oldstyle = false) { + if (!oldstyle) { + ERROR("statistically distributed actions or pauses requires 'distribution' parameter"); + } +- if (ptr = xp_get_value("normal")) { ++ if ((ptr = xp_get_value("normal"))) { + distname = "normal"; +- } else if (ptr = xp_get_value("exponential")) { ++ } else if ((ptr = xp_get_value("exponential"))) { + distname = "exponential"; +- } else if (ptr = xp_get_value("lognormal")) { ++ } else if ((ptr = xp_get_value("lognormal"))) { + distname = "lognormal"; +- } else if (ptr = xp_get_value("weibull")) { ++ } else if ((ptr = xp_get_value("weibull"))) { + distname = "weibull"; +- } else if (ptr = xp_get_value("pareto")) { ++ } else if ((ptr = xp_get_value("pareto"))) { + distname = "pareto"; +- } else if (ptr = xp_get_value("gamma")) { ++ } else if ((ptr = xp_get_value("gamma"))) { + distname = "gamma"; +- } else if (ptr = xp_get_value("min")) { ++ } else if ((ptr = xp_get_value("min"))) { + distname = "uniform"; +- } else if (ptr = xp_get_value("max")) { ++ } else if ((ptr = xp_get_value("max"))) { + distname = "uniform"; +- } else if (ptr = xp_get_value("milliseconds")) { ++ } else if ((ptr = xp_get_value("milliseconds"))) { + double val = get_double(ptr, "Pause milliseconds"); + return new CFixed(val); + } else { +@@ -1150,9 +1184,9 @@ void parse_slave_cfg() + if(f){ + while (fgets(line, MAX_PEER_SIZE, f) != NULL) + { +- if(temp_peer = strtok(line, ";")){ +- if(peer_host = (char *) malloc(MAX_PEER_SIZE)){ +- if(temp_host = strtok(NULL, ";")){ ++ if((temp_peer = strtok(line, ";"))) { ++ if((peer_host = (char *) malloc(MAX_PEER_SIZE))){ ++ if((temp_host = strtok(NULL, ";"))){ + strcpy(peer_host, temp_host); + peer_addrs[std::string(temp_peer)] = peer_host; + } +@@ -1173,88 +1207,101 @@ void scenario::computeSippMode() + { + bool isRecvCmdFound = false; + bool isSendCmdFound = false; +- bool isFirstMessageFound = true; + +- toolMode = -1; +- for(int i=0; i 0); ++ ++ for(unsigned int i=0; iM_type) + { + case MSG_TYPE_PAUSE: + case MSG_TYPE_NOP: + /* Allow pauses or nops to go first. */ + continue; +- case MSG_TYPE_SEND: +- if(isFirstMessageFound) +- toolMode = MODE_CLIENT; +- isFirstMessageFound = false; ++ case MSG_TYPE_SEND: ++ if (sendMode == -1) { ++ sendMode = MODE_CLIENT; ++ } ++ if (creationMode == -1) { ++ creationMode = MODE_CLIENT; ++ } + break; + + case MSG_TYPE_RECV: +- if(isFirstMessageFound) +- toolMode = MODE_SERVER; +- isFirstMessageFound = false; ++ if (sendMode == -1) { ++ sendMode = MODE_SERVER; ++ } ++ if (creationMode == -1) { ++ creationMode = MODE_SERVER; ++ } + break; + case MSG_TYPE_SENDCMD: + isSendCmdFound = true; ++ if (creationMode == -1) { ++ creationMode = MODE_CLIENT; ++ } + if(!isRecvCmdFound) { +- if (false == isFirstMessageFound && toolMode == MODE_SERVER) { ++ if (creationMode == MODE_SERVER) { + /* + * If it is a server already, then start it in + * 3PCC A passive mode + */ + if(twinSippMode){ +- toolMode = MODE_3PCC_A_PASSIVE; ++ thirdPartyMode = MODE_3PCC_A_PASSIVE; + }else if (extendedTwinSippMode){ +- toolMode = MODE_MASTER_PASSIVE; ++ thirdPartyMode = MODE_MASTER_PASSIVE; + } + } else { + if(twinSippMode){ +- toolMode = MODE_3PCC_CONTROLLER_A; +- }else if (extendedTwinSippMode){ +- toolMode = MODE_MASTER; +- } ++ thirdPartyMode = MODE_3PCC_CONTROLLER_A; ++ }else if (extendedTwinSippMode){ ++ thirdPartyMode = MODE_MASTER; ++ } + } +- if((toolMode == MODE_MASTER_PASSIVE || toolMode == MODE_MASTER) && !master_name){ ++ if((thirdPartyMode == MODE_MASTER_PASSIVE || thirdPartyMode == MODE_MASTER) && !master_name){ + ERROR("Inconsistency between command line and scenario: master scenario but -master option not set\n"); + } + if(!twinSippMode && !extendedTwinSippMode) + ERROR("sendCmd message found in scenario but no twin sipp" + " address has been passed! Use -3pcc option or 3pcc extended mode.\n"); +- return; + } +- isFirstMessageFound = false; + break; + + case MSG_TYPE_RECVCMD: ++ if (creationMode == -1) { ++ creationMode = MODE_SERVER; ++ } + isRecvCmdFound = true; + if(!isSendCmdFound) + { + if(twinSippMode){ +- toolMode = MODE_3PCC_CONTROLLER_B; ++ thirdPartyMode = MODE_3PCC_CONTROLLER_B; + } else if(extendedTwinSippMode){ +- toolMode = MODE_SLAVE; ++ thirdPartyMode = MODE_SLAVE; + if(!slave_number) { +- ERROR("Inconsistency between command line and scenario: slave scenario but -slave option not set\n"); +- }else{ +- toolMode = MODE_SLAVE; +- } ++ ERROR("Inconsistency between command line and scenario: slave scenario but -slave option not set\n"); ++ }else{ ++ thirdPartyMode = MODE_SLAVE; ++ } + } + if(!twinSippMode && !extendedTwinSippMode) +- ERROR("sendCmd message found in scenario but no " ++ ERROR("recvCmd message found in scenario but no " + "twin sipp address has been passed! Use " + "-3pcc option\n"); +- return; + } +- isFirstMessageFound = false; + break; + default: + break; + } + } +- if(toolMode == -1) +- ERROR("Unable to determine mode of the tool (server, " +- "client, 3PCC controller A, 3PCC controller B).\n"); ++ if(creationMode == -1) ++ ERROR("Unable to determine creation mode of the tool (server, client)\n"); ++ if(sendMode == -1) ++ ERROR("Unable to determine send mode of the tool (server, client)\n"); + } + + void scenario::handle_rhs(CAction *tmpAction, char *what) { +@@ -1278,39 +1325,21 @@ void scenario::handle_arithmetic(CAction *tmpAction, char *what) { + handle_rhs(tmpAction, what); + } + +-// Action list for the message indexed by message_index in +-// the scenario +-void scenario::getActionForThisMessage() +-{ +- unsigned int recvScenarioLen = 0; ++void scenario::parseAction(CActions *actions) { + char * actionElem; ++ unsigned int recvScenarioLen = 0; + char * currentRegExp = NULL; + char * buffer = NULL; + char ** currentTabVarName = NULL; + int currentNbVarNames; + char * ptr; + int sub_currentNbVarId; +- +- if(!(actionElem = xp_open_element(0))) { +- return; +- } +- if(strcmp(actionElem, "action")) { +- return; +- } +- +- /* We actually have an action element. */ +- if(messages[length]->M_actions != NULL) { +- ERROR("Duplicate action for scenario index %d", length); +- } +- messages[length]->M_actions = new CActions(); + +- while(actionElem = xp_open_element(recvScenarioLen)) { ++ while((actionElem = xp_open_element(recvScenarioLen))) { + CAction *tmpAction = new CAction(this); + + if(!strcmp(actionElem, "ereg")) { +- if(!(ptr = xp_get_value((char *)"regexp"))) { +- ERROR("'ereg' action without 'regexp' argument (mandatory)"); +- } ++ ptr = xp_get_string("regexp", "ereg"); + + // keeping regexp expression in memory + if(currentRegExp != NULL) +@@ -1325,24 +1354,22 @@ void scenario::getActionForThisMessage() + + // warning - although these are detected for both msg and hdr + // they are only implemented for search_in="hdr" +- if ((ptr = xp_get_value((char *)"case_indep"))) { +- tmpAction->setCaseIndep(get_bool(ptr, "case_indep")); +- } else { +- tmpAction->setCaseIndep(false); +- } +- +- if ((ptr = xp_get_value((char *)"start_line"))) { +- tmpAction->setHeadersOnly(get_bool(ptr, "start_line")); +- } else { +- tmpAction->setHeadersOnly(false); +- } ++ tmpAction->setCaseIndep(xp_get_bool("case_indep", "ereg", false)); ++ tmpAction->setHeadersOnly(xp_get_bool("start_line", "ereg", false)); + ++ free(ptr); + if ( 0 != ( ptr = xp_get_value((char *)"search_in") ) ) { + tmpAction->setOccurence(1); + + if ( 0 == strcmp(ptr, (char *)"msg") ) { + tmpAction->setLookingPlace(CAction::E_LP_MSG); + tmpAction->setLookingChar (NULL); ++ } else if ( 0 == strcmp(ptr, (char *)"body") ) { ++ tmpAction->setLookingPlace(CAction::E_LP_BODY); ++ tmpAction->setLookingChar (NULL); ++ } else if (!strcmp(ptr, (char *)"var")) { ++ tmpAction->setVarInId(xp_get_var("variable", "ereg")); ++ tmpAction->setLookingPlace(CAction::E_LP_VAR); + } else if (!strcmp(ptr, (char *)"hdr")) { + ptr = xp_get_value((char *)"header"); + if (!ptr || !strlen(ptr)) { +@@ -1353,6 +1380,9 @@ void scenario::getActionForThisMessage() + if (0 != (ptr = xp_get_value((char *)"occurence"))) { + tmpAction->setOccurence (atol(ptr)); + } ++ if (0 != (ptr = xp_get_value((char *)"occurrence"))) { ++ tmpAction->setOccurence (atol(ptr)); ++ } + } else { + ERROR("Unknown search_in value %s", ptr); + } +@@ -1361,11 +1391,15 @@ void scenario::getActionForThisMessage() + tmpAction->setLookingChar(NULL); + } // end if-else search_in + +- if(ptr = xp_get_value((char *)"check_it")) { +- tmpAction->setCheckIt(get_bool(ptr, "check_it")); ++ if (xp_get_value("check_it")) { ++ tmpAction->setCheckIt(xp_get_bool("check_it", "ereg", false)); ++ if (xp_get_value("check_it_inverse")) { ++ ERROR("Can not have both check_it and check_it_inverse for ereg!"); ++ } + } else { +- tmpAction->setCheckIt(false); ++ tmpAction->setCheckItInverse(xp_get_bool("check_it_inverse", "ereg", false)); + } ++ + if (!(ptr = xp_get_value((char *) "assign_to"))) { + ERROR("assign_to value is missing"); + } +@@ -1393,30 +1427,21 @@ void scenario::getActionForThisMessage() + } + currentRegExp = NULL; + } /* end !strcmp(actionElem, "ereg") */ else if(!strcmp(actionElem, "log")) { +- if(ptr = xp_get_value((char *)"message")) { +- tmpAction->setMessage(ptr); +- } else { +- ERROR("Log message without a message!"); +- } ++ tmpAction->setMessage(xp_get_string("message", "log")); + tmpAction->setActionType(CAction::E_AT_LOG_TO_FILE); + } else if(!strcmp(actionElem, "warning")) { +- if(ptr = xp_get_value((char *)"message")) { +- tmpAction->setMessage(ptr); +- } else { +- ERROR("Warning message without a message!"); +- } ++ tmpAction->setMessage(xp_get_string("message", "warning")); + tmpAction->setActionType(CAction::E_AT_LOG_WARNING); ++ } else if(!strcmp(actionElem, "error")) { ++ tmpAction->setMessage(xp_get_string("message", "error")); ++ tmpAction->setActionType(CAction::E_AT_LOG_ERROR); + } else if(!strcmp(actionElem, "assign")) { + tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_VALUE); + handle_arithmetic(tmpAction, "assign"); + } else if(!strcmp(actionElem, "assignstr")) { + tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_STRING); + tmpAction->setVarId(xp_get_var("assign_to", "assignstr")); +- if(ptr = xp_get_value((char *)"value")) { +- tmpAction->setMessage(ptr); +- } else { +- ERROR("assignstr action without a value!"); +- } ++ tmpAction->setMessage(xp_get_string("value", "assignstr")); + } else if(!strcmp(actionElem, "gettimeofday")) { + tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_GETTIMEOFDAY); + +@@ -1472,11 +1497,16 @@ void scenario::getActionForThisMessage() + } else if(!strcmp(actionElem, "test")) { + tmpAction->setVarId(xp_get_var("assign_to", "test")); + tmpAction->setVarInId(xp_get_var("variable", "test")); +- tmpAction->setDoubleValue(xp_get_double("value", "test")); +- tmpAction->setActionType(CAction::E_AT_VAR_TEST); +- if (!(ptr = xp_get_value("compare"))) { +- ERROR("test actions require a 'compare' parameter"); ++ if (xp_get_value("value")) { ++ tmpAction->setDoubleValue(xp_get_double("value", "test")); ++ if (xp_get_value("variable2")) { ++ ERROR("Can not have both a value and a variable2 for test!"); ++ } ++ } else { ++ tmpAction->setVarIn2Id(xp_get_var("variable2", "test")); + } ++ tmpAction->setActionType(CAction::E_AT_VAR_TEST); ++ ptr = xp_get_string("compare", "test"); + if (!strcmp(ptr, "equal")) { + tmpAction->setComparator(CAction::E_C_EQ); + } else if (!strcmp(ptr, "not_equal")) { +@@ -1492,6 +1522,7 @@ void scenario::getActionForThisMessage() + } else { + ERROR("Invalid 'compare' parameter: %s", ptr); + } ++ free(ptr); + } else if(!strcmp(actionElem, "verifyauth")) { + #ifdef _USE_OPENSSL + tmpAction->setVarId(xp_get_var("assign_to", "verifyauth")); +@@ -1506,19 +1537,42 @@ void scenario::getActionForThisMessage() + tmpAction->setMessage(xp_get_string("file", "lookup"), 0); + tmpAction->setMessage(xp_get_string("key", "lookup"), 1); + tmpAction->setActionType(CAction::E_AT_LOOKUP); ++ } else if(!strcmp(actionElem, "insert")) { ++ tmpAction->setMessage(xp_get_string("file", "insert"), 0); ++ tmpAction->setMessage(xp_get_string("value", "insert"), 1); ++ tmpAction->setActionType(CAction::E_AT_INSERT); ++ } else if(!strcmp(actionElem, "replace")) { ++ tmpAction->setMessage(xp_get_string("file", "replace"), 0); ++ tmpAction->setMessage(xp_get_string("line", "replace"), 1); ++ tmpAction->setMessage(xp_get_string("value", "replace"), 2); ++ tmpAction->setActionType(CAction::E_AT_REPLACE); ++ } else if(!strcmp(actionElem, "setdest")) { ++ tmpAction->setMessage(xp_get_string("host", actionElem), 0); ++ tmpAction->setMessage(xp_get_string("port", actionElem), 1); ++ tmpAction->setMessage(xp_get_string("protocol", actionElem), 2); ++ tmpAction->setActionType(CAction::E_AT_SET_DEST); ++ } else if(!strcmp(actionElem, "closecon")) { ++ tmpAction->setActionType(CAction::E_AT_CLOSE_CON); + } else if(!strcmp(actionElem, "strcmp")) { + tmpAction->setVarId(xp_get_var("assign_to", "strcmp")); +- tmpAction->setVarInId(xp_get_var("variable", "test")); +- tmpAction->setStringValue(xp_get_string("value", "test")); ++ tmpAction->setVarInId(xp_get_var("variable", "strcmp")); ++ if (xp_get_value("value")) { ++ tmpAction->setStringValue(xp_get_string("value", "strcmp")); ++ if (xp_get_value("variable2")) { ++ ERROR("Can not have both a value and a variable2 for strcmp!"); ++ } ++ } else { ++ tmpAction->setVarIn2Id(xp_get_var("variable2", "strcmp")); ++ } + tmpAction->setActionType(CAction::E_AT_VAR_STRCMP); + } else if(!strcmp(actionElem, "trim")) { + tmpAction->setVarId(xp_get_var("assign_to", "trim")); + tmpAction->setActionType(CAction::E_AT_VAR_TRIM); + } else if(!strcmp(actionElem, "exec")) { +- if(ptr = xp_get_value((char *)"command")) { ++ if((ptr = xp_get_value((char *)"command"))) { + tmpAction->setActionType(CAction::E_AT_EXECUTE_CMD); +- tmpAction->setCmdLine(ptr); +- } /* end (ptr = xp_get_value("command") */ else if(ptr = xp_get_value((char *)"int_cmd")) { ++ tmpAction->setMessage(ptr); ++ } /* end (ptr = xp_get_value("command") */ else if((ptr = xp_get_value((char *)"int_cmd"))) { + CAction::T_IntCmdType type(CAction::E_INTCMD_STOPCALL); /* assume the default */ + + if (!strcmp(ptr, "stop_now")) { +@@ -1534,18 +1588,18 @@ void scenario::getActionForThisMessage() + tmpAction->setActionType(CAction::E_AT_EXEC_INTCMD); + tmpAction->setIntCmd(type); + #ifdef PCAPPLAY +- } else if (ptr = xp_get_value((char *) "play_pcap_audio")) { ++ } else if ((ptr = xp_get_value((char *) "play_pcap_audio"))) { + tmpAction->setPcapArgs(ptr); + tmpAction->setActionType(CAction::E_AT_PLAY_PCAP_AUDIO); + hasMedia = 1; +- } else if (ptr = xp_get_value((char *) "play_pcap_video")) { ++ } else if ((ptr = xp_get_value((char *) "play_pcap_video"))) { + tmpAction->setPcapArgs(ptr); + tmpAction->setActionType(CAction::E_AT_PLAY_PCAP_VIDEO); + hasMedia = 1; + #else +- } else if (ptr = xp_get_value((char *) "play_pcap_audio")) { ++ } else if ((ptr = xp_get_value((char *) "play_pcap_audio"))) { + ERROR("play_pcap_audio requires pcap support! Please recompile SIPp"); +- } else if (ptr = xp_get_value((char *) "play_pcap_video")) { ++ } else if ((ptr = xp_get_value((char *) "play_pcap_video"))) { + ERROR("play_pcap_video requires pcap support! Please recompile SIPp"); + #endif + } else { +@@ -1557,78 +1611,101 @@ void scenario::getActionForThisMessage() + + /* If the action was not well-formed, there should have already been an + * ERROR declaration, thus it is safe to add it here at the end of the loop. */ +- messages[length]->M_actions->setAction(tmpAction); ++ actions->setAction(tmpAction); + + xp_close_element(); + recvScenarioLen++; + } // end while ++} ++ ++// Action list for the message indexed by message_index in ++// the scenario ++void scenario::getActionForThisMessage(message *message) ++{ ++ char * actionElem; ++ ++ if(!(actionElem = xp_open_element(0))) { ++ return; ++ } ++ if(strcmp(actionElem, "action")) { ++ return; ++ } ++ ++ /* We actually have an action element. */ ++ if(message->M_actions != NULL) { ++ ERROR("Duplicate action for %s index %d", message->desc, message->index); ++ } ++ message->M_actions = new CActions(); ++ ++ parseAction(message->M_actions); + xp_close_element(); + } + +-void scenario::getBookKeeping() { ++void scenario::getBookKeeping(message *message) { + char *ptr; + +- if(ptr = xp_get_value((char *)"rtd")) { +- messages[length] -> stop_rtd = get_rtd(ptr); +- rtd_stopped[messages[length]->stop_rtd - 1] = true; ++ if((ptr = xp_get_value((char *)"rtd"))) { ++ message -> stop_rtd = get_rtd(ptr, false); + } +- if (ptr = xp_get_value((char *)"repeat_rtd")) { +- if (messages[length] -> stop_rtd) { +- messages[length] -> repeat_rtd = get_bool(ptr, "repeat_rtd"); ++ if ((ptr = xp_get_value((char *)"repeat_rtd"))) { ++ if (message -> stop_rtd) { ++ message-> repeat_rtd = get_bool(ptr, "repeat_rtd"); + } else { + ERROR("There is a repeat_rtd element without an rtd element"); + } + } + +- if(ptr = xp_get_value((char *)"start_rtd")) { +- messages[length] -> start_rtd = get_rtd(ptr); +- rtd_started[messages[length]->start_rtd - 1] = true; ++ if((ptr = xp_get_value((char *)"start_rtd"))) { ++ message -> start_rtd = get_rtd(ptr, true); + } + +- if(ptr = xp_get_value((char *)"counter")) { +- messages[length] -> counter = get_counter(ptr, "counter"); ++ if((ptr = xp_get_value((char *)"counter"))) { ++ message -> counter = get_counter(ptr, "counter"); + } + } + +-void scenario::getCommonAttributes() { ++void scenario::getCommonAttributes(message *message) { + char *ptr; + +- getBookKeeping(); +- getActionForThisMessage(); ++ getBookKeeping(message); ++ getActionForThisMessage(message); + +- if(ptr = xp_get_value((char *)"lost")) { +- messages[length] -> lost = get_double(ptr, "lost percentage"); ++ if((ptr = xp_get_value((char *)"lost"))) { ++ message -> lost = get_double(ptr, "lost percentage"); + lose_packets = 1; + } + +- if(ptr = xp_get_value((char *)"crlf")) { +- messages[length] -> crlf = 1; ++ if((ptr = xp_get_value((char *)"crlf"))) { ++ message -> crlf = 1; + } + + if (xp_get_value("hiderest")) { + hidedefault = xp_get_bool("hiderest", "hiderest"); + } +- messages[length] -> hide = xp_get_bool("hide", "hide", hidedefault); +- if(ptr = xp_get_value((char *)"display")) { +- messages[length] -> display_str = strdup(ptr); ++ message -> hide = xp_get_bool("hide", "hide", hidedefault); ++ if((ptr = xp_get_value((char *)"display"))) { ++ message -> display_str = strdup(ptr); + } + ++ message -> condexec = xp_get_var("condexec", "condexec variable", -1); ++ message -> condexec_inverse = xp_get_bool("condexec_inverse", "condexec_inverse", false); ++ + if ((ptr = xp_get_value((char *)"next"))) { + if (found_timewait) { + ERROR("next labels are not allowed in elements."); + } +- nextLabels[length] = strdup(ptr); +- messages[length] -> test = xp_get_var("test", "test variable", -1); ++ message -> nextLabel = strdup(ptr); ++ message -> test = xp_get_var("test", "test variable", -1); + if ( 0 != ( ptr = xp_get_value((char *)"chance") ) ) { + float chance = get_double(ptr,"chance"); + /* probability of branch to next */ + if (( chance < 0.0 ) || (chance > 1.0 )) { + ERROR("Chance %s not in range [0..1]", ptr); + } +- messages[length] -> chance = (int)((1.0-chance)*RAND_MAX); ++ message -> chance = (int)((1.0-chance)*RAND_MAX); + } + else { +- messages[length] -> chance = 0; /* always */ ++ message -> chance = 0; /* always */ + } + } + +@@ -1636,7 +1713,7 @@ void scenario::getCommonAttributes() { + if (found_timewait) { + ERROR("ontimeout labels are not allowed in elements."); + } +- ontimeoutLabels[length] = ptr; ++ message -> onTimeoutLabel = strdup(ptr); + } + } + +diff --git a/scenario.hpp b/scenario.hpp +index ec8119f..64861ef 100644 +--- a/scenario.hpp ++++ b/scenario.hpp +@@ -43,6 +43,7 @@ + #define MODE_CLIENT 0 + #define MODE_SERVER 1 + ++#define MODE_3PCC_NONE 0 + #define MODE_3PCC_CONTROLLER_A 2 + #define MODE_3PCC_CONTROLLER_B 3 + #define MODE_3PCC_A_PASSIVE 4 +@@ -108,9 +109,13 @@ public: + bool hide; + char * display_str; + int next; ++ char * nextLabel; + int test; ++ int condexec; ++ bool condexec_inverse; + int chance;/* 0=always, RAND_MAX+1=never (test rand() >= chance) */ + int on_timeout; ++ char * onTimeoutLabel; + + /* Statistics */ + unsigned long nb_sent; +@@ -139,31 +144,48 @@ public: + + char *recv_response_for_cseq_method_list; + int start_txn; ++ int ack_txn; + int response_txn; ++ int index; ++ const char * desc; + +- message(); ++ message(int index, const char *desc); + ~message(); + }; + ++typedef std::vector msgvec; ++ ++struct txnControlInfo { ++ char *name; ++ bool isInvite; ++ int acks; ++ int started; ++ int responses; ++}; ++typedef std::vector txnvec; ++ ++ + class scenario { + public: + scenario(char * filename, int deflt); + ~scenario(); + +- message **messages; +- int length; ++ void runInit(); ++ ++ msgvec messages; ++ msgvec initmessages; + char *name; + int duration; +- int maxTxnUsed; +- int_str_map txnRevMap; ++ txnvec transactions; + int unexpected_jump; + int retaddr; + int pausedaddr; + + void computeSippMode(); + +- bool rtd_stopped[MAX_RTD_INFO_LENGTH]; + int get_var(const char *varName, const char *what); ++ int get_counter(const char *varName, const char *what); ++ int get_rtd(const char *ptr, bool start); + int find_var(const char *varName, const char *what); + + CStat *stats; +@@ -173,44 +195,39 @@ private: + + /* The mapping of labels to IDs. */ + str_int_map labelMap; +- /* The string label representations. */ +- int_str_map nextLabels; +- int_str_map ontimeoutLabels; ++ str_int_map initLabelMap; + + str_int_map txnMap; +- int_int_map txnStarted; +- int_int_map txnResponses; +- + + bool found_timewait; +- bool rtd_started[MAX_RTD_INFO_LENGTH]; + +- void getBookKeeping(); +- void getCommonAttributes(); +- void getActionForThisMessage(); ++ void getBookKeeping(message *message); ++ void getCommonAttributes(message *message); ++ void getActionForThisMessage(message *message); ++ void parseAction(CActions *actions); + void handle_arithmetic(CAction *tmpAction, char *what); + void handle_rhs(CAction *tmpAction, char *what); ++ void checkOptionalRecv(char *elem, unsigned int scenario_file_cursor); + +- void apply_labels(); +- void init_rtds(); +- void validate_rtds(); ++ void apply_labels(msgvec v, str_int_map labels); + void validate_variable_usage(); + void validate_txn_usage(); + +- int get_txn(const char *txnName, const char *what, bool start); ++ int get_txn(const char *txnName, const char *what, bool start, bool isInvite, bool isAck); + int xp_get_var(const char *name, const char *what); + int xp_get_var(const char *name, const char *what, int defval); + +- void expand(int length); +- + bool hidedefault; ++ bool last_recv_optional; + }; + + /* There are external variable containing the current scenario */ + extern scenario *main_scenario; + extern scenario *ooc_scenario; + extern scenario *display_scenario; +-extern int toolMode; ++extern int creationMode; ++extern int sendMode; ++extern int thirdPartyMode; + + extern message::ContentLengthFlag content_length_flag; + +diff --git a/screen.cpp b/screen.cpp +index f665442..5bac606 100644 +--- a/screen.cpp ++++ b/screen.cpp +@@ -168,9 +168,9 @@ void manage_oversized_file() + "Max file size reached - no more logs\n", + CStat::formatTime(¤tTime)); + fflush(f); +- stop_all_traces(); ++ stop_all_traces(); + print_all_responses = 0; +- screen_errorf = 0; ++ error_lfi.fptr = NULL; + } + + +@@ -216,10 +216,11 @@ void screen_init(void (*exit_handler)()) + static void _screen_error(int fatal, bool use_errno, int error, const char *fmt, va_list ap) + { + static unsigned long long count = 0; +- FILE * output; + char * c = screen_last_error; + struct timeval currentTime; + ++ CStat::globalStat(fatal ? CStat::E_FATAL_ERRORS : CStat::E_WARNING); ++ + GET_TIME (¤tTime); + + c+= sprintf(c, "%s: ", CStat::formatTime(¤tTime)); +@@ -230,33 +231,33 @@ static void _screen_error(int fatal, bool use_errno, int error, const char *fmt, + c+= sprintf(c, ".\n"); + screen_errors++; + +- if(screen_inited && !screen_errorf && print_all_responses) { ++ if(screen_inited && !error_lfi.fptr && print_all_responses) { + rotate_errorf(); +- if(!screen_errorf) { ++ if(!error_lfi.fptr) { + c += sprintf(c, "%s: Unable to create '%s': %s.\n", + screen_exename, screen_logfile, strerror(errno)); + screen_exit(EXIT_FATAL_ERROR); + } else { +- fprintf(screen_errorf, "%s: The following events occured:\n", ++ fprintf(error_lfi.fptr, "%s: The following events occured:\n", + screen_exename); +- fflush(screen_errorf); ++ fflush(error_lfi.fptr); + } + } + +- if(screen_errorf) { +- count += fprintf(screen_errorf, "%s", screen_last_error); +- fflush(screen_errorf); ++ if(error_lfi.fptr) { ++ count += fprintf(error_lfi.fptr, "%s", screen_last_error); ++ fflush(error_lfi.fptr); + if (ringbuffer_size && count > ringbuffer_size) { + rotate_errorf(); + count = 0; + } + if (max_log_size && count > max_log_size) { + print_all_responses = 0; +- if (screen_errorf) { +- fflush(screen_errorf); +- fclose(screen_errorf); +- screen_errorf = NULL; +- errorf_overwrite = false; ++ if (error_lfi.fptr) { ++ fflush(error_lfi.fptr); ++ fclose(error_lfi.fptr); ++ error_lfi.fptr = NULL; ++ error_lfi.overwrite = false; + } + } + } else if (fatal) { +@@ -266,36 +267,45 @@ static void _screen_error(int fatal, bool use_errno, int error, const char *fmt, + + if(fatal) { + if(!screen_inited) { ++ if(error == EADDRINUSE) { ++ exit(EXIT_BIND_ERROR); ++ } else { + exit(EXIT_FATAL_ERROR); ++ } ++ } else { ++ if(error == EADDRINUSE) { ++ screen_exit(EXIT_BIND_ERROR); + } else { + screen_exit(EXIT_FATAL_ERROR); + } + } ++ } + } + + extern "C" { +-int ERROR(const char *fmt, ...) { ++void ERROR(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + _screen_error(true, false, 0, fmt, ap); + va_end(ap); ++ assert(0); + } + +-int ERROR_NO(const char *fmt, ...) { ++void ERROR_NO(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + _screen_error(true, true, errno, fmt, ap); + va_end(ap); + } + +-int WARNING(const char *fmt, ...) { ++void WARNING(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + _screen_error(false, false, 0, fmt, ap); + va_end(ap); + } + +-int WARNING_NO(const char *fmt, ...) { ++void WARNING_NO(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + _screen_error(false, true, errno, fmt, ap); +diff --git a/screen.hpp b/screen.hpp +index b606cdf..4b0da99 100644 +--- a/screen.hpp ++++ b/screen.hpp +@@ -31,10 +31,10 @@ + #ifdef __cplusplus + extern "C" { + #endif +- int ERROR(const char *fmt, ...); +- int WARNING(const char *fmt, ...); +- int ERROR_NO(const char *fmt, ...); +- int WARNING_NO(const char *fmt, ...); ++ void ERROR(const char *fmt, ...) __attribute__ ((noreturn)); ++ void WARNING(const char *fmt, ...); ++ void ERROR_NO(const char *fmt, ...); ++ void WARNING_NO(const char *fmt, ...); + #ifdef __cplusplus + } + #endif +@@ -45,6 +45,7 @@ extern "C" { + #define EXIT_TEST_RES_UNKNOWN 98 + #define EXIT_OTHER 99 + #define EXIT_FATAL_ERROR -1 ++#define EXIT_BIND_ERROR -2 + + void screen_set_exename(char * exe_name); + void screen_init(void (*exit_handler)()); +diff --git a/send_packets.c b/send_packets.c +index 0c2f7e5..22357d2 100644 +--- a/send_packets.c ++++ b/send_packets.c +@@ -54,6 +54,7 @@ + #include + #include + #include ++#include + + #include "send_packets.h" + #include "prepare_pcap.h" +@@ -112,6 +113,14 @@ void hexdump(char *p, int s) { + /*Safe threaded version*/ + void do_sleep (struct timeval *, struct timeval *, + struct timeval *, struct timeval *); ++void send_packets_cleanup(void *arg) ++{ ++ int sock = (int) arg; ++ ++ // Close send socket ++ close(sock); ++} ++ + + int + send_packets (play_args_t * play_args) +@@ -171,6 +180,13 @@ send_packets (play_args_t * play_args) + memcpy(&(from6.sin6_addr.s6_addr), &(((struct sockaddr_in6 *)(void *) from)->sin6_addr.s6_addr), sizeof(from6.sin6_addr.s6_addr)); + } + ++ ++ /* Ensure the sender socket is closed when the thread exits - this ++ * allows the thread to be cancelled cleanly. ++ */ ++ pthread_cleanup_push(send_packets_cleanup, ((void *) sock)); ++ ++ + while (pkt_index < pkt_max) { + memcpy(udp, pkt_index->data, pkt_index->pktlen); + port_diff = ntohs (udp->uh_dport) - pkts->base; +@@ -228,7 +244,8 @@ send_packets (play_args_t * play_args) + pkt_index++; + } + +- close(sock); ++ /* Closing the socket is handled by pthread_cleanup_push()/pthread_cleanup_pop() */ ++ pthread_cleanup_pop(1); + return 0; + } + +diff --git a/sipp.cpp b/sipp.cpp +index b4a57e2..f65c3e6 100644 +--- a/sipp.cpp ++++ b/sipp.cpp +@@ -20,6 +20,7 @@ + * David MANSUTTI + * Francois-Xavier Kowalski + * Gerard Lyonnaz ++ * Francois Draperi (for dynamic_id) + * From Hewlett Packard Company. + * F. Tarek Rogers + * Peter Higginson +@@ -29,13 +30,24 @@ + * Clement Chen + * Wolfgang Beck + * Charles P Wright from IBM Research ++ * Martin Van Leeuwen ++ * Andy Aicken ++ * Michael Hirschbichler + */ + + #define GLOBALS_FULL_DEFINITION + ++#include + #include "sipp.hpp" + #include "assert.h" + ++void sipp_usleep(unsigned long usec); ++ ++void rotate_messagef(); ++void rotate_calldebugf(); ++void rotate_shortmessagef(); ++void rotate_logfile(); ++ + #ifdef _USE_OPENSSL + SSL_CTX *sip_trp_ssl_ctx = NULL; /* For SSL cserver context */ + SSL_CTX *sip_trp_ssl_ctx_client = NULL; /* For SSL cserver context */ +@@ -56,7 +68,6 @@ int passwd_call_back_routine(char *buf , int size , int flag, void *passwd) + } + #endif + +-unsigned long calls_since_last_rate_change = 0; + bool do_hide = true; + bool show_index = false; + +@@ -114,6 +125,11 @@ struct sipp_option { + #define SIPP_OPTION_DEFAULTS 31 + #define SIPP_OPTION_OOC_SCENARIO 32 + #define SIPP_OPTION_INDEX_FILE 33 ++#define SIPP_OPTION_VAR 34 ++#define SIPP_OPTION_RTCHECK 35 ++#define SIPP_OPTION_LFNAME 36 ++#define SIPP_OPTION_LFOVERWRITE 37 ++#define SIPP_OPTION_PLUGIN 38 + + /* Put Each option, its help text, and type in this table. */ + struct sipp_option options_table[] = { +@@ -135,6 +151,9 @@ struct sipp_option options_table[] = { + {"bind_local", "Bind socket to local IP address, i.e. the local IP address is used as the source IP address. If SIPp runs in server mode it will only listen on the local IP address instead of all IP addresses.", SIPP_OPTION_SETFLAG, &bind_local, 1}, + {"buff_size", "Set the send and receive buffer size.", SIPP_OPTION_INT, &buff_size, 1}, + ++ {"calldebug_file", "Set the name of the call debug file.", SIPP_OPTION_LFNAME, &calldebug_lfi, 1}, ++ {"calldebug_overwrite", "Overwrite the call debug file (default true).", SIPP_OPTION_LFOVERWRITE, &calldebug_lfi, 1}, ++ + {"cid_str", "Call ID string (default %u-%p@%s). %u=call_number, %s=ip_address, %p=process_number, %%=% (in any order).", SIPP_OPTION_STRING, &call_id_string, 1}, + {"ci", "Set the local control IP address", SIPP_OPTION_IP, control_ip, 1}, + {"cp", "Set the local control port number. Default is 8888.", SIPP_OPTION_INT, &control_port, 1}, +@@ -150,6 +169,9 @@ struct sipp_option options_table[] = { + "If a behavior is prefaced with a -, then it is turned off. Example: all,-bye\n", + SIPP_OPTION_DEFAULTS, &default_behaviors, 1}, + ++ {"error_file", "Set the name of the error log file.", SIPP_OPTION_LFNAME, &error_lfi, 1}, ++ {"error_overwrite", "Overwrite the error log file (default true).", SIPP_OPTION_LFOVERWRITE, &error_lfi, 1}, ++ + {"f", "Set the statistics report frequency on screen. Default is 1 and default unit is seconds.", SIPP_OPTION_TIME_SEC, &report_freq, 1}, + {"fd", "Set the statistics dump log report frequency. Default is 60 and default unit is seconds.", SIPP_OPTION_TIME_SEC, &report_freq_dumpLog, 1}, + +@@ -167,9 +189,13 @@ struct sipp_option options_table[] = { + {"l", "Set the maximum number of simultaneous calls. Once this limit is reached, traffic is decreased until the number of open calls goes down. Default:\n" + " (3 * call_duration (s) * rate).", SIPP_OPTION_LIMIT, NULL, 1}, + ++ {"log_file", "Set the name of the log actions log file.", SIPP_OPTION_LFNAME, &log_lfi, 1}, ++ {"log_overwrite", "Overwrite the log actions log file (default true).", SIPP_OPTION_LFOVERWRITE, &log_lfi, 1}, ++ + {"lost", "Set the number of packets to lose by default (scenario specifications override this value).", SIPP_OPTION_FLOAT, &global_lost, 1}, ++ {"rtcheck", "Select the retransmisison detection method: full (default) or loose.", SIPP_OPTION_RTCHECK, &rtcheck, 1}, + {"m", "Stop the test and exit when 'calls' calls are processed", SIPP_OPTION_LONG, &stop_after, 1}, +- {"mi", "Set the local media IP address", SIPP_OPTION_IP, media_ip, 1}, ++ {"mi", "Set the local media IP address (default: local primary host IP address)", SIPP_OPTION_IP, media_ip, 1}, + {"master","3pcc extended mode: indicates the master number", SIPP_OPTION_3PCC_EXTENDED, &master_name, 1}, + {"max_recv_loops", "Set the maximum number of messages received read per cycle. Increase this value for high traffic level. The default value is 1000.", SIPP_OPTION_INT, &max_recv_loops, 1}, + {"max_sched_loops", "Set the maximum number of calsl run per event loop. Increase this value for high traffic level. The default value is 1000.", SIPP_OPTION_INT, &max_sched_loops, 1}, +@@ -181,6 +207,8 @@ struct sipp_option options_table[] = { + {"max_socket", "Set the max number of sockets to open simultaneously. This option is significant if you use one socket per call. Once this limit is reached, traffic is distributed over the sockets already opened. Default value is 50000", SIPP_OPTION_MAX_SOCKET, NULL, 1}, + + {"mb", "Set the RTP echo buffer size (default: 2048).", SIPP_OPTION_INT, &media_bufsize, 1}, ++ {"message_file", "Set the name of the message log file.", SIPP_OPTION_LFNAME, &message_lfi, 1}, ++ {"message_overwrite", "Overwrite the message log file (default true).", SIPP_OPTION_LFOVERWRITE, &message_lfi, 1}, + {"mp", "Set the local RTP echo port number. Default is 6000.", SIPP_OPTION_INT, &user_media_port, 1}, + + {"nd", "No Default. Disable all default behavior of SIPp which are the following:\n" +@@ -198,6 +226,7 @@ struct sipp_option options_table[] = { + {"p", "Set the local port number. Default is a random free port chosen by the system.", SIPP_OPTION_INT, &user_port, 1}, + {"pause_msg_ign", "Ignore the messages received during a pause defined in the scenario ", SIPP_OPTION_SETFLAG, &pause_msg_ign, 1}, + {"periodic_rtd", "Reset response time partition counters each logging interval.", SIPP_OPTION_SETFLAG, &periodic_rtd, 1}, ++ {"plugin", "Load a plugin.", SIPP_OPTION_PLUGIN, NULL, 1}, + + {"r", "Set the call rate (in calls per seconds). This value can be" + "changed during test by pressing '+','_','*' or '/'. Default is 10.\n" +@@ -218,6 +247,7 @@ struct sipp_option options_table[] = { + {"no_rate_quit", "If -rate_increase is set, do not quit after the rate reaches -rate_max.", SIPP_OPTION_UNSETFLAG, &rate_quit, 1}, + {"recv_timeout", "Global receive timeout. Default unit is milliseconds. If the expected message is not received, the call times out and is aborted.", SIPP_OPTION_TIME_MS_LONG, &defl_recv_timeout, 1}, + {"send_timeout", "Global send timeout. Default unit is milliseconds. If a message is not sent (due to congestion), the call times out and is aborted.", SIPP_OPTION_TIME_MS_LONG, &defl_send_timeout, 1}, ++ {"sleep", "How long to sleep for at startup. Default unit is seconds.", SIPP_OPTION_TIME_SEC, &sleeptime, 1}, + {"reconnect_close", "Should calls be closed on reconnect?", SIPP_OPTION_BOOL, &reset_close, 1}, + {"reconnect_sleep", "How long (in milliseconds) to sleep between the close and reconnect?", SIPP_OPTION_TIME_MS, &reset_sleep, 1}, + {"ringbuffer_files", "How many error/message files should be kept after rotation?", SIPP_OPTION_INT, &ringbuffer_files, 1}, +@@ -231,6 +261,8 @@ struct sipp_option options_table[] = { + {"s", "Set the username part of the resquest URI. Default is 'service'.", SIPP_OPTION_STRING, &service, 1}, + {"sd", "Dumps a default scenario (embeded in the sipp executable)", SIPP_OPTION_SCENARIO, NULL, 0}, + {"sf", "Loads an alternate xml scenario file. To learn more about XML scenario syntax, use the -sd option to dump embedded scenarios. They contain all the necessary help.", SIPP_OPTION_SCENARIO, NULL, 2}, ++ {"shortmessage_file", "Set the name of the short message log file.", SIPP_OPTION_LFNAME, &shortmessage_lfi, 1}, ++ {"shortmessage_overwrite", "Overwrite the short message log file (default true).", SIPP_OPTION_LFOVERWRITE, &shortmessage_lfi, 1}, + {"oocsf", "Load out-of-call scenario.", SIPP_OPTION_OOC_SCENARIO, NULL, 2}, + {"oocsn", "Load out-of-call scenario.", SIPP_OPTION_OOC_SCENARIO, NULL, 2}, + {"skip_rlimit", "Do not perform rlimit tuning of file descriptor limits. Default: false.", SIPP_OPTION_SETFLAG, &skip_rlimit, 1}, +@@ -265,6 +297,7 @@ struct sipp_option options_table[] = { + , SIPP_OPTION_TRANSPORT, NULL, 1}, + + {"timeout", "Global timeout. Default unit is seconds. If this option is set, SIPp quits after nb units (-timeout 20s quits after 20 seconds).", SIPP_OPTION_TIME_SEC, &global_timeout, 1}, ++ {"timeout_error", "SIPp fails if the global timeout is reached is set (-timeout option required).", SIPP_OPTION_SETFLAG, &timeout_error, 1}, + {"timer_resol", "Set the timer resolution. Default unit is milliseconds. This option has an impact on timers precision." + "Small values allow more precise scheduling but impacts CPU usage." + "If the compression is on, the value is set to 50ms. The default value is 10ms.", SIPP_OPTION_TIME_MS, &timer_resolution, 1}, +@@ -276,6 +309,7 @@ struct sipp_option options_table[] = { + {"trace_screen", "Dump statistic screens in the __screens.log file when quitting SIPp. Useful to get a final status report in background mode (-bg option).", SIPP_OPTION_SETFLAG, &useScreenf, 1}, + {"trace_err", "Trace all unexpected messages in __errors.log.", SIPP_OPTION_SETFLAG, &print_all_responses, 1}, + // {"trace_timeout", "Displays call ids for calls with timeouts in __timeout.log", SIPP_OPTION_SETFLAG, &useTimeoutf, 1}, ++ {"trace_calldebug", "Dumps debugging information about aborted calls to __calldebug.log file.", SIPP_OPTION_SETFLAG, &useCallDebugf, 1}, + {"trace_stat", "Dumps all statistics in _.csv file. Use the '-h stat' option for a detailed description of the statistics file content.", SIPP_OPTION_SETFLAG, &dumpInFile, 1}, + {"trace_counts", "Dumps individual message counts in a CSV file.", SIPP_OPTION_SETFLAG, &useCountf, 1}, + {"trace_rtt", "Allow tracing of all response times in __rtt.csv.", SIPP_OPTION_SETFLAG, &dumpInRtt, 1}, +@@ -283,6 +317,13 @@ struct sipp_option options_table[] = { + + {"users", "Instead of starting calls at a fixed rate, begin 'users' calls at startup, and keep the number of calls constant.", SIPP_OPTION_USERS, NULL, 1}, + ++ {"watchdog_interval", "Set gap between watchdog timer firings. Default is 400.", SIPP_OPTION_TIME_MS, &watchdog_interval, 1}, ++ {"watchdog_reset", "If the watchdog timer has not fired in more than this time period, then reset the max triggers counters. Default is 10 minutes.", SIPP_OPTION_TIME_MS, &watchdog_reset, 1}, ++ {"watchdog_minor_threshold", "If it has been longer than this period between watchdog executions count a minor trip. Default is 500.", SIPP_OPTION_TIME_MS, &watchdog_minor_threshold, 1}, ++ {"watchdog_major_threshold", "If it has been longer than this period between watchdog executions count a major trip. Default is 3000.", SIPP_OPTION_TIME_MS, &watchdog_major_threshold, 1}, ++ {"watchdog_major_maxtriggers", "How many times the major watchdog timer can be tripped before the test is terminated. Default is 10.", SIPP_OPTION_INT, &watchdog_major_maxtriggers, 1}, ++ {"watchdog_minor_maxtriggers", "How many times the minor watchdog timer can be tripped before the test is terminated. Default is 120.", SIPP_OPTION_INT, &watchdog_minor_maxtriggers, 1}, ++ + #ifdef _USE_OPENSSL + {"ap", "Set the password for authentication challenges. Default is 'password", SIPP_OPTION_STRING, &auth_password, 1}, + {"tls_cert", "Set the name for TLS Certificate file. Default is 'cacert.pem", SIPP_OPTION_STRING, &tls_cert_name, 1}, +@@ -303,6 +344,10 @@ struct sipp_option options_table[] = { + "A circuit must be available for the call to be placed.\n" + "Format: -tdmmap {0-3}{99}{5-8}{1-31}", SIPP_OPTION_TDMMAP, NULL, 1}, + {"key", "keyword value\nSet the generic parameter named \"keyword\" to \"value\".", SIPP_OPTION_KEY, NULL, 1}, ++ {"set", "variable value\nSet the global variable parameter named \"variable\" to \"value\".", SIPP_OPTION_VAR, NULL, 3}, ++ {"dynamicStart", "variable value\nSet the start offset of dynamic_id varaiable", SIPP_OPTION_INT, &startDynamicId, 1}, ++ {"dynamicMax", "variable value\nSet the maximum of dynamic_id variable ", SIPP_OPTION_INT, &maxDynamicId, 1}, ++ {"dynamicStep", "variable value\nSet the increment of dynamic_id variable", SIPP_OPTION_INT, &stepDynamicId, 1} + }; + + struct sipp_option *find_option(const char *option) { +@@ -340,6 +385,8 @@ unsigned long long getmicroseconds() + if (!VI_micro_base) VI_micro_base = VI_micro - 1; + VI_micro = VI_micro - VI_micro_base; + ++ clock_tick = VI_micro / 1000LL; ++ + return VI_micro; + } + +@@ -678,7 +725,6 @@ int get_decimal_from_hex(char hex) { + /******************** Recv Poll Processing *********************/ + + int pollnfds; +-unsigned int call_sockets; + struct pollfd pollfiles[SIPP_MAXFDS]; + struct sipp_socket *sockets[SIPP_MAXFDS]; + +@@ -706,7 +752,6 @@ bool sipMsgCheck (const char *P_msg, int P_msgSize, struct sipp_socket *socket) + + void print_stats_in_file(FILE * f, int last) + { +- int index; + static char temp_str[256]; + int divisor; + +@@ -731,25 +776,26 @@ void print_stats_in_file(FILE * f, int last) + sprintf(temp_str, "%3.1f(%d ms)/%5.3fs", rate, duration, (double)rate_period_ms / 1000.0); + } + unsigned long long total_calls = display_scenario->stats->GetStat(CStat::CPT_C_IncomingCallCreated) + display_scenario->stats->GetStat(CStat::CPT_C_OutgoingCallCreated); +- if( toolMode == MODE_SERVER) { ++ if( creationMode == MODE_SERVER) { + fprintf + (f, + " Port Total-time Total-calls Transport" + SIPP_ENDL +- " %-5d %6d.%02d s %8llu %s" ++ " %-5d %6lu.%02lu s %8llu %s" + SIPP_ENDL SIPP_ENDL, + local_port, + clock_tick / 1000, (clock_tick % 1000) / 10, + total_calls, + TRANSPORT_TO_STRING(transport)); + } else { ++ assert(creationMode == MODE_CLIENT); + if (users >= 0) { + fprintf(f, " Users (length)"); + } else { + fprintf(f, " Call-rate(length)"); + } + fprintf(f, " Port Total-time Total-calls Remote-host" SIPP_ENDL +- "%19s %-5d %6d.%02d s %8llu %s:%d(%s)" SIPP_ENDL SIPP_ENDL, ++ "%19s %-5d %6lu.%02lu s %8llu %s:%d(%s)" SIPP_ENDL SIPP_ENDL, + temp_str, + local_port, + clock_tick / 1000, (clock_tick % 1000) / 10, +@@ -773,13 +819,13 @@ void print_stats_in_file(FILE * f, int last) + ((clock_tick-last_report_time) % 1000)); + } + divisor = scheduling_loops; if(!divisor) { divisor = 1; } +- fprintf(f," %-38s %d ms scheduler resolution" ++ fprintf(f," %-38s %lu ms scheduler resolution" + SIPP_ENDL, + temp_str, + (clock_tick-last_report_time) / divisor); + + /* 2nd line */ +- if( toolMode == MODE_SERVER) { ++ if( creationMode == MODE_SERVER) { + sprintf(temp_str, "%llu calls", display_scenario->stats->GetStat(CStat::CPT_C_CurrentCall)); + } else { + sprintf(temp_str, "%llu calls (limit %d)", display_scenario->stats->GetStat(CStat::CPT_C_CurrentCall), open_calls_allowed); +@@ -796,7 +842,7 @@ void print_stats_in_file(FILE * f, int last) + sprintf(temp_str,"%llu dead call msg (discarded)", + display_scenario->stats->GetStat(CStat::CPT_G_C_DeadCallMsgs)); + fprintf(f," %-37s", temp_str); +- if( toolMode != MODE_SERVER) { ++ if( creationMode == MODE_CLIENT) { + sprintf(temp_str,"%llu out-of-call msg (discarded)", + display_scenario->stats->GetStat(CStat::CPT_G_C_OutOfCallMsgs)); + fprintf(f," %-37s", temp_str); +@@ -814,7 +860,7 @@ void print_stats_in_file(FILE * f, int last) + pollnfds); + fprintf(f," %-38s", temp_str); + if(nb_net_recv_errors || nb_net_send_errors || nb_net_cong) { +- fprintf(f," %d/%d/%d %s errors (send/recv/cong)" SIPP_ENDL, ++ fprintf(f," %lu/%lu/%lu %s errors (send/recv/cong)" SIPP_ENDL, + nb_net_send_errors, + nb_net_recv_errors, + nb_net_cong, +@@ -829,7 +875,7 @@ void print_stats_in_file(FILE * f, int last) + sprintf(temp_str, "%lu Total RTP pckts sent ", + rtp_pckts_pcap); + if (clock_tick-last_report_time) { +- fprintf(f," %-38s %d.%03d last period RTP rate (kB/s)" SIPP_ENDL, ++ fprintf(f," %-38s %lu.%03lu last period RTP rate (kB/s)" SIPP_ENDL, + temp_str, + (rtp_bytes_pcap)/(clock_tick-last_report_time), + (rtp_bytes_pcap)%(clock_tick-last_report_time)); +@@ -846,7 +892,7 @@ void print_stats_in_file(FILE * f, int last) + + // AComment: Fix for random coredump when using RTP echo + if (clock_tick-last_report_time) { +- fprintf(f," %-38s %d.%03d last period RTP rate (kB/s)" SIPP_ENDL, ++ fprintf(f," %-38s %lu.%03lu last period RTP rate (kB/s)" SIPP_ENDL, + temp_str, + (rtp_bytes)/(clock_tick-last_report_time), + (rtp_bytes)%(clock_tick-last_report_time)); +@@ -857,7 +903,7 @@ void print_stats_in_file(FILE * f, int last) + + // AComment: Fix for random coredump when using RTP echo + if (clock_tick-last_report_time) { +- fprintf(f," %-38s %d.%03d last period RTP rate (kB/s)" SIPP_ENDL, ++ fprintf(f," %-38s %lu.%03lu last period RTP rate (kB/s)" SIPP_ENDL, + temp_str, + (rtp2_bytes)/(clock_tick-last_report_time), + (rtp2_bytes)%(clock_tick-last_report_time)); +@@ -877,8 +923,8 @@ void print_stats_in_file(FILE * f, int last) + "Messages Retrans Timeout Unexp. Lost" + SIPP_ENDL); + } +- for(index = 0; +- index < display_scenario->length; ++ for(unsigned long index = 0; ++ index < display_scenario->messages.size(); + index ++) { + message *curmsg = display_scenario->messages[index]; + +@@ -886,7 +932,7 @@ void print_stats_in_file(FILE * f, int last) + continue; + } + if (show_index) { +- fprintf(f, "%-02d:", index); ++ fprintf(f, "%-2lu:", index); + } + + if(SendingMessage *src = curmsg -> send_scheme) { +@@ -896,7 +942,7 @@ void print_stats_in_file(FILE * f, int last) + sprintf(temp_str, "%s", src->getMethod()); + } + +- if(toolMode == MODE_SERVER) { ++ if(creationMode == MODE_SERVER) { + fprintf(f," <---------- %-10s ", temp_str); + } else { + fprintf(f," %10s ----------> ", temp_str); +@@ -910,20 +956,20 @@ void print_stats_in_file(FILE * f, int last) + } + + if(curmsg -> retrans_delay) { +- fprintf(f,"%-9d %-9d %-9d %-9s" , ++ fprintf(f,"%-9lu %-9lu %-9lu %-9s" , + curmsg -> nb_sent, + curmsg -> nb_sent_retrans, + curmsg -> nb_timeout, + "" /* Unexpected */); + } else { +- fprintf(f,"%-9d %-9d %-9s %-9s" , ++ fprintf(f,"%-9lu %-9lu %-9s %-9s" , + curmsg -> nb_sent, + curmsg -> nb_sent_retrans, + "", /* Timeout. */ + "" /* Unexpected. */); + } + } else if(curmsg -> recv_response) { +- if(toolMode == MODE_SERVER) { ++ if(creationMode == MODE_SERVER) { + fprintf(f," ----------> %-10d ", curmsg -> recv_response); + } else { + fprintf(f," %10d <---------- ", curmsg -> recv_response); +@@ -966,16 +1012,16 @@ void print_stats_in_file(FILE * f, int last) + } + int len = strlen(desc) < 9 ? 9 : strlen(desc); + +- if(toolMode == MODE_SERVER) { ++ if(creationMode == MODE_SERVER) { + fprintf(f," [%9s] Pause%*s", desc, 23 - len > 0 ? 23 - len : 0, ""); + } else { + fprintf(f," Pause [%9s]%*s", desc, 18 - len > 0 ? 18 - len : 0, ""); + } + + fprintf(f,"%-9d", curmsg->sessions); +- fprintf(f," %-9d" , curmsg->nb_unexp); ++ fprintf(f," %-9lu" , curmsg->nb_unexp); + } else if(curmsg -> recv_request) { +- if(toolMode == MODE_SERVER) { ++ if(creationMode == MODE_SERVER) { + fprintf(f," ----------> %-10s ", curmsg -> recv_request); + } else { + fprintf(f," %10s <---------- ", curmsg -> recv_request); +@@ -1018,7 +1064,7 @@ void print_stats_in_file(FILE * f, int last) + } + } else if(curmsg -> M_type == MSG_TYPE_SENDCMD) { + fprintf(f," [ Sent Command ] "); +- fprintf(f,"%-9d %-9s %-9s" , ++ fprintf(f,"%-9lu %-9s %-9s" , + curmsg -> M_nbCmdSent, + "", + ""); +@@ -1028,7 +1074,7 @@ void print_stats_in_file(FILE * f, int last) + } + + if(lose_packets && (curmsg -> nb_lost)) { +- fprintf(f," %-9d" SIPP_ENDL, ++ fprintf(f," %-9lu" SIPP_ENDL, + curmsg -> nb_lost); + } else { + fprintf(f,SIPP_ENDL); +@@ -1043,18 +1089,22 @@ void print_stats_in_file(FILE * f, int last) + void print_count_file(FILE *f, int header) { + char temp_str[256]; + ++ if (!main_scenario || (!header && !main_scenario->stats)) { ++ return; ++ } ++ + if (header) { + fprintf(f, "CurrentTime%sElapsedTime%s", stat_delimiter, stat_delimiter); + } else { + struct timeval currentTime, startTime; + GET_TIME(¤tTime); +- display_scenario->stats->getStartTime(&startTime); ++ main_scenario->stats->getStartTime(&startTime); + unsigned long globalElapsedTime = CStat::computeDiffTimeInMs (¤tTime, &startTime); + fprintf(f, "%s%s", CStat::formatTime(¤tTime), stat_delimiter); + fprintf(f, "%s%s", CStat::msToHHMMSSmmm(globalElapsedTime), stat_delimiter); + } + +- for(int index = 0; index < main_scenario->length; index ++) { ++ for(unsigned int index = 0; index < main_scenario->messages.size(); index ++) { + message *curmsg = main_scenario->messages[index]; + if(curmsg->hide) { + continue; +@@ -1077,18 +1127,18 @@ void print_count_file(FILE *f, int header) { + fprintf(f, "%sLost%s", temp_str, stat_delimiter); + } + } else { +- fprintf(f, "%d%s", curmsg->nb_sent, stat_delimiter); +- fprintf(f, "%d%s", curmsg->nb_sent_retrans, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->nb_sent, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->nb_sent_retrans, stat_delimiter); + if(curmsg -> retrans_delay) { +- fprintf(f, "%d%s", curmsg->nb_timeout, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->nb_timeout, stat_delimiter); + } + if(lose_packets) { +- fprintf(f, "%d%s", curmsg->nb_lost, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->nb_lost, stat_delimiter); + } + } + } else if(curmsg -> recv_response) { + if(header) { +- sprintf(temp_str, "%d_%d_", index, curmsg->recv_response); ++ sprintf(temp_str, "%u_%d_", index, curmsg->recv_response); + + fprintf(f, "%sRecv%s", temp_str, stat_delimiter); + fprintf(f, "%sRetrans%s", temp_str, stat_delimiter); +@@ -1098,17 +1148,17 @@ void print_count_file(FILE *f, int header) { + fprintf(f, "%sLost%s", temp_str, stat_delimiter); + } + } else { +- fprintf(f, "%d%s", curmsg->nb_recv, stat_delimiter); +- fprintf(f, "%d%s", curmsg->nb_recv_retrans, stat_delimiter); +- fprintf(f, "%d%s", curmsg->nb_timeout, stat_delimiter); +- fprintf(f, "%d%s", curmsg->nb_unexp, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->nb_recv, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->nb_recv_retrans, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->nb_timeout, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->nb_unexp, stat_delimiter); + if(lose_packets) { +- fprintf(f, "%d%s", curmsg->nb_lost, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->nb_lost, stat_delimiter); + } + } + } else if(curmsg -> recv_request) { + if(header) { +- sprintf(temp_str, "%d_%s_", index, curmsg->recv_request); ++ sprintf(temp_str, "%u_%s_", index, curmsg->recv_request); + + fprintf(f, "%sRecv%s", temp_str, stat_delimiter); + fprintf(f, "%sRetrans%s", temp_str, stat_delimiter); +@@ -1118,12 +1168,12 @@ void print_count_file(FILE *f, int header) { + fprintf(f, "%sLost%s", temp_str, stat_delimiter); + } + } else { +- fprintf(f, "%d%s", curmsg->nb_recv, stat_delimiter); +- fprintf(f, "%d%s", curmsg->nb_recv_retrans, stat_delimiter); +- fprintf(f, "%d%s", curmsg->nb_timeout, stat_delimiter); +- fprintf(f, "%d%s", curmsg->nb_unexp, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->nb_recv, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->nb_recv_retrans, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->nb_timeout, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->nb_unexp, stat_delimiter); + if(lose_packets) { +- fprintf(f, "%d%s", curmsg->nb_lost, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->nb_lost, stat_delimiter); + } + } + } else if (curmsg -> pause_distribution || +@@ -1135,7 +1185,7 @@ void print_count_file(FILE *f, int header) { + fprintf(f, "%sUnexp%s", temp_str, stat_delimiter); + } else { + fprintf(f, "%d%s", curmsg->sessions, stat_delimiter); +- fprintf(f, "%d%s", curmsg->nb_unexp, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->nb_unexp, stat_delimiter); + } + } else if(curmsg -> M_type == MSG_TYPE_NOP) { + /* No output. */ +@@ -1145,15 +1195,15 @@ void print_count_file(FILE *f, int header) { + fprintf(f, "%s%s", temp_str, stat_delimiter); + fprintf(f, "%s_Timeout%s", temp_str, stat_delimiter); + } else { +- fprintf(f, "%d%s", curmsg->M_nbCmdRecv, stat_delimiter); +- fprintf(f, "%d%s", curmsg->nb_timeout, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->M_nbCmdRecv, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->nb_timeout, stat_delimiter); + } + } else if(curmsg -> M_type == MSG_TYPE_SENDCMD) { + if(header) { + sprintf(temp_str, "%d_SendCmd", index); +- fprintf(f, "%s%s", temp_str); ++ fprintf(f, "%s%s", temp_str, stat_delimiter); + } else { +- fprintf(f, "%d%s", curmsg->M_nbCmdSent, stat_delimiter); ++ fprintf(f, "%lu%s", curmsg->M_nbCmdSent, stat_delimiter); + } + } else { + ERROR("Unknown count file message type:"); +@@ -1202,22 +1252,28 @@ void print_bottom_line(FILE *f, int last) + } else if(outbound_congestion) { + fprintf(f,"------------------------------ OUTBOUND CONGESTION -----------------------------" SIPP_ENDL); + } else { +- switch(toolMode) +- { +- case MODE_SERVER : +- fprintf(f,"------------------------------ Sipp Server Mode -------------------------------" SIPP_ENDL); ++ if (creationMode == MODE_CLIENT) { ++ switch(thirdPartyMode) { ++ case MODE_MASTER : ++ fprintf(f,"-----------------------3PCC extended mode - Master side -------------------------" SIPP_ENDL); + break; +- case MODE_3PCC_CONTROLLER_B : +- fprintf(f,"----------------------- 3PCC Mode - Controller B side -------------------------" SIPP_ENDL); ++ case MODE_3PCC_CONTROLLER_A : ++ fprintf(f,"----------------------- 3PCC Mode - Controller A side -------------------------" SIPP_ENDL); + break; ++ case MODE_3PCC_NONE: ++ fprintf(f,"------ [+|-|*|/]: Adjust rate ---- [q]: Soft exit ---- [p]: Pause traffic -----" SIPP_ENDL); ++ break; ++ default: ++ ERROR("Internal error: creationMode=%d, thirdPartyMode=%d", creationMode, thirdPartyMode); ++ } ++ } else { ++ assert(creationMode == MODE_SERVER); ++ switch(thirdPartyMode) { + case MODE_3PCC_A_PASSIVE : + fprintf(f,"------------------ 3PCC Mode - Controller A side (passive) --------------------" SIPP_ENDL); + break; +- case MODE_3PCC_CONTROLLER_A : +- fprintf(f,"----------------------- 3PCC Mode - Controller A side -------------------------" SIPP_ENDL); +- break; +- case MODE_MASTER : +- fprintf(f,"-----------------------3PCC extended mode - Master side -------------------------" SIPP_ENDL); ++ case MODE_3PCC_CONTROLLER_B : ++ fprintf(f,"----------------------- 3PCC Mode - Controller B side -------------------------" SIPP_ENDL); + break; + case MODE_MASTER_PASSIVE : + fprintf(f,"------------------ 3PCC extended mode - Master side (passive) --------------------" SIPP_ENDL); +@@ -1225,11 +1281,13 @@ void print_bottom_line(FILE *f, int last) + case MODE_SLAVE : + fprintf(f,"----------------------- 3PCC extended mode - Slave side -------------------------" SIPP_ENDL); + break; +- case MODE_CLIENT : ++ case MODE_3PCC_NONE: ++ fprintf(f,"------------------------------ Sipp Server Mode -------------------------------" SIPP_ENDL); ++ break; + default: +- fprintf(f,"------ [+|-|*|/]: Adjust rate ---- [q]: Soft exit ---- [p]: Pause traffic -----" SIPP_ENDL); +- break; ++ ERROR("Internal error: creationMode=%d, thirdPartyMode=%d", creationMode, thirdPartyMode); + } ++ } + } + fprintf(f,SIPP_ENDL); + fflush(stdout); +@@ -1256,7 +1314,7 @@ void print_tdm_map() + printf(SIPP_ENDL); + printf("%d/%d circuits (%d%%) in use", in_use, interval, int(100*in_use/interval)); + printf(SIPP_ENDL); +- for(int i=0; i<(display_scenario->length + 8 - int(interval/(tdm_map_c+1))); i++) { ++ for(unsigned int i=0; i<(display_scenario->messages.size() + 8 - int(interval/(tdm_map_c+1))); i++) { + printf(SIPP_ENDL); + } + } +@@ -1265,14 +1323,13 @@ void print_variable_list() + { + CActions * actions; + CAction * action; +- int i,j; + int printed = 0; + bool found; + + printf("Action defined Per Message :" SIPP_ENDL); + printed++; + found = false; +- for(i=0; ilength; i++) ++ for(unsigned int i=0; imessages.size(); i++) + { + message *curmsg = display_scenario->messages[i]; + actions = curmsg->M_actions; +@@ -1322,7 +1379,7 @@ void print_variable_list() + } + + printf(SIPP_ENDL); +- for(i=0; i<(display_scenario->length + 5 - printed); i++) { ++ for(unsigned int i=0; i<(display_scenario->messages.size() + 5 - printed); i++) { + printf(SIPP_ENDL); + } + } +@@ -1349,10 +1406,9 @@ void print_screens(void) + print_bottom_line( screenf, 0); + + currentScreenToDisplay = DISPLAY_SECONDARY_REPARTITION_SCREEN; +- for (int i = 1; i <= MAX_RTD_INFO_LENGTH; i++) { +- currentRepartitionToDisplay = i; ++ for (currentRepartitionToDisplay = 2; currentRepartitionToDisplay <= display_scenario->stats->nRtds(); currentRepartitionToDisplay++) { + print_header_line( screenf, 0); +- display_scenario->stats->displaySecondaryRepartition(screenf, i-1); ++ display_scenario->stats->displayRtdRepartition(screenf, currentRepartitionToDisplay); + print_bottom_line( screenf, 0); + } + +@@ -1392,7 +1448,7 @@ void print_statistics(int last) + print_tdm_map(); + break; + case DISPLAY_SECONDARY_REPARTITION_SCREEN : +- display_scenario->stats->displaySecondaryRepartition(stdout, currentRepartitionToDisplay - 1); ++ display_scenario->stats->displayRtdRepartition(stdout, currentRepartitionToDisplay); + break; + case DISPLAY_SCENARIO_SCREEN : + default: +@@ -1427,71 +1483,6 @@ void print_statistics(int last) + } + } + +-void set_rate(double new_rate) +-{ +- if(toolMode == MODE_SERVER) { +- rate = 0; +- open_calls_allowed = 0; +- } +- +- rate = new_rate; +- if(rate < 0) { +- rate = 0; +- } +- +- last_rate_change_time = clock_tick; +- calls_since_last_rate_change = 0; +- +- if(!open_calls_user_setting) { +- +- int call_duration_min = display_scenario->duration; +- +- if(duration > call_duration_min) call_duration_min = duration; +- +- if(call_duration_min < 1000) call_duration_min = 1000; +- +- open_calls_allowed = (int)((3.0 * rate * call_duration_min) / (double)rate_period_ms); +- if(!open_calls_allowed) { +- open_calls_allowed = 1; +- } +- } +-} +- +-void set_users(int new_users) +-{ +- if (new_users < 0) { +- new_users = 0; +- } +- assert(users >= 0); +- +- if(toolMode == MODE_SERVER) { +- rate = 0; +- open_calls_allowed = 0; +- } +- +- if (users < new_users ) { +- while (users < new_users) { +- int userid; +- if (!retiredUsers.empty()) { +- userid = retiredUsers.back(); +- retiredUsers.pop_back(); +- } else { +- userid = users + 1; +- userVarMap[userid] = new VariableTable(userVariables); +- } +- freeUsers.push_front(userid); +- users++; +- } +- } +- +- users = open_calls_allowed = new_users; +- +- last_rate_change_time = clock_tick; +- calls_since_last_rate_change = 0; +- +- assert(open_calls_user_setting); +-} +- + void sipp_sigusr1(int /* not used */) + { + /* Smooth exit: do not place any new calls and exit */ +@@ -1540,56 +1531,51 @@ bool process_key(int c) { + case '8': + case '9': + currentScreenToDisplay = DISPLAY_SECONDARY_REPARTITION_SCREEN; +- currentRepartitionToDisplay = (c - '6') + 1; ++ currentRepartitionToDisplay = (c - '6') + 2; + print_statistics(0); + break; + + case '+': + if (users >= 0) { +- set_users((int)(users + 1 * rate_scale)); ++ opentask::set_users((int)(users + 1 * rate_scale)); + } else { +- set_rate(rate + 1 * rate_scale); ++ opentask::set_rate(rate + 1 * rate_scale); + } + print_statistics(0); + break; + + case '-': + if (users >= 0) { +- set_users((int)(users - 1 * rate_scale)); ++ opentask::set_users((int)(users - 1 * rate_scale)); + } else { +- set_rate(rate - 1 * rate_scale); ++ opentask::set_rate(rate - 1 * rate_scale); + } + print_statistics(0); + break; + + case '*': + if (users >= 0) { +- set_users((int)(users + 10 * rate_scale)); ++ opentask::set_users((int)(users + 10 * rate_scale)); + } else { +- set_rate(rate + 10 * rate_scale); ++ opentask::set_rate(rate + 10 * rate_scale); + } + print_statistics(0); + break; + + case '/': + if (users >= 0) { +- set_users((int)(users - 10 * rate_scale)); ++ opentask::set_users((int)(users - 10 * rate_scale)); + } else { +- set_rate(rate - 10 * rate_scale); ++ opentask::set_rate(rate - 10 * rate_scale); + } + print_statistics(0); + break; + + case 'p': + if(paused) { +- paused = 0; +- if (users >= 0) { +- set_users(users); +- } else { +- set_rate(rate); +- } ++ opentask::set_paused(false); + } else { +- paused = 1; ++ opentask::set_paused(true); + } + print_statistics(0); + break; +@@ -1645,7 +1631,7 @@ void process_set(char *what) { + } else if (*end) { + WARNING("Invalid rate value: \"%s\"", rest); + } else { +- set_rate(drest); ++ opentask::set_rate(drest); + } + } else if (!strcmp(what, "rate-scale")) { + char *end; +@@ -1666,13 +1652,15 @@ void process_set(char *what) { + } else if (urest < 0) { + WARNING("Invalid users value: \"%s\"", rest); + } else { +- set_users(urest); ++ opentask::set_users(urest); + } + } else if (!strcmp(what, "limit")) { + char *end; + unsigned long lrest = strtoul(rest, &end, 0); +- if (*end) { +- WARNING("Invalid rate-scale value: \"%s\"", rest); ++ if (users >= 0) { ++ WARNING("Can not set call limit for a user-based benchmark."); ++ } else if (*end) { ++ WARNING("Invalid limit value: \"%s\"", rest); + } else { + open_calls_allowed = lrest; + open_calls_user_setting = 1; +@@ -1706,6 +1694,15 @@ void process_set(char *what) { + } + } + ++void log_off(struct logfile_info *lfi) { ++ if (lfi->fptr) { ++ fflush(lfi->fptr); ++ fclose(lfi->fptr); ++ lfi->fptr = NULL; ++ lfi->overwrite = false; ++ } ++} ++ + void process_trace(char *what) { + bool on = false; + char *rest = strchr(what, ' '); +@@ -1742,15 +1739,10 @@ void process_trace(char *what) { + print_all_responses = 1; + } else { + print_all_responses = 0; +- if (screen_errorf) { +- fflush(screen_errorf); +- fclose(screen_errorf); +- screen_errorf = NULL; +- errorf_overwrite = false; +- } ++ log_off(&error_lfi); + } + } else if (!strcmp(what, "logs")) { +- if (on == !!logfile) { ++ if (on == !!log_lfi.fptr) { + return; + } + if (on) { +@@ -1758,13 +1750,10 @@ void process_trace(char *what) { + rotate_logfile(); + } else { + useLogf = 0; +- fflush(logfile); +- fclose(logfile); +- logfile = NULL; +- logfile_overwrite = false; ++ log_off(&log_lfi); + } + } else if (!strcmp(what, "messages")) { +- if (on == !!messagef) { ++ if (on == !!message_lfi.fptr) { + return; + } + if (on) { +@@ -1772,13 +1761,10 @@ void process_trace(char *what) { + rotate_logfile(); + } else { + useMessagef = 0; +- fflush(messagef); +- fclose(messagef); +- messagef = NULL; +- messagef_overwrite = false; ++ log_off(&message_lfi); + } + } else if (!strcmp(what, "shortmessages")) { +- if (on == !!shortmessagef) { ++ if (on == !!shortmessage_lfi.fptr) { + return; + } + +@@ -1787,10 +1773,7 @@ void process_trace(char *what) { + rotate_shortmessagef(); + } else { + useShortMessagef = 0; +- fflush(shortmessagef); +- fclose(shortmessagef); +- shortmessagef = NULL; +- shortmessagef_overwrite = false; ++ log_off(&shortmessage_lfi); + } + } else { + WARNING("Unknown log file: %s", what); +@@ -1800,11 +1783,21 @@ void process_trace(char *what) { + void process_dump(char *what) { + if (!strcmp(what, "tasks")) { + dump_tasks(); ++ } else if (!strcmp(what, "variables")) { ++ display_scenario->allocVars->dump(); + } else { + WARNING("Unknown dump type: %s", what); + } + } + ++void process_reset(char *what) { ++ if (!strcmp(what, "stats")) { ++ main_scenario->stats->computeStat(CStat::E_RESET_C_COUNTERS); ++ } else { ++ WARNING("Unknown reset type: %s", what); ++ } ++} ++ + bool process_command(char *command) { + trim(command); + +@@ -1820,6 +1813,8 @@ bool process_command(char *command) { + process_trace(rest); + } else if (!strcmp(command, "dump")) { + process_dump(rest); ++ } else if (!strcmp(command, "reset")) { ++ process_reset(rest); + } else { + WARNING("Unrecognized command: \"%s\"", command); + } +@@ -1849,10 +1844,10 @@ int handle_ctrl_socket() { + } else { + process_key(bufrcv[0]); + } ++ return 0; + } + + void setup_ctrl_socket() { +- int ret; + int port, firstport; + int try_counter = 60; + struct sockaddr_storage ctl_sa; +@@ -1907,8 +1902,8 @@ void setup_ctrl_socket() { + + if (try_counter == 0) { + if (control_port) { +- ERROR("Unable to bind remote control socket to UDP port %d: %s", +- control_port, strerror(errno)); ++ ERROR_NO("Unable to bind remote control socket to UDP port %d", ++ control_port); + } else { + WARNING("Unable to bind remote control socket (tried UDP ports %d-%d): %s", + firstport, port - 1, strerror(errno)); +@@ -2079,7 +2074,7 @@ char * get_incoming_header_content(char* message, char * name) + return last_header; + } + +- while(src = strstr(src, name)) { ++ while((src = strstr(src, name))) { + + /* just want the header's content */ + src += strlen(name); +@@ -2118,7 +2113,7 @@ char * get_incoming_header_content(char* message, char * name) + } + + /* remove enclosed CRs in multilines */ +- while(ptr = strchr(last_header, '\r')) { ++ while((ptr = strchr(last_header, '\r'))) { + /* Use strlen(ptr) to include trailing zero */ + memmove(ptr, ptr+1, strlen(ptr)); + } +@@ -2130,7 +2125,7 @@ char * get_incoming_first_line(char * message) + { + /* non reentrant. consider accepting char buffer as param */ + static char last_header[MAX_HEADER_LEN * 10]; +- char * src, *dest, *ptr; ++ char * src, *dest; + + /* returns empty string in case of error */ + memset(last_header, 0, sizeof(last_header)); +@@ -2261,6 +2256,7 @@ struct socketbuf *alloc_socketbuf(char *buffer, size_t size, int copy, struct so + if (!socketbuf) { + ERROR("Could not allocate socket buffer!\n"); + } ++ memset(socketbuf, 0, sizeof(struct socketbuf)); + if (copy) { + socketbuf->buf = (char *)malloc(size); + if (!socketbuf->buf) { +@@ -2520,7 +2516,9 @@ static int read_error(struct sipp_socket *socket, int ret) { + #endif + + /* We have only non-blocking reads, so this should not occur. */ +- assert(errno != EAGAIN); ++ if (ret < 0) { ++ assert(errno != EAGAIN); ++ } + + if (socket->ss_transport == T_TCP || socket->ss_transport == T_TLS) { + if (ret == 0) { +@@ -2535,7 +2533,7 @@ static int read_error(struct sipp_socket *socket, int ret) { + quitting += 20; + }else if(twinSippMode) { + if(twinSippSocket) sipp_close_socket(twinSippSocket); +- if(toolMode == MODE_3PCC_CONTROLLER_B) { ++ if(thirdPartyMode == MODE_3PCC_CONTROLLER_B) { + WARNING("3PCC controller A has ended -> exiting"); + quitting += 20; + }else { +@@ -2602,7 +2600,6 @@ static int flush_socket(struct sipp_socket *socket) { + + void buffer_write(struct sipp_socket *socket, char *buffer, size_t len, struct sockaddr_storage *dest) { + struct socketbuf *buf = socket->ss_out; +- struct socketbuf *prev = buf; + + if (!buf) { + socket->ss_out = alloc_socketbuf(buffer, len, DO_COPY, dest); +@@ -2611,11 +2608,10 @@ void buffer_write(struct sipp_socket *socket, char *buffer, size_t len, struct s + } + + while(buf->next) { +- prev = buf; + buf = buf->next; + } + +- prev->next = alloc_socketbuf(buffer, len, DO_COPY, dest); ++ buf->next = alloc_socketbuf(buffer, len, DO_COPY, dest); + TRACE_MSG("Appended buffered message to socket %d\n", socket->ss_fd); + } + +@@ -2639,6 +2635,10 @@ void buffer_read(struct sipp_socket *socket, struct socketbuf *newbuf) { + /* Write data to a socket. */ + int write_socket(struct sipp_socket *socket, char *buffer, ssize_t len, int flags, struct sockaddr_storage *dest) { + int rc; ++ if ( socket == NULL ) { ++ //FIX coredump when trying to send data but no master yet ... ( for example after unexpected mesdsage) ++ return 0; ++ } + + if (socket->ss_out) { + rc = flush_socket(socket); +@@ -2670,6 +2670,7 @@ int write_socket(struct sipp_socket *socket, char *buffer, ssize_t len, int flag + } else if (rc <= 0) { + if ((errno == EWOULDBLOCK) && (flags & WS_BUFFER)) { + buffer_write(socket, buffer, len, dest); ++ enter_congestion(socket, errno); + return len; + } + if (useMessagef == 1) { +@@ -2694,6 +2695,7 @@ int write_socket(struct sipp_socket *socket, char *buffer, ssize_t len, int flag + rc, len, len, buffer); + } + buffer_write(socket, buffer + rc, len - rc, dest); ++ enter_congestion(socket, errno); + } + + return rc; +@@ -2930,10 +2932,6 @@ void sipp_socket_invalidate(struct sipp_socket *socket) { + ERROR("Pollset error: index %d is greater than number of fds %d!", pollidx, pollnfds); + } + +- if (socket->ss_call_socket) { +- call_sockets--; +- } +- + socket->ss_invalid = true; + socket->ss_pollidx = -1; + +@@ -2944,6 +2942,12 @@ void sipp_socket_invalidate(struct sipp_socket *socket) { + pollfiles[pollidx] = pollfiles[pollnfds]; + sockets[pollidx] = sockets[pollnfds]; + sockets[pollidx]->ss_pollidx = pollidx; ++ sockets[pollnfds] = NULL; ++ ++ if (socket->ss_msglen) ++ { ++ pending_messages--; ++ } + } + + void sipp_close_socket (struct sipp_socket *socket) { +@@ -2963,7 +2967,8 @@ static ssize_t read_message(struct sipp_socket *socket, char *buf, size_t len, s + if (!socket->ss_msglen) + return 0; + if (socket->ss_msglen > len) +- ERROR("There is a message waiting in a socket that is bigger (%zd bytes) than the read size.", socket->ss_msglen); ++ ERROR("There is a message waiting in sockfd(%d) that is bigger (%d bytes) than the read size.", ++ socket->ss_fd, socket->ss_msglen); + + len = socket->ss_msglen; + +@@ -3030,29 +3035,14 @@ void process_message(struct sipp_socket *socket, char *msg, ssize_t msg_size, st + if (useShortMessagef == 1) { + struct timeval currentTime; + GET_TIME (¤tTime); +- TRACE_SHORTMSG("%s\tS\t%s\tCSeq:%s\t%s\n", ++ TRACE_SHORTMSG("%s\tR\t%s\tCSeq:%s\t%s\n", + CStat::formatTime(¤tTime),call_id, get_incoming_header_content(msg,"CSeq:"), get_incoming_first_line(msg)); + } + + if(!listener_ptr) + { +- if(toolMode == MODE_SERVER) +- { +- if (quitting >= 1) { +- CStat::globalStat(CStat::E_OUT_OF_CALL_MSGS); +- TRACE_MSG("Discarded message for new calls while quitting\n"); +- return; +- } +- +- // Adding a new INCOMING call ! +- main_scenario->stats->computeStat(CStat::E_CREATE_INCOMING_CALL); +- listener_ptr = new call(call_id, socket, src); +- if (!listener_ptr) { +- ERROR("Out of memory allocating a call!"); +- } +- } +- else if(toolMode == MODE_3PCC_CONTROLLER_B || toolMode == MODE_3PCC_A_PASSIVE +- || toolMode == MODE_MASTER_PASSIVE || toolMode == MODE_SLAVE) ++ if(thirdPartyMode == MODE_3PCC_CONTROLLER_B || thirdPartyMode == MODE_3PCC_A_PASSIVE ++ || thirdPartyMode == MODE_MASTER_PASSIVE || thirdPartyMode == MODE_SLAVE) + { + // Adding a new OUTGOING call ! + main_scenario->stats->computeStat(CStat::E_CREATE_OUTGOING_CALL); +@@ -3087,6 +3077,21 @@ void process_message(struct sipp_socket *socket, char *msg, ssize_t msg_size, st + } + listener_ptr = new_ptr; + } ++ else if(creationMode == MODE_SERVER) ++ { ++ if (quitting >= 1) { ++ CStat::globalStat(CStat::E_OUT_OF_CALL_MSGS); ++ TRACE_MSG("Discarded message for new calls while quitting\n"); ++ return; ++ } ++ ++ // Adding a new INCOMING call ! ++ main_scenario->stats->computeStat(CStat::E_CREATE_INCOMING_CALL); ++ listener_ptr = new call(call_id, socket, use_remote_sending_addr ? &remote_sending_sockaddr : src); ++ if (!listener_ptr) { ++ ERROR("Out of memory allocating a call!"); ++ } ++ } + else // mode != from SERVER and 3PCC Controller B + { + // This is a message that is not relating to any known call +@@ -3097,12 +3102,12 @@ void process_message(struct sipp_socket *socket, char *msg, ssize_t msg_size, st + if(!get_reply_code(msg)){ + ooc_scenario->stats->computeStat(CStat::E_CREATE_INCOMING_CALL); + /* This should have the real address that the message came from. */ +- call *call_ptr = new call(ooc_scenario, socket, src, call_id, 0 /* no user. */, socket->ss_ipv6, true); ++ call *call_ptr = new call(ooc_scenario, socket, use_remote_sending_addr ? &remote_sending_sockaddr : src, call_id, 0 /* no user. */, socket->ss_ipv6, true, false); + if (!call_ptr) { + ERROR("Out of memory allocating a call!"); + } + CStat::globalStat(CStat::E_AUTO_ANSWERED); +- call_ptr->process_incoming(msg); ++ call_ptr->process_incoming(msg, src); + } else { + /* We received a response not relating to any known call */ + /* Do nothing, even if in auto answer mode */ +@@ -3126,11 +3131,11 @@ void process_message(struct sipp_socket *socket, char *msg, ssize_t msg_size, st + } + else + { +- listener_ptr -> process_incoming(msg); ++ listener_ptr -> process_incoming(msg, src); + } + } + +-void pollset_process() ++void pollset_process(int wait) + { + int rs; /* Number of times to execute recv(). + For TCP with 1 socket per call: +@@ -3144,9 +3149,13 @@ void pollset_process() + /* What index should we try reading from? */ + static int read_index; + ++ if (read_index >= pollnfds) { ++ read_index = 0; ++ } ++ + /* We need to process any messages that we have left over. */ +- while (pending_messages && (loops-- > 0)) { +- clock_tick = getmilliseconds(); ++ while (pending_messages && (loops > 0)) { ++ getmilliseconds(); + if (sockets[read_index]->ss_msglen) { + struct sockaddr_storage src; + char msg[SIPP_MAX_MSG_SIZE]; +@@ -3156,6 +3165,7 @@ void pollset_process() + } else { + assert(0); + } ++ loops--; + } + read_index = (read_index + 1) % pollnfds; + } +@@ -3166,7 +3176,7 @@ void pollset_process() + } + + /* Get socket events. */ +- rs = poll(pollfiles, pollnfds, 1); ++ rs = poll(pollfiles, pollnfds, wait ? 1 : 0); + if((rs < 0) && (errno == EINTR)) { + return; + } +@@ -3201,7 +3211,7 @@ void pollset_process() + } else if (sock == stdin_socket) { + handle_stdin_socket(); + } else if (sock == localTwinSippSocket) { +- if (toolMode == MODE_3PCC_CONTROLLER_B) { ++ if (thirdPartyMode == MODE_3PCC_CONTROLLER_B) { + twinSippSocket = sipp_accept_socket(sock); + if (!twinSippMode) { + ERROR_NO("Accepting new TCP connection on Twin SIPp Socket.\n"); +@@ -3225,7 +3235,16 @@ void pollset_process() + } + } else { + if ((ret = empty_socket(sock)) <= 0) { +- read_error(sock, ret); ++ ret = read_error(sock, ret); ++ if (ret == 0) { ++ /* If read_error() then the poll_idx now belongs ++ * to the newest/last socket added to the sockets[]. ++ * Need to re-do the same poll_idx for the "new" socket. */ ++ poll_idx--; ++ events++; ++ rs--; ++ continue; ++ } + } + } + events++; +@@ -3237,9 +3256,13 @@ void pollset_process() + } + } + ++ if (read_index >= pollnfds) { ++ read_index = 0; ++ } ++ + /* We need to process any new messages that we read. */ +- while (pending_messages && (loops-- > 0)) { +- clock_tick = getmilliseconds(); ++ while (pending_messages && (loops > 0)) { ++ getmilliseconds(); + + if (sockets[read_index]->ss_msglen) { + char msg[SIPP_MAX_MSG_SIZE]; +@@ -3252,6 +3275,7 @@ void pollset_process() + } else { + assert(0); + } ++ loops--; + } + read_index = (read_index + 1) % pollnfds; + } +@@ -3260,6 +3284,9 @@ void pollset_process() + } + + void timeout_alarm(int param){ ++ if (timeout_error) { ++ ERROR("%s timed out after '%.3lf' seconds", scenario_file, ((double)clock_tick / 1000LL)); ++ } + quitting = 1; + timeout_exit = true; + } +@@ -3268,33 +3295,26 @@ void timeout_alarm(int param){ + + void traffic_thread() + { +- unsigned int calls_to_open = 0; +- unsigned int new_time; +- unsigned int last_time; +- bool firstPass; +- + /* create the file */ + char L_file_name [MAX_PATH]; + sprintf (L_file_name, "%s_%d_screen.log", scenario_file, getpid()); + ++ getmilliseconds(); + +- firstPass = true; +- last_time = getmilliseconds(); +- + /* Arm the global timer if needed */ + if (global_timeout > 0) { + signal(SIGALRM, timeout_alarm); + alarm(global_timeout / 1000); + } +- +- while(1) { +- scheduling_loops ++; + +- /* update local time, except if resetted*/ +- new_time = getmilliseconds(); ++ // Dump (to create file on disk) and showing screen at the beginning even if ++ // the report period is not reached ++ stattask::report(); ++ screentask::report(false); + +- clock_tick = new_time; +- last_time = new_time; ++ while(1) { ++ scheduling_loops++; ++ getmilliseconds(); + + if (signalDump) { + /* Screen dumping in a file */ +@@ -3324,86 +3344,10 @@ void traffic_thread() + sockets_pending_reset.erase(sockets_pending_reset.begin()); + } + +- if ((!quitting) && (!paused)) { +- long l=0; +- unsigned long long current_calls = main_scenario->stats->GetStat(CStat::CPT_C_CurrentCall); +- unsigned long long total_calls = main_scenario->stats->GetStat(CStat::CPT_C_IncomingCallCreated) + main_scenario->stats->GetStat(CStat::CPT_C_OutgoingCallCreated); +- +- if (users >= 0) { +- calls_to_open = ((l = (users - current_calls)) > 0) ? l : 0; +- } else { +- calls_to_open = (unsigned int) +- ((l=(long)floor(((clock_tick - last_rate_change_time) * rate/rate_period_ms) +- - calls_since_last_rate_change))>0?l:0); +- } +- +- +- if( (toolMode == MODE_CLIENT) +- || (toolMode == MODE_3PCC_CONTROLLER_A) +- || (toolMode == MODE_MASTER) +- ) +- { +- int first_open_tick = clock_tick; +- while((calls_to_open--) && +- (!open_calls_allowed || current_calls < open_calls_allowed) && +- (total_calls < stop_after)) +- { +- /* Associate a user with this call, if we are in users mode. */ +- int userid = 0; +- if (users >= 0) { +- userid = freeUsers.back(); +- freeUsers.pop_back(); +- } +- +- // adding a new OUTGOING CALL +- main_scenario->stats->computeStat(CStat::E_CREATE_OUTGOING_CALL); +- call * call_ptr = call::add_call(userid, is_ipv6, use_remote_sending_addr ? &remote_sending_sockaddr : &remote_sockaddr); +- if(!call_ptr) { +- ERROR("Out of memory allocating call!"); +- } +- +- calls_since_last_rate_change++; +- +- outbound_congestion = false; +- +- if (!multisocket) { +- switch(transport) { +- case T_UDP: +- call_ptr->associate_socket(main_socket); +- main_socket->ss_count++; +- break; +- case T_TCP: +- case T_TLS: +- call_ptr->associate_socket(tcp_multiplex); +- tcp_multiplex->ss_count++; +- break; +- } +- } +- +- call_ptr -> run(); +- +- while (sockets_pending_reset.begin() != sockets_pending_reset.end()) { +- reset_connection(*(sockets_pending_reset.begin())); +- sockets_pending_reset.erase(sockets_pending_reset.begin()); +- } +- +- new_time = getmilliseconds(); +- /* Never spend more than half of our time processing new call requests. */ +- if (new_time > (first_open_tick + (timer_resolution < 2 ? 1 : (timer_resolution / 2)))) { +- break; +- } +- } +- +- if(open_calls_allowed && (current_calls >= open_calls_allowed)) { +- set_rate(rate); +- } +- } +- +- // Quit after asked number of calls is reached +- if(total_calls >= stop_after) { +- quitting = 1; +- } +- } else if (quitting) { ++ if ((main_scenario->stats->GetStat(CStat::CPT_C_IncomingCallCreated) + main_scenario->stats->GetStat(CStat::CPT_C_OutgoingCallCreated)) >= stop_after) { ++ quitting = 1; ++ } ++ if (quitting) { + if (quitting > 11) { + /* Force exit: abort all calls */ + abort_all_tasks(); +@@ -3412,36 +3356,22 @@ void traffic_thread() + if(!main_scenario->stats->GetStat(CStat::CPT_C_CurrentCall)) { + /* We can have calls that do not count towards our open-call count (e.g., dead calls). */ + abort_all_tasks(); +- print_statistics(0); +- +- // Dump the latest statistics if necessary +- if(dumpInFile) { +- main_scenario->stats->dumpData(); +- } +- if (useCountf) { +- print_count_file(countf, 0); +- } +- +- if(dumpInRtt) { +- main_scenario->stats->dumpDataRtt(); +- } +- +- /* Screen dumping in a file if asked */ +- if(screenf) { +- print_screens(); +- } + + for (int i = 0; i < pollnfds; i++) { + sipp_close_socket(sockets[i]); + } + ++ screentask::report(true); ++ stattask::report(); ++ if (screenf) { ++ print_screens(); ++ } ++ + screen_exit(EXIT_TEST_RES_UNKNOWN); + } + } + +- new_time = getmilliseconds(); +- clock_tick = new_time; +- last_time = new_time; ++ getmilliseconds(); + + /* Schedule all pending calls and process their timers */ + task_list *running_tasks; +@@ -3476,9 +3406,9 @@ void traffic_thread() + for(iter = running_tasks->begin(); iter != running_tasks->end(); iter++) { + if(last) { + last -> run(); +- while (sockets_pending_reset.begin() != sockets_pending_reset.end()) { +- reset_connection(*(sockets_pending_reset.begin())); +- sockets_pending_reset.erase(sockets_pending_reset.begin()); ++ if (sockets_pending_reset.begin() != sockets_pending_reset.end()) { ++ last = NULL; ++ break; + } + } + last = *iter; +@@ -3488,80 +3418,16 @@ void traffic_thread() + } + if(last) { + last -> run(); +- while (sockets_pending_reset.begin() != sockets_pending_reset.end()) { +- reset_connection(*(sockets_pending_reset.begin())); +- sockets_pending_reset.erase(sockets_pending_reset.begin()); +- } ++ } ++ while (sockets_pending_reset.begin() != sockets_pending_reset.end()) { ++ reset_connection(*(sockets_pending_reset.begin())); ++ sockets_pending_reset.erase(sockets_pending_reset.begin()); + } + + /* Update the clock. */ +- new_time = getmilliseconds(); +- clock_tick = new_time ; +- last_time = new_time; +- ++ getmilliseconds(); + /* Receive incoming messages */ +- pollset_process(); +- new_time = getmilliseconds(); +- clock_tick = new_time ; +- last_time = new_time; +- +- if(firstPass) +- { +- // dumping (to create file on disk) and showing +- // screen at the beginning even if the report +- // period is not reach +- firstPass = false; +- if (report_freq > 0) { +- print_statistics(0); +- } +- /* Dumping once to create the file on disk */ +- if(dumpInFile) +- { +- main_scenario->stats->dumpData(); +- } +- +- if (useCountf) { +- print_count_file(countf, 0); +- } +- +- if(dumpInRtt) +- { +- main_scenario->stats->dumpDataRtt(); +- } +- +- } +- +- if(report_freq && ((clock_tick - last_report_time) >= report_freq)) +- { +- print_statistics(0); +- display_scenario->stats->computeStat(CStat::E_RESET_PD_COUNTERS); +- last_report_time = clock_tick; +- scheduling_loops = 0; +- } +- +- // FIXME - Should we recompute time ? print stat take +- // a lot of time, so the clock_time is no more +- // the current time ! +- if((clock_tick - last_dump_time) >= report_freq_dumpLog) { +- if(dumpInFile) { +- main_scenario->stats->dumpData(); +- } +- if (useCountf) { +- print_count_file(countf, 0); +- } +- main_scenario->stats->computeStat(CStat::E_RESET_PL_COUNTERS); +- last_dump_time = clock_tick; +- if (rate_increase) { +- rate += rate_increase; +- if (rate_max && (rate > rate_max)) { +- rate = rate_max; +- if (rate_quit) { +- quitting += 10; +- } +- } +- set_rate(rate); +- } +- } ++ pollset_process(running_tasks->size() == 0); + } + } + +@@ -3670,14 +3536,19 @@ char *wrap(const char *in, int offset, int size) { + } + } else { + int m; ++ int move_back = 0; + +- out[j] = '\0'; + //printf("Before wrapping (pos = %d, k = %d, j = %d):\n%-*s%s\n", pos, k, j, offset, "", out); + + out[k] = '\n'; + pos = j - k; ++ // move_back is used to step back in the in and out buffers when a ++ // word is longer than useoffset. ++ if (i > (k + useoffset)) { ++ move_back = i - (k + useoffset); ++ i -= move_back; ++ } + k++; +- out[j] = '\0'; + out = (char *)realloc(out, alloced += useoffset); + if (!out) { + ERROR_NO("realloc"); +@@ -3688,8 +3559,7 @@ char *wrap(const char *in, int offset, int size) { + } + out[k + m] = ' '; + } +- j += useoffset; +- out[j] = '\0'; ++ j += useoffset - move_back; + //printf("After wrapping (pos = %d, k = %d):\n%-*s%s\n", pos, k, offset, "", out); + } + } +@@ -3752,6 +3622,7 @@ void help() + " 97: exit on internal command. Calls may have been processed\n" + " 99: Normal exit without calls processed\n" + " -1: Fatal error\n" ++ " -2: Fatal error binding a socket\n" + "\n" + "\n" + "Example:\n" +@@ -3842,6 +3713,12 @@ void help_stats() + " during the call but the counter is increased only by\n" + " one).\n" + "\n" ++" - FailedRegexpShouldntMatch:\n" ++" Number of failed calls because of regexp that shouldn't\n" ++" match (there might be several regexp that shouldn't match\n" ++" during the call but the counter is increased only by\n" ++" one).\n" ++"\n" + " - FailedRegexpHdrNotFound:\n" + " Number of failed calls because of regexp with hdr \n" + " option but no matching header found.\n" +@@ -3867,6 +3744,9 @@ void print_last_stats() + // and print statistics screen + currentScreenToDisplay = DISPLAY_STAT_SCREEN; + print_statistics(1); ++ if (main_scenario) { ++ stattask::report(); ++ } + } + + void freeInFiles() { +@@ -3884,9 +3764,6 @@ void freeUserVarMap() { + + void releaseGlobalAllocations() + { +- int i,j; +- message * L_ptMsg = NULL; +- + delete main_scenario; + delete ooc_scenario; + free_default_messages(); +@@ -3897,12 +3774,10 @@ void releaseGlobalAllocations() + + void stop_all_traces() + { +- if(messagef) messagef = NULL; +- if(logfile) logfile = NULL; +- // if(timeoutf) timeoutf = NULL; TODO: finish the -trace_timeout option implementation ++ message_lfi.fptr = NULL; ++ log_lfi.fptr = NULL; + if(dumpInRtt) dumpInRtt = 0; + if(dumpInFile) dumpInFile = 0; +- + } + + char* remove_pattern(char* P_buffer, char* P_extensionPattern) { +@@ -3941,6 +3816,7 @@ static struct sipp_socket *sipp_allocate_socket(bool use_ipv6, int transport, in + ret->ss_fd = fd; + ret->ss_comp_state = NULL; + ret->ss_count = 1; ++ ret->ss_changed_dest = false; + + /* Initialize all sockets with our destination address. */ + memcpy(&ret->ss_remote_sockaddr, &remote_sockaddr, sizeof(ret->ss_remote_sockaddr)); +@@ -3999,7 +3875,7 @@ int socket_fd(bool use_ipv6, int transport) { + } + + if((fd = socket(use_ipv6 ? AF_INET6 : AF_INET, socket_type, 0))== -1) { +- ERROR("Unable to get a %s socket", TRANSPORT_TO_STRING(transport)); ++ ERROR("Unable to get a %s socket (3)", TRANSPORT_TO_STRING(transport)); + } + + return fd; +@@ -4009,6 +3885,27 @@ struct sipp_socket *new_sipp_socket(bool use_ipv6, int transport) { + struct sipp_socket *ret; + int fd = socket_fd(use_ipv6, transport); + ++#if defined(__SUNOS) ++ if (fd < 256) ++ { ++ int newfd = fcntl(fd, F_DUPFD, 256); ++ if (newfd <= 0) ++ { ++ // Typically, (24)(Too many open files) is the error here ++ WARNING("Unable to get a different %s socket, errno=%d(%s)", ++ TRANSPORT_TO_STRING(transport), errno, strerror(errno)); ++ ++ // Keep the original socket fd. ++ newfd = fd; ++ } ++ else ++ { ++ close(fd); ++ } ++ fd = newfd; ++ } ++#endif ++ + ret = sipp_allocate_socket(use_ipv6, transport, fd); + if (!ret) { + close(fd); +@@ -4020,7 +3917,7 @@ struct sipp_socket *new_sipp_socket(bool use_ipv6, int transport) { + struct sipp_socket *new_sipp_call_socket(bool use_ipv6, int transport, bool *existing) { + struct sipp_socket *sock = NULL; + static int next_socket; +- if (call_sockets >= max_multi_socket - 1) { // we must take the main socket into account ++ if (pollnfds >= max_multi_socket) { // we must take the main socket into account + /* Find an existing socket that matches transport and ipv6 parameters. */ + int first = next_socket; + do +@@ -4028,8 +3925,18 @@ struct sipp_socket *new_sipp_call_socket(bool use_ipv6, int transport, bool *exi + int test_socket = next_socket; + next_socket = (next_socket + 1) % pollnfds; + +- assert(sockets[test_socket]->ss_call_socket >= 0); + if (sockets[test_socket]->ss_call_socket) { ++ /* Here we need to check that the address is the default. */ ++ if (sockets[test_socket]->ss_ipv6 != use_ipv6) { ++ continue; ++ } ++ if (sockets[test_socket]->ss_transport != transport) { ++ continue; ++ } ++ if (sockets[test_socket]->ss_changed_dest) { ++ continue; ++ } ++ + sock = sockets[test_socket]; + sock->ss_count++; + *existing = true; +@@ -4043,7 +3950,6 @@ struct sipp_socket *new_sipp_call_socket(bool use_ipv6, int transport, bool *exi + } else { + sock = new_sipp_socket(use_ipv6, transport); + sock->ss_call_socket = true; +- call_sockets++; + *existing = false; + } + return sock; +@@ -4059,6 +3965,28 @@ struct sipp_socket *sipp_accept_socket(struct sipp_socket *accept_socket) { + ERROR("Unable to accept on a %s socket: %s", TRANSPORT_TO_STRING(transport), strerror(errno)); + } + ++#if defined(__SUNOS) ++ if (fd < 256) ++ { ++ int newfd = fcntl(fd, F_DUPFD, 256); ++ if (newfd <= 0) ++ { ++ // Typically, (24)(Too many open files) is the error here ++ WARNING("Unable to get a different %s socket, errno=%d(%s)", ++ TRANSPORT_TO_STRING(transport), errno, strerror(errno)); ++ ++ // Keep the original socket fd. ++ newfd = fd; ++ } ++ else ++ { ++ close(fd); ++ } ++ fd = newfd; ++ } ++#endif ++ ++ + ret = sipp_allocate_socket(accept_socket->ss_ipv6, accept_socket->ss_transport, fd, 1); + if (!ret) { + close(fd); +@@ -4189,7 +4117,7 @@ int main(int argc, char *argv[]) + { + int argi = 0; + struct sockaddr_storage media_sockaddr; +- pthread_t pthread_id, pthread2_id, pthread3_id; ++ pthread_t pthread2_id, pthread3_id; + int L_maxSocketPresent = 0; + unsigned int generic_count = 0; + bool slave_masterSet = false; +@@ -4249,7 +4177,7 @@ int main(int argc, char *argv[]) + "Use 'sipp -h' for details", argv[argi - 1]); } + #define CHECK_PASS() if (option->pass != pass) { break; } + +- for (int pass = 0; pass <= 2; pass++) { ++ for (int pass = 0; pass <= 3; pass++) { + for(argi = 1; argi < argc; argi++) { + struct sipp_option *option = find_option(argv[argi]); + if (!option) { +@@ -4351,15 +4279,25 @@ int main(int argc, char *argv[]) + *((int *)option->data) = argi; + break; + case SIPP_OPTION_INPUT_FILE: +- REQUIRE_ARG(); +- CHECK_PASS(); +- inFiles[argv[argi]] = new FileContents(argv[argi]); +- /* By default, the first file is used for IP address input. */ +- if (!ip_file) { +- ip_file = argv[argi]; +- } +- if (!default_file) { +- default_file = argv[argi]; ++ { ++ REQUIRE_ARG(); ++ CHECK_PASS(); ++ FileContents *data = new FileContents(argv[argi]); ++ char *name = argv[argi]; ++ if (strrchr(name, '/')) { ++ name = strrchr(name, '/') + 1; ++ } else if (strrchr(name, '\\')) { ++ name = strrchr(name, '\\') + 1; ++ } ++ assert(name); ++ inFiles[name] = data; ++ /* By default, the first file is used for IP address input. */ ++ if (!ip_file) { ++ ip_file = name; ++ } ++ if (!default_file) { ++ default_file = name; ++ } + } + break; + case SIPP_OPTION_INDEX_FILE: +@@ -4369,7 +4307,6 @@ int main(int argc, char *argv[]) + { + char *fileName = argv[argi - 1]; + char *endptr; +- char tmp[SIPP_MAX_MSG_SIZE]; + int field; + + if (inFiles.find(fileName) == inFiles.end()) { +@@ -4381,13 +4318,7 @@ int main(int argc, char *argv[]) + ERROR("Invalid field specification for -infindex: %s", argv[argi]); + } + +- infIndex[fileName] = new str_int_map; +- +- for (int line = 0; line < inFiles[fileName]->numLines(); line++) { +- inFiles[fileName]->getField(line, field, tmp, SIPP_MAX_MSG_SIZE); +- str_int_map *indmap = infIndex[fileName]; +- indmap->insert(pair(str_int_map::key_type(tmp), line)); +- } ++ inFiles[fileName]->index(field); + } + break; + case SIPP_OPTION_SETFLAG: +@@ -4482,6 +4413,9 @@ int main(int argc, char *argv[]) + case SIPP_OPTION_LIMIT: + REQUIRE_ARG(); + CHECK_PASS(); ++ if (users >= 0) { ++ ERROR("Can not set open call limit (-l) when -users is specified."); ++ } + open_calls_allowed = get_long(argv[argi], argv[argi - 1]); + open_calls_user_setting = 1; + break; +@@ -4502,6 +4436,20 @@ int main(int argc, char *argv[]) + generic[generic_count++] = &argv[argi - 1]; + generic[generic_count] = NULL; + break; ++ case SIPP_OPTION_VAR: ++ REQUIRE_ARG(); ++ REQUIRE_ARG(); ++ CHECK_PASS(); ++ ++ { ++ int varId = globalVariables->find(argv[argi - 1], false); ++ if (varId == -1) { ++ globalVariables->dump(); ++ ERROR("Can not set the global variable %s, because it does not exist.", argv[argi - 1]); ++ } ++ globalVariables->getVar(varId)->setString(strdup(argv[argi])); ++ } ++ break; + case SIPP_OPTION_3PCC: + if(slave_masterSet){ + ERROR("-3PCC option is not compatible with -master and -slave options\n"); +@@ -4519,10 +4467,14 @@ int main(int argc, char *argv[]) + REQUIRE_ARG(); + CHECK_PASS(); + if (!strcmp(argv[argi - 1], "-sf")) { +- main_scenario = new scenario(argv[argi], 0); + scenario_file = new char [strlen(argv[argi])+1] ; + sprintf(scenario_file,"%s", argv[argi]); +- main_scenario->stats->setFileName(argv[argi], (char*)".csv"); ++ scenario_file = remove_pattern (scenario_file, (char*)".xml"); ++ if (useLogf == 1) { ++ rotate_logfile(); ++ } ++ main_scenario = new scenario(argv[argi], 0); ++ main_scenario->stats->setFileName(scenario_file, (char*)".csv"); + } else if (!strcmp(argv[argi - 1], "-sn")) { + int i = find_scenario(argv[argi]); + +@@ -4562,8 +4514,8 @@ int main(int argc, char *argv[]) + parse_slave_cfg(); + break; + case SIPP_OPTION_3PCC_EXTENDED: +- REQUIRE_ARG(); +- CHECK_PASS(); ++ REQUIRE_ARG(); ++ CHECK_PASS(); + if(slave_masterSet){ + ERROR("-slave and -master options are not compatible\n"); + } +@@ -4621,6 +4573,17 @@ int main(int argc, char *argv[]) + freeaddrinfo(local_addr); + break; + } ++ case SIPP_OPTION_RTCHECK: ++ REQUIRE_ARG(); ++ CHECK_PASS(); ++ if (!strcmp(argv[argi], "full")) { ++ *((int *)option->data) = RTCHECK_FULL; ++ } else if (!strcmp(argv[argi], "loose")) { ++ *((int *)option->data) = RTCHECK_LOOSE; ++ } else { ++ ERROR("Unknown retransmission detection method: %s\n", argv[argi]); ++ } ++ break; + case SIPP_OPTION_TDMMAP: { + REQUIRE_ARG(); + CHECK_PASS(); +@@ -4691,7 +4654,43 @@ int main(int argc, char *argv[]) + } + token = NULL; + } +- fprintf(stderr, "Defaults: %lu\n", *ptr); ++ break; ++ } ++ case SIPP_OPTION_LFNAME: ++ REQUIRE_ARG(); ++ CHECK_PASS(); ++ ((struct logfile_info*)option->data)->fixedname = true; ++ strcpy(((struct logfile_info*)option->data)->file_name, argv[argi]); ++ break; ++ case SIPP_OPTION_LFOVERWRITE: ++ REQUIRE_ARG(); ++ CHECK_PASS(); ++ ((struct logfile_info*)option->data)->fixedname = true; ++ ((struct logfile_info*)option->data)->overwrite = get_bool(argv[argi], argv[argi-1]); ++ break; ++ case SIPP_OPTION_PLUGIN: { ++ void *handle; ++ char *error; ++ int (*init)(); ++ int ret; ++ ++ REQUIRE_ARG(); ++ CHECK_PASS(); ++ ++ handle = dlopen(argv[argi], RTLD_NOW); ++ if (!handle) { ++ ERROR("Could not open plugin %s: %s", argv[argi], dlerror()); ++ } ++ ++ init = (int (*)())dlsym(handle, "init"); ++ if((error = (char *) dlerror())) { ++ ERROR("Could not locate init function in %s: %s", argv[argi], dlerror()); ++ } ++ ++ ret = init(); ++ if (ret != 0) { ++ ERROR("Plugin %s initialization failed.", argv[argi]); ++ } + } + break; + default: +@@ -4723,8 +4722,6 @@ int main(int argc, char *argv[]) + if (scenario_file == NULL) { + scenario_file = new char [ 5 ] ; + sprintf(scenario_file, "%s", "sipp"); +- } else { +- scenario_file = remove_pattern (scenario_file, (char*)".xml"); + } + + screen_init(print_last_stats); +@@ -4743,6 +4740,10 @@ int main(int argc, char *argv[]) + if (useShortMessagef == 1) { + rotate_shortmessagef(); + } ++ ++ if (useCallDebugf) { ++ rotate_calldebugf(); ++ } + + if (useScreenf == 1) { + char L_file_name [MAX_PATH]; +@@ -4774,10 +4775,6 @@ int main(int argc, char *argv[]) + print_count_file(countf, 1); + } + +- if (useLogf == 1) { +- rotate_logfile(); +- } +- + if (dumpInRtt == 1) { + main_scenario->stats->initRtt((char*)scenario_file, (char*)".csv", + report_freq_dumpRtt); +@@ -4797,7 +4794,7 @@ int main(int argc, char *argv[]) + + if (rlimit.rlim_max > + #ifndef __CYGWIN +- ((L_maxSocketPresent) ? max_multi_socket : FD_SETSIZE) ++ ((L_maxSocketPresent) ? (unsigned int)max_multi_socket : FD_SETSIZE) + #else + FD_SETSIZE + #endif +@@ -4808,13 +4805,13 @@ int main(int argc, char *argv[]) + + rlimit.rlim_max = + #ifndef __CYGWIN +- (L_maxSocketPresent) ? max_multi_socket+min_socket : FD_SETSIZE ; ++ (L_maxSocketPresent) ? (unsigned int)max_multi_socket+min_socket : FD_SETSIZE ; + #else + + FD_SETSIZE; + #endif + } +- ++ + rlimit.rlim_cur = rlimit.rlim_max; + if (setrlimit (RLIMIT_NOFILE, &rlimit) < 0) { + ERROR("Unable to increase the open file limit to FD_SETSIZE = %d", +@@ -4843,6 +4840,17 @@ int main(int argc, char *argv[]) + if(argiFileName) { + main_scenario->stats->setFileName(argv[argiFileName]); + } ++ ++ // setup option form cmd line ++ call::maxDynamicId = maxDynamicId; ++ call::startDynamicId = startDynamicId; ++ call::dynamicId = startDynamicId; ++ call::stepDynamicId = stepDynamicId; ++ ++ ++ /* Now Initialize the scenarios. */ ++ main_scenario->runInit(); ++ ooc_scenario->runInit(); + + /* In which mode the tool is launched ? */ + main_scenario->computeSippMode(); +@@ -4876,17 +4884,27 @@ int main(int argc, char *argv[]) + exit(EXIT_OTHER); + } + } +- ++ ++ sipp_usleep(sleeptime * 1000); ++ ++ /* Create the statistics reporting task. */ ++ stattask::initialize(); ++ /* Create the screen update task. */ ++ screentask::initialize(); ++ /* Create a watchdog task. */ ++ if (watchdog_interval) { ++ new watchdog(watchdog_interval, watchdog_reset, watchdog_major_threshold, watchdog_major_maxtriggers, watchdog_minor_threshold, watchdog_minor_maxtriggers); ++ } ++ + /* Setting the rate and its dependant params (open_calls_allowed) */ +- set_rate(rate); +- +- if (toolMode == MODE_SERVER) { +- reset_number = 0; ++ /* If we are a client, then create the task to open new calls. */ ++ if (creationMode == MODE_CLIENT) { ++ opentask::initialize(); ++ opentask::set_rate(rate); + } +- ++ + open_connections(); +- +- ++ + /* Defaults for media sockets */ + if (media_ip[0] == '\0') { + strcpy(media_ip, local_ip); +@@ -4919,7 +4937,10 @@ int main(int argc, char *argv[]) + ERROR("Unknown RTP address '%s'.\n" + "Use 'sipp -h' for details", media_ip); + } +- ++ ++ memset(&media_sockaddr,0,sizeof(struct sockaddr_storage)); ++ media_sockaddr.ss_family = local_addr->ai_addr->sa_family; ++ + memcpy(&media_sockaddr, + local_addr->ai_addr, + SOCK_ADDR_SIZE( +@@ -5024,7 +5045,7 @@ int main(int argc, char *argv[]) + == -1) { + ERROR_NO("Unable to create second RTP echo thread"); + } +- } ++ } + + traffic_thread(); + +@@ -5097,7 +5118,7 @@ int open_connections() { + local_port = 0; + + if(!strlen(remote_host)) { +- if((toolMode != MODE_SERVER)) { ++ if((sendMode != MODE_SERVER)) { + ERROR("Missing remote host parameter. This scenario requires it"); + } + } else { +@@ -5185,12 +5206,10 @@ int open_connections() { + get_inet_address( + _RCAST(struct sockaddr_storage *, local_addr->ai_addr))); + } else { +- if (!(local_sockaddr.ss_family == AF_INET6)) { +- memcpy(&local_sockaddr, +- local_addr->ai_addr, +- SOCK_ADDR_SIZE( +- _RCAST(struct sockaddr_storage *,local_addr->ai_addr))); +- } ++ memcpy(&local_sockaddr, ++ local_addr->ai_addr, ++ SOCK_ADDR_SIZE( ++ _RCAST(struct sockaddr_storage *,local_addr->ai_addr))); + } + freeaddrinfo(local_addr); + +@@ -5325,7 +5344,7 @@ int open_connections() { + + // Create additional server sockets when running in socket per + // IP address mode. +- if (peripsocket && toolMode == MODE_SERVER) { ++ if (peripsocket && sendMode == MODE_SERVER) { + struct sockaddr_storage server_sockaddr; + struct addrinfo * local_addr; + struct addrinfo hints; +@@ -5379,7 +5398,7 @@ int open_connections() { + } + + if((!multisocket) && (transport == T_TCP || transport == T_TLS) && +- (toolMode != MODE_SERVER)) { ++ (sendMode != MODE_SERVER)) { + if((tcp_multiplex = new_sipp_socket(local_ip_is_ipv6, transport)) == NULL) { + ERROR_NO("Unable to get a TCP socket"); + } +@@ -5419,26 +5438,26 @@ int open_connections() { + + /* Trying to connect to Twin Sipp in 3PCC mode */ + if(twinSippMode) { +- if(toolMode == MODE_3PCC_CONTROLLER_A || toolMode == MODE_3PCC_A_PASSIVE) { ++ if(thirdPartyMode == MODE_3PCC_CONTROLLER_A || thirdPartyMode == MODE_3PCC_A_PASSIVE) { + connect_to_peer(twinSippHost, twinSippPort, &twinSipp_sockaddr, twinSippIp, &twinSippSocket); +- }else if(toolMode == MODE_3PCC_CONTROLLER_B){ ++ }else if(thirdPartyMode == MODE_3PCC_CONTROLLER_B){ + connect_local_twin_socket(twinSippHost); + }else{ +- ERROR("TwinSipp Mode enabled but toolMode is different " ++ ERROR("TwinSipp Mode enabled but thirdPartyMode is different " + "from 3PCC_CONTROLLER_B and 3PCC_CONTROLLER_A\n"); + } + }else if (extendedTwinSippMode){ +- if (toolMode == MODE_MASTER || toolMode == MODE_MASTER_PASSIVE) { ++ if (thirdPartyMode == MODE_MASTER || thirdPartyMode == MODE_MASTER_PASSIVE) { + strcpy(twinSippHost,get_peer_addr(master_name)); + get_host_and_port(twinSippHost, twinSippHost, &twinSippPort); + connect_local_twin_socket(twinSippHost); + connect_to_all_peers(); +- }else if(toolMode == MODE_SLAVE) { ++ }else if(thirdPartyMode == MODE_SLAVE) { + strcpy(twinSippHost,get_peer_addr(slave_number)); + get_host_and_port(twinSippHost, twinSippHost, &twinSippPort); + connect_local_twin_socket(twinSippHost); + }else{ +- ERROR("extendedTwinSipp Mode enabled but toolMode is different " ++ ERROR("extendedTwinSipp Mode enabled but thirdPartyMode is different " + "from MASTER and SLAVE\n"); + } + } +@@ -5662,180 +5681,154 @@ void free_peer_addr_map() { + } + } + +-#ifdef __cplusplus +-extern "C" { +-#endif +-int TRACE_MSG(char *fmt, ...) { +- int ret = 0; +- static unsigned long long count = 0; +- if(messagef) { +- va_list ap; +- va_start(ap, fmt); +- ret = vfprintf(messagef, fmt, ap); +- va_end(ap); +- fflush(messagef); ++void rotatef(struct logfile_info *lfi) { ++ char L_rotate_file_name [MAX_PATH]; + +- count += ret; ++ if (!lfi->fixedname) { ++ sprintf (lfi->file_name, "%s_%d_%s.log", scenario_file, getpid(), lfi->name); ++ } + +- if (max_log_size && count > max_log_size) { +- fclose(messagef); +- messagef = NULL; ++ if (ringbuffer_files > 0) { ++ if (!lfi->ftimes) { ++ lfi->ftimes = (struct logfile_id *)calloc(ringbuffer_files, sizeof(struct logfile_id)); + } +- +- if (ringbuffer_size && count > ringbuffer_size) { +- rotate_messagef(); +- count = 0; ++ /* We need to rotate away an existing file. */ ++ if (lfi->nfiles == ringbuffer_files) { ++ if ((lfi->ftimes)[0].n) { ++ sprintf(L_rotate_file_name, "%s_%d_%s_%lu.%d.log", scenario_file, getpid(), lfi->name, (lfi->ftimes)[0].start, (lfi->ftimes)[0].n); ++ } else { ++ sprintf(L_rotate_file_name, "%s_%d_%s_%lu.log", scenario_file, getpid(), lfi->name, (lfi->ftimes)[0].start); ++ } ++ unlink(L_rotate_file_name); ++ lfi->nfiles--; ++ memmove(lfi->ftimes, &((lfi->ftimes)[1]), sizeof(struct logfile_id) * (lfi->nfiles)); ++ } ++ if (lfi->starttime) { ++ (lfi->ftimes)[lfi->nfiles].start = lfi->starttime; ++ (lfi->ftimes)[lfi->nfiles].n = 0; ++ /* If we have the same time, then we need to append an identifier. */ ++ if (lfi->nfiles && ((lfi->ftimes)[lfi->nfiles].start == (lfi->ftimes)[lfi->nfiles - 1].start)) { ++ (lfi->ftimes)[lfi->nfiles].n = (lfi->ftimes)[lfi->nfiles - 1].n + 1; ++ } ++ if ((lfi->ftimes)[lfi->nfiles].n) { ++ sprintf(L_rotate_file_name, "%s_%d_%s_%lu.%d.log", scenario_file, getpid(), lfi->name, (lfi->ftimes)[lfi->nfiles].start, (lfi->ftimes)[lfi->nfiles].n); ++ } else { ++ sprintf(L_rotate_file_name, "%s_%d_%s_%lu.log", scenario_file, getpid(), lfi->name, (lfi->ftimes)[lfi->nfiles].start); ++ } ++ lfi->nfiles++; ++ fflush(lfi->fptr); ++ fclose(lfi->fptr); ++ lfi->fptr = NULL; ++ rename(lfi->file_name, L_rotate_file_name); + } + } +- return ret; ++ ++ time(&lfi->starttime); ++ if (lfi->overwrite) { ++ lfi->fptr = fopen(lfi->file_name, "w"); ++ } else { ++ lfi->fptr = fopen(lfi->file_name, "a"); ++ lfi->overwrite = true; ++ } ++ if(lfi->check && !lfi->fptr) { ++ /* We can not use the error functions from this function, as we may be rotating the error log itself! */ ++ ERROR("Unable to create '%s'", lfi->file_name); ++ } + } + +-int TRACE_SHORTMSG(char *fmt, ...) { +- int ret = 0; +- static unsigned long long count = 0; +- if(shortmessagef) { +- va_list ap; +- va_start(ap, fmt); +- ret = vfprintf(shortmessagef, fmt, ap); +- va_end(ap); +- fflush(shortmessagef); ++void rotate_calldebugf() { ++ rotatef(&calldebug_lfi); ++} + +- count += ret; ++void rotate_messagef() { ++ rotatef(&message_lfi); ++} + +- if (max_log_size && count > max_log_size) { +- fclose(shortmessagef); +- shortmessagef = NULL; +- } + +- if (ringbuffer_size && count > ringbuffer_size) { +- rotate_shortmessagef(); +- count = 0; +- } +- } +- return ret; ++void rotate_shortmessagef() { ++ rotatef(&shortmessage_lfi); + } + +-int LOG_MSG(char *fmt, ...) { ++ ++void rotate_logfile() { ++ rotatef(&log_lfi); ++} ++ ++void rotate_errorf() { ++ rotatef(&error_lfi); ++ strcpy(screen_logfile, error_lfi.file_name); ++} ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++int _trace (struct logfile_info *lfi, char *fmt, va_list ap) { + int ret = 0; +- static unsigned long long count = 0; +- if(logfile) { +- va_list ap; +- va_start(ap, fmt); +- ret = vfprintf(logfile, fmt, ap); +- va_end(ap); +- fflush(logfile); ++ if(lfi->fptr) { ++ ret = vfprintf(lfi->fptr, fmt, ap); ++ fflush(lfi->fptr); + +- count += ret; ++ lfi->count += ret; + +- if (max_log_size && count > max_log_size) { +- fclose(logfile); +- logfile = NULL; ++ if (max_log_size && lfi->count > max_log_size) { ++ fclose(lfi->fptr); ++ lfi->fptr = NULL; + } + +- if (ringbuffer_size && count > ringbuffer_size) { +- rotate_messagef(); +- count = 0; ++ if (ringbuffer_size && lfi->count > ringbuffer_size) { ++ rotatef(lfi); ++ lfi->count = 0; + } + } + return ret; + } + +-// TODO: finish the -trace_timeout option implementation +-/* int TRACE_TIMEOUT(char *fmt, ...) */ + +-#ifdef __cplusplus +-} +-#endif ++int TRACE_MSG(char *fmt, ...) { ++ int ret; ++ va_list ap; + +-struct logfile_id { +- time_t start; +- int n; +-}; ++ va_start(ap, fmt); ++ ret = _trace(&message_lfi, fmt, ap); ++ va_end(ap); + +-/* We can not use the error functions from this file, as we may be rotating the error log itself! */ +-void rotatef(char *name, FILE **fptr, time_t *starttime, int *nfiles, struct logfile_id **ftimes, bool check, bool *overwrite) { +- char L_file_name [MAX_PATH]; +- char L_rotate_file_name [MAX_PATH]; ++ return ret; ++} + +- sprintf (L_file_name, "%s_%d_%s.log", scenario_file, getpid(), name); ++int TRACE_SHORTMSG(char *fmt, ...) { ++ int ret; ++ va_list ap; + +- if (ringbuffer_files > 0) { +- if (!*ftimes) { +- *ftimes = (struct logfile_id *)calloc(ringbuffer_files, sizeof(struct logfile_id)); +- } +- /* We need to rotate away an existing file. */ +- if (*nfiles == ringbuffer_files) { +- if ((*ftimes)[0].n) { +- sprintf(L_rotate_file_name, "%s_%d_%s_%d.%d.log", scenario_file, getpid(), name, (*ftimes)[0].start, (*ftimes)[0].n); +- } else { +- sprintf(L_rotate_file_name, "%s_%d_%s_%d.log", scenario_file, getpid(), name, (*ftimes)[0].start); +- } +- unlink(L_rotate_file_name); +- (*nfiles)--; +- memmove(*ftimes, &((*ftimes)[1]), sizeof(struct logfile_id) * (*nfiles)); +- } +- if (*starttime) { +- (*ftimes)[*nfiles].start = *starttime; +- (*ftimes)[*nfiles].n = 0; +- /* If we have the same time, then we need to append an identifier. */ +- if (*nfiles && ((*ftimes)[*nfiles].start == (*ftimes)[*nfiles - 1].start)) { +- (*ftimes)[*nfiles].n = (*ftimes)[*nfiles - 1].n + 1; +- } +- if ((*ftimes)[*nfiles].n) { +- sprintf(L_rotate_file_name, "%s_%d_%s_%d.%d.log", scenario_file, getpid(), name, (*ftimes)[*nfiles].start, (*ftimes)[*nfiles].n); +- } else { +- sprintf(L_rotate_file_name, "%s_%d_%s_%d.log", scenario_file, getpid(), name, (*ftimes)[*nfiles].start); +- } +- (*nfiles)++; +- fflush(*fptr); +- fclose(*fptr); +- *fptr = NULL; +- rename(L_file_name, L_rotate_file_name); +- } +- } ++ va_start(ap, fmt); ++ ret = _trace(&shortmessage_lfi, fmt, ap); ++ va_end(ap); + +- time(starttime); +- if (*overwrite) { +- *fptr = fopen(L_file_name, "w"); +- } else { +- *fptr = fopen(L_file_name, "a"); +- *overwrite = true; +- } +- if(check && !*fptr) { +- ERROR("Unable to create '%s'", L_file_name); +- } ++ return ret; + } + +-int messagef_nfiles = 0; +-struct logfile_id *messagef_times = NULL; +- +-void rotate_messagef() { +- static time_t starttime = 0; +- rotatef("messages", &messagef, &starttime, &messagef_nfiles, &messagef_times, true, &messagef_overwrite); +-} ++int LOG_MSG(char *fmt, ...) { ++ int ret; ++ va_list ap; + +-int shortmessagef_nfiles = 0; +-struct logfile_id *shortmessagef_times = NULL; ++ va_start(ap, fmt); ++ ret = _trace(&log_lfi, fmt, ap); ++ va_end(ap); + +-void rotate_shortmessagef() { +- static time_t starttime = 0; +- rotatef("shortmessages", &shortmessagef, &starttime, &shortmessagef_nfiles, &shortmessagef_times, true, &shortmessagef_overwrite); ++ return ret; + } + +-int logfile_nfiles = 0; +-struct logfile_id *logfile_times = NULL; ++int TRACE_CALLDEBUG(char *fmt, ...) { ++ int ret; ++ va_list ap; + +-void rotate_logfile() { +- static time_t starttime = 0; +- rotatef("logs", &logfile, &starttime, &logfile_nfiles, &logfile_times, true, &logfile_overwrite); +-} ++ va_start(ap, fmt); ++ ret = _trace(&calldebug_lfi, fmt, ap); ++ va_end(ap); + +-int errorf_nfiles = 0; +-struct logfile_id *errorf_times = NULL; ++ return ret; ++} + +-void rotate_errorf() { +- static time_t starttime = 0; +- rotatef("errors", &screen_errorf, &starttime, &errorf_nfiles, &errorf_times, false, &errorf_overwrite); +- /* If rotatef is changed, this must be changed as well. */ +- sprintf (screen_logfile, "%s_%d_errors.log", scenario_file, getpid()); ++#ifdef __cplusplus + } ++#endif ++ +diff --git a/sipp.hpp b/sipp.hpp +index 7466c85..ec1e8ef 100644 +--- a/sipp.hpp ++++ b/sipp.hpp +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -67,10 +68,13 @@ + #include "socketowner.hpp" + #include "call.hpp" + #include "comp.h" ++#include "variables.hpp" + #include "stat.hpp" + #include "actions.hpp" +-#include "variables.hpp" + #include "infile.hpp" ++#include "opentask.hpp" ++#include "reporttask.hpp" ++#include "watchdog.hpp" + /* Open SSL stuff */ + #ifdef _USE_OPENSSL + #include "sslcommon.h" +@@ -184,23 +188,25 @@ extern int rate_max _DEFVAL(0); + extern bool rate_quit _DEFVAL(true); + extern int users _DEFVAL(-1); + extern int rate_period_ms _DEFVAL(DEFAULT_RATE_PERIOD_MS); ++extern int sleeptime _DEFVAL(0); + extern unsigned long defl_recv_timeout _DEFVAL(0); + extern unsigned long defl_send_timeout _DEFVAL(0); + extern unsigned long global_timeout _DEFVAL(0); + extern int transport _DEFVAL(DEFAULT_TRANSPORT); + extern bool retrans_enabled _DEFVAL(1); ++extern int rtcheck _DEFVAL(RTCHECK_FULL); + extern int max_udp_retrans _DEFVAL(UDP_MAX_RETRANS); + extern int max_invite_retrans _DEFVAL(UDP_MAX_RETRANS_INVITE_TRANSACTION); + extern int max_non_invite_retrans _DEFVAL(UDP_MAX_RETRANS_NON_INVITE_TRANSACTION); + extern unsigned long default_behaviors _DEFVAL(DEFAULT_BEHAVIOR_ALL); + extern unsigned long deadcall_wait _DEFVAL(DEFAULT_DEADCALL_WAIT); + extern bool pause_msg_ign _DEFVAL(0); +-extern int auto_answer _DEFVAL(0); ++extern bool auto_answer _DEFVAL(false); + extern int multisocket _DEFVAL(0); + extern int compression _DEFVAL(0); + extern int peripsocket _DEFVAL(0); + extern int peripfield _DEFVAL(0); +-extern int bind_local _DEFVAL(0); ++extern bool bind_local _DEFVAL(false); + extern void * monosocket_comp_state _DEFVAL(0); + extern char * service _DEFVAL(DEFAULT_SERVICE); + extern char * auth_password _DEFVAL(DEFAULT_AUTH_PASSWORD); +@@ -211,11 +217,12 @@ extern bool periodic_rtd _DEFVAL(false); + extern char * stat_delimiter _DEFVAL(";"); + + extern bool timeout_exit _DEFVAL(false); ++extern bool timeout_error _DEFVAL(false); + + extern unsigned long report_freq_dumpRtt _DEFVAL + (DEFAULT_FREQ_DUMP_RTT); + +-extern unsigned int max_multi_socket _DEFVAL ++extern int max_multi_socket _DEFVAL + (DEFAULT_MAX_MULTI_SOCKET); + extern bool skip_rlimit _DEFVAL(false); + +@@ -245,11 +252,11 @@ extern char remote_ip[40]; + extern char remote_ip_escaped[42]; + extern int remote_port _DEFVAL(DEFAULT_PORT); + extern unsigned int pid _DEFVAL(0); +-extern int print_all_responses _DEFVAL(0); ++extern bool print_all_responses _DEFVAL(false); + extern unsigned long stop_after _DEFVAL(0xffffffff); + extern int quitting _DEFVAL(0); + extern int interrupt _DEFVAL(0); +-extern int paused _DEFVAL(0); ++extern bool paused _DEFVAL(false); + extern int lose_packets _DEFVAL(0); + extern double global_lost _DEFVAL(0.0); + extern char remote_host[255]; +@@ -297,7 +304,6 @@ extern char *tls_crl_name _DEFVAL(DEFAULT_TLS_CRL) ; + typedef std::map file_map; + extern file_map inFiles; + typedef std::map file_index; +-extern file_index infIndex; + extern char *ip_file _DEFVAL(NULL); + extern char *default_file _DEFVAL(NULL); + +@@ -353,7 +359,6 @@ extern int last_running_calls _DEFVAL(0); + extern int last_woken_calls _DEFVAL(0); + extern int last_paused_calls _DEFVAL(0); + extern unsigned int open_calls_allowed _DEFVAL(0); +-extern unsigned long last_rate_change_time _DEFVAL(0); + extern unsigned long last_report_time _DEFVAL(0); + extern unsigned long last_dump_time _DEFVAL(0); + +@@ -363,6 +368,21 @@ extern unsigned long clock_tick _DEFVAL(0); + extern unsigned long scheduling_loops _DEFVAL(0); + extern unsigned long last_timer_cycle _DEFVAL(0); + ++extern unsigned long watchdog_interval _DEFVAL(400); ++extern unsigned long watchdog_minor_threshold _DEFVAL(500); ++extern unsigned long watchdog_minor_maxtriggers _DEFVAL(120); ++extern unsigned long watchdog_major_threshold _DEFVAL(3000); ++extern unsigned long watchdog_major_maxtriggers _DEFVAL(10); ++extern unsigned long watchdog_reset _DEFVAL(600000); ++ ++ ++/********************* dynamic Id ************************* */ ++extern int maxDynamicId _DEFVAL(12000); // max value for dynamicId; this value is reached ++extern int startDynamicId _DEFVAL(10000); // offset for first dynamicId FIXME:in CmdLine ++extern int stepDynamicId _DEFVAL(4); // step of increment for dynamicId ++ ++ ++ + #define GET_TIME(clock) \ + { \ + struct timezone tzp; \ +@@ -371,8 +391,9 @@ extern unsigned long last_timer_cycle _DEFVAL(0); + + /*********************** Global Sockets **********************/ + +-extern struct sipp_socket *main_socket _DEFVAL(0); +-extern struct sipp_socket *tcp_multiplex _DEFVAL(0); ++extern struct sipp_socket *main_socket _DEFVAL(NULL); ++extern struct sipp_socket *main_remote_socket _DEFVAL(NULL); ++extern struct sipp_socket *tcp_multiplex _DEFVAL(NULL); + extern int media_socket _DEFVAL(0); + extern int media_socket_video _DEFVAL(0); + +@@ -383,7 +404,7 @@ extern char hostname[80]; + extern bool is_ipv6 _DEFVAL(false); + + extern int reset_number _DEFVAL(0); +-extern int reset_close _DEFVAL(1); ++extern bool reset_close _DEFVAL(true); + extern int reset_sleep _DEFVAL(1000); + extern bool sendbuffer_warn _DEFVAL(false); + /* A list of sockets pending reset. */ +@@ -427,20 +448,13 @@ enum E_Alter_YesNo + /************************** Trace Files ***********************/ + + extern FILE * screenf _DEFVAL(0); +-extern FILE * logfile _DEFVAL(0); +-extern FILE * messagef _DEFVAL(0); +-extern FILE * shortmessagef _DEFVAL(0); + extern FILE * countf _DEFVAL(0); + // extern FILE * timeoutf _DEFVAL(0); + extern bool useMessagef _DEFVAL(0); ++extern bool useCallDebugf _DEFVAL(0); + extern bool useShortMessagef _DEFVAL(0); + extern bool useScreenf _DEFVAL(0); + extern bool useLogf _DEFVAL(0); +-// should we overwrite the existing files? +-extern bool messagef_overwrite _DEFVAL(true); +-extern bool shortmessagef_overwrite _DEFVAL(true); +-extern bool errorf_overwrite _DEFVAL(true); +-extern bool logfile_overwrite _DEFVAL(true); + //extern bool useTimeoutf _DEFVAL(0); + extern bool dumpInFile _DEFVAL(0); + extern bool dumpInRtt _DEFVAL(0); +@@ -454,13 +468,46 @@ extern int ringbuffer_files _DEFVAL(0); + + extern char screen_last_error[32768]; + extern char screen_logfile[MAX_PATH] _DEFVAL(""); +-extern FILE * screen_errorf _DEFVAL(NULL); + +-void rotate_messagef(); +-void rotate_shortmessagef(); +-void rotate_logfile(); ++/* Log Rotation Functions. */ ++struct logfile_id { ++ time_t start; ++ int n; ++}; ++ ++struct logfile_info { ++ char *name; ++ bool check; ++ FILE *fptr; ++ int nfiles; ++ struct logfile_id *ftimes; ++ char file_name[MAX_PATH]; ++ bool overwrite; ++ bool fixedname; ++ time_t starttime; ++ unsigned int count; ++}; ++ ++#ifdef GLOBALS_FULL_DEFINITION ++#define LOGFILE(name, s, check) \ ++ struct logfile_info name = { s, check, NULL, 0, NULL, "", true, false, 0, 0}; ++#else ++#define LOGFILE(name, s, check) \ ++ extern struct logfile_info name; ++#endif ++LOGFILE(calldebug_lfi, "calldebug", true); ++LOGFILE(message_lfi, "messages", true); ++LOGFILE(shortmessage_lfi, "shortmessages", true); ++LOGFILE(log_lfi, "logs", true); ++LOGFILE(error_lfi, "errors", false); ++ + void rotate_errorf(); + ++/* Screen/Statistics Printing Functions. */ ++void print_statistics(int last); ++void print_count_file(FILE *f, int header); ++ ++ + /********************* Mini-Parser Routines *******************/ + + int get_method(char *msg); +@@ -501,6 +548,7 @@ struct sipp_socket { + bool ss_ipv6; + bool ss_control; /* Is this a control socket? */ + bool ss_call_socket; /* Is this a call socket? */ ++ bool ss_changed_dest; /* Has the destination changed from default. */ + + int ss_fd; /* The underlying file descriptor for this socket. */ + void *ss_comp_state; /* The compression state. */ +@@ -583,6 +631,7 @@ void free_peer_addr_map(); + extern "C" { + #endif + int TRACE_MSG(char *fmt, ...); ++int TRACE_CALLDEBUG(char *fmt, ...); + int TRACE_SHORTMSG(char *fmt, ...); + int LOG_MSG(char *fmt, ...); + #ifdef __cplusplus +diff --git a/socketowner.cpp b/socketowner.cpp +index 0976880..3b928b5 100644 +--- a/socketowner.cpp ++++ b/socketowner.cpp +@@ -72,7 +72,9 @@ socketowner::socketowner() { + } + + socketowner::~socketowner() { +- sipp_close_socket(dissociate_socket()); ++ if (this->call_socket) { ++ sipp_close_socket(dissociate_socket()); ++ } + } + + void socketowner::add_owner_to_socket(struct sipp_socket *socket) { +diff --git a/stat.cpp b/stat.cpp +index 2eef980..0540934 100644 +--- a/stat.cpp ++++ b/stat.cpp +@@ -82,12 +82,33 @@ + #define DISPLAY_INFO(T1)\ + fprintf(f," %-77.77s \r\n", T1) + #define DISPLAY_REPART(T1, T2, V1)\ +- fprintf(f," %8d ms <= n < %8d ms : %10d %-29.29s \r\n", T1, T2, V1, "") ++ fprintf(f," %8d ms <= n < %8d ms : %10lu %-29.29s \r\n", T1, T2, V1, "") + #define DISPLAY_LAST_REPART(T1, V1)\ +- fprintf(f," %14.14s n >= %8d ms : %10d %-29.29s \r\n", "", T1, V1, "") ++ fprintf(f," %14.14s n >= %8d ms : %10lu %-29.29s \r\n", "", T1, V1, "") + + #define RESET_COUNTERS(PT)\ +- memset (PT, 0, CStat::E_NB_G_COUNTER * sizeof(unsigned long long)) ++ memset (PT, 0, CStat::E_NB_COUNTER * sizeof(unsigned long long)) ++ ++#define RESET_C_COUNTERS \ ++{ \ ++ int i; \ ++ for (i=CStat::CPT_G_C_OutOfCallMsgs; \ ++ i<=CStat::CPT_G_C_AutoAnswered; \ ++ i++) \ ++ M_G_counters[i - E_NB_COUNTER - 1] = (unsigned long) 0; \ ++ for (i=CStat::CPT_C_IncomingCallCreated; \ ++ i<=CStat::CPT_C_Retransmissions; \ ++ i++) \ ++ M_counters[i] = (unsigned long) 0; \ ++ for (unsigned int j=0;jsecond); ++ } + + M_SizeOfResponseTimeRepartition = 0; + M_SizeOfCallLengthRepartition = 0; +@@ -437,7 +479,7 @@ void CStat::setRepartitionResponseTime (char * P_listeStr) + int sizeOfListe; + int i; + +- for (i = 0; i < MAX_RTD_INFO_LENGTH; i++) { ++ for (i = 0; i < nRtds(); i++) { + if(createIntegerTable(P_listeStr, &listeInteger, &sizeOfListe) == 1) { + initRepartition(listeInteger, + sizeOfListe, +@@ -464,7 +506,7 @@ void CStat::setRepartitionCallLength(unsigned int* repartition, + void CStat::setRepartitionResponseTime(unsigned int* repartition, + int nombre) + { +- for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) { ++ for (int i = 0; i < nRtds(); i++) { + initRepartition(repartition, + nombre, + &M_ResponseTimeRepartition[i], +@@ -628,6 +670,12 @@ int CStat::computeStat (E_Action P_action) + M_counters [CPT_PL_FailedCallRegexpDoesntMatch]++; + break; + ++ case E_FAILED_REGEXP_SHOULDNT_MATCH : ++ M_counters [CPT_C_FailedCallRegexpShouldntMatch]++; ++ M_counters [CPT_PD_FailedCallRegexpShouldntMatch]++; ++ M_counters [CPT_PL_FailedCallRegexpShouldntMatch]++; ++ break; ++ + case E_FAILED_REGEXP_HDR_NOT_FOUND : + M_counters [CPT_C_FailedCallRegexpHdrNotFound]++; + M_counters [CPT_PD_FailedCallRegexpHdrNotFound]++; +@@ -658,6 +706,11 @@ int CStat::computeStat (E_Action P_action) + M_counters [CPT_PL_Retransmissions]++; + break; + ++ case E_RESET_C_COUNTERS : ++ RESET_C_COUNTERS; ++ GET_TIME (&M_startTime); ++ break; ++ + case E_RESET_PD_COUNTERS : + //DEBUG (C_Debug::E_LEVEL_4, "ENTER CASE", "%s", + // "CStat::computeStat : RESET_PD_COUNTERS"); +@@ -672,7 +725,7 @@ int CStat::computeStat (E_Action P_action) + GET_TIME (&M_plStartTime); + if (periodic_rtd) { + resetRepartition(M_CallLengthRepartition, M_SizeOfCallLengthRepartition); +- for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) { ++ for (int i = 0; i < nRtds(); i++) { + resetRepartition(M_ResponseTimeRepartition[i], M_SizeOfResponseTimeRepartition); + } + } +@@ -694,12 +747,36 @@ int CStat::globalStat (E_Action P_action) { + M_G_counters [CPT_G_PL_OutOfCallMsgs - E_NB_COUNTER - 1]++; + break; + ++ case E_WATCHDOG_MAJOR : ++ M_G_counters [CPT_G_C_WatchdogMajor - E_NB_COUNTER - 1]++; ++ M_G_counters [CPT_G_PD_WatchdogMajor - E_NB_COUNTER - 1]++; ++ M_G_counters [CPT_G_PL_WatchdogMajor - E_NB_COUNTER - 1]++; ++ break; ++ ++ case E_WATCHDOG_MINOR : ++ M_G_counters [CPT_G_C_WatchdogMinor - E_NB_COUNTER - 1]++; ++ M_G_counters [CPT_G_PD_WatchdogMinor - E_NB_COUNTER - 1]++; ++ M_G_counters [CPT_G_PL_WatchdogMinor - E_NB_COUNTER - 1]++; ++ break; ++ + case E_DEAD_CALL_MSGS : + M_G_counters [CPT_G_C_DeadCallMsgs - E_NB_COUNTER - 1]++; + M_G_counters [CPT_G_PD_DeadCallMsgs - E_NB_COUNTER - 1]++; + M_G_counters [CPT_G_PL_DeadCallMsgs - E_NB_COUNTER - 1]++; + break; + ++ case E_FATAL_ERRORS : ++ M_G_counters [CPT_G_C_FatalErrors - E_NB_COUNTER - 1]++; ++ M_G_counters [CPT_G_PD_FatalErrors - E_NB_COUNTER - 1]++; ++ M_G_counters [CPT_G_PL_FatalErrors - E_NB_COUNTER - 1]++; ++ break; ++ ++ case E_WARNING : ++ M_G_counters [CPT_G_C_Warnings - E_NB_COUNTER - 1]++; ++ M_G_counters [CPT_G_PD_Warnings - E_NB_COUNTER - 1]++; ++ M_G_counters [CPT_G_PL_Warnings - E_NB_COUNTER - 1]++; ++ break; ++ + case E_AUTO_ANSWERED : + // Let's count the automatic answered calls + M_G_counters [CPT_G_C_AutoAnswered - 1]++; +@@ -747,7 +824,7 @@ void CStat::getStartTime(struct timeval *t) + double CStat::computeStdev(E_CounterName P_SumCounter, + E_CounterName P_NbOfCallUsed, + E_CounterName P_Squares) { +- if (M_counters[P_NbOfCallUsed] == 0) ++ if (M_counters[P_NbOfCallUsed] <= 0) + return 0.0; + + double numerator = ((double)(M_counters[P_NbOfCallUsed]) * (double)(M_counters[P_Squares])) - ((double)(M_counters[P_SumCounter] * M_counters[P_SumCounter])); +@@ -763,6 +840,29 @@ double CStat::computeMean(E_CounterName P_SumCounter, + return ((double)(M_counters[P_SumCounter]) / (double)(M_counters[P_NbOfCallUsed])); + } + ++double CStat::computeRtdMean(int which, int type) { ++ unsigned long long count = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) + RTD_COUNT]; ++ unsigned long long sum = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) + RTD_SUM]; ++ ++ if (count == 0) ++ return 0.0; ++ return ((double)(sum) / (double)(count)); ++} ++ ++double CStat::computeRtdStdev(int which, int type) { ++ unsigned long long count = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) + RTD_COUNT]; ++ unsigned long long sum = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) + RTD_SUM]; ++ unsigned long long sumsq = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) + RTD_SUMSQ]; ++ ++ if (count <= 1) ++ return 0.0; ++ ++ double numerator = ((double)count * (double)sumsq) - (double)(sum * sum); ++ double denominator = (double)(count) * ((double)(count) - 1.0); ++ ++ return sqrt(numerator/denominator); ++} ++ + void CStat::updateAverageCounter(E_CounterName P_SumCounter, + E_CounterName P_NbOfCallUsed, + E_CounterName P_Squares, +@@ -787,6 +887,109 @@ int CStat::computeStat (E_Action P_action, + return computeStat(P_action, P_value, 0); + } + ++int CStat::findCounter(const char *counter, bool alloc) { ++ str_int_map::iterator it = M_genericMap.find(str_int_map::key_type(counter)); ++ if (it != M_genericMap.end()) { ++ return it->second; ++ } ++ if (!alloc) { ++ return -1; ++ } ++ int ret = M_genericMap.size() + 1; ++ M_genericMap[str_int_map::key_type(counter)] = ret; ++ ++ bool numeric = true; ++ const char *p = counter; ++ while (*p) { ++ if (!isdigit(*p)) { ++ numeric = false; ++ break; ++ } ++ p++; ++ } ++ if (numeric) { ++ char *s = new char[20]; ++ snprintf(s, 20, "GenericCounter%s", counter); ++ M_revGenericMap[ret] = s; ++ M_genericDisplay[ret] = strdup(counter); ++ } else { ++ M_revGenericMap[ret] = strdup(counter); ++ M_genericDisplay[ret] = strdup(counter); ++ } ++ ++ ++ M_genericCounters = (unsigned long long *)realloc(M_genericCounters, sizeof(unsigned long long) * GENERIC_TYPES * M_genericMap.size()); ++ if (!M_genericCounters) { ++ ERROR("Could not allocate generic counters!\n"); ++ } ++ M_genericCounters[(ret - 1) * GENERIC_TYPES + GENERIC_C] = 0; ++ M_genericCounters[(ret - 1) * GENERIC_TYPES + GENERIC_PD] = 0; ++ M_genericCounters[(ret - 1)* GENERIC_TYPES + GENERIC_PL] = 0; ++ ++ return ret; ++} ++ ++int CStat::findRtd(const char *name, bool start) { ++ str_int_map::iterator it = M_rtdMap.find(str_int_map::key_type(name)); ++ if (it != M_rtdMap.end()) { ++ if (start) { ++ rtd_started[it->first] = true; ++ } else { ++ rtd_stopped[it->first] = true; ++ } ++ return it->second; ++ } ++ ++ int ret = M_rtdMap.size() + 1; ++ M_rtdMap[str_int_map::key_type(name)] = ret; ++ ++ M_revRtdMap[ret] = strdup(name); ++ ++ ++ M_rtdInfo = (unsigned long long *)realloc(M_rtdInfo, sizeof(unsigned long long) * RTD_TYPES * GENERIC_TYPES * M_rtdMap.size()); ++ if (!M_rtdInfo) { ++ ERROR("Could not allocate RTD info!\n"); ++ } ++ M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_COUNT] = 0; ++ M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUM] = 0; ++ M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUMSQ] = 0; ++ ++ M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_COUNT] = 0; ++ M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUM] = 0; ++ M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUMSQ] = 0; ++ ++ M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_COUNT] = 0; ++ M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUM] = 0; ++ M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUMSQ] = 0; ++ ++ M_ResponseTimeRepartition = (T_dynamicalRepartition **)realloc(M_ResponseTimeRepartition, sizeof(T_dynamicalRepartition *) * M_rtdMap.size()); ++ if (!M_ResponseTimeRepartition) { ++ ERROR("Could not allocate RTD info!\n"); ++ } ++ M_ResponseTimeRepartition[ret - 1] = NULL; ++ ++ if (start) { ++ rtd_started[name] = true; ++ } else { ++ rtd_stopped[name] = true; ++ } ++ return ret; ++} ++ ++int CStat::nRtds() { ++ return M_rtdMap.size(); ++} ++ ++/* If you start an RTD, then you should be interested in collecting statistics for it. */ ++void CStat::validateRtds() { ++ for (str_int_map::iterator it = rtd_started.begin(); it != rtd_started.end(); it++) { ++ str_int_map::iterator stopit = rtd_stopped.find(it->first); ++ if (stopit == rtd_stopped.end() || !stopit->second) { ++ ERROR("You have started Response Time Duration %s, but have never stopped it!", it->first.c_str()); ++ } ++ } ++} ++ + int CStat::computeStat (E_Action P_action, + unsigned long P_value, + int which) +@@ -812,26 +1015,27 @@ int CStat::computeStat (E_Action P_action, + + + case E_ADD_GENERIC_COUNTER : +- M_counters [CPT_C_Generic + which] += P_value; +- M_counters [CPT_PD_Generic + which] += P_value; +- M_counters [CPT_PL_Generic + which] += P_value; ++ M_genericCounters[which * GENERIC_TYPES + GENERIC_C] += P_value; ++ M_genericCounters[which * GENERIC_TYPES + GENERIC_PD] += P_value; ++ M_genericCounters[which * GENERIC_TYPES + GENERIC_PL] += P_value; + break; + + case E_ADD_RESPONSE_TIME_DURATION : + // Updating Cumulative Counter +- updateAverageCounter((E_CounterName)(CPT_C_AverageResponseTime_Sum + which), +- (E_CounterName)(CPT_C_NbOfCallUsedForAverageResponseTime + which), +- (E_CounterName)(CPT_C_AverageResponseTime_Squares + which), P_value); +- updateRepartition(M_ResponseTimeRepartition[which], +- M_SizeOfResponseTimeRepartition, P_value); ++ M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_COUNT]++; ++ M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUM] += P_value; ++ M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUMSQ] += (P_value * P_value); ++ updateRepartition(M_ResponseTimeRepartition[which], M_SizeOfResponseTimeRepartition, P_value); ++ + // Updating Periodical Diplayed counter +- updateAverageCounter((E_CounterName)(CPT_PD_AverageResponseTime_Sum + which), +- (E_CounterName)(CPT_PD_NbOfCallUsedForAverageResponseTime + which), +- (E_CounterName)(CPT_PD_AverageResponseTime_Squares + which), P_value); ++ M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_COUNT]++; ++ M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUM] += P_value; ++ M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUMSQ] += (P_value * P_value); ++ + // Updating Periodical Logging counter +- updateAverageCounter((E_CounterName)(CPT_PL_AverageResponseTime_Sum + which), +- (E_CounterName)(CPT_PL_NbOfCallUsedForAverageResponseTime + which), +- (E_CounterName)(CPT_PL_AverageResponseTime_Squares + which), P_value); ++ M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_COUNT]++; ++ M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUM] += P_value; ++ M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUMSQ] += (P_value * P_value); + break; + + default : +@@ -886,19 +1090,19 @@ CStat::CStat () + M_fileName = new char[L_size]; + strcpy(M_fileName, DEFAULT_FILE_NAME); + strcat(M_fileName, DEFAULT_EXTENSION); +- for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) { +- M_ResponseTimeRepartition[i] = NULL; +- } ++ M_ResponseTimeRepartition = NULL; + M_CallLengthRepartition = NULL; + M_SizeOfResponseTimeRepartition = 0; + M_SizeOfCallLengthRepartition = 0; + M_fileNameRtt = NULL; ++ M_genericCounters = NULL; + M_time_ref = 0.0 ; + M_dumpRespTime = NULL ; + M_counterDumpRespTime = 0 ; + M_dumpRespTime = NULL; + M_fileNameRtt = NULL; +- ++ M_rtdInfo = NULL; ++ + init(); + } + +@@ -906,23 +1110,28 @@ char* CStat::sRepartitionHeader(T_dynamicalRepartition * tabRepartition, + int sizeOfTab, + char * P_repartitionName) + { +- static char repartitionHeader[MAX_REPARTITION_HEADER_LENGTH]; ++ static char *repartitionHeader = NULL; + char buffer[MAX_CHAR_BUFFER_SIZE]; ++ int dlen = strlen(stat_delimiter); + + if(tabRepartition != NULL) + { ++ repartitionHeader = (char *)realloc(repartitionHeader, strlen(P_repartitionName) + dlen + 1); + sprintf(repartitionHeader, "%s%s", P_repartitionName, stat_delimiter); + for(int i=0; i<(sizeOfTab-1); i++) +- { ++ { + sprintf(buffer, "<%d%s", tabRepartition[i].borderMax, stat_delimiter); ++ repartitionHeader = (char *)realloc(repartitionHeader, strlen(repartitionHeader) + strlen(buffer) + 1); + strcat(repartitionHeader, buffer); + } + sprintf(buffer, ">=%d%s", tabRepartition[sizeOfTab-1].borderMax, stat_delimiter); ++ repartitionHeader = (char *)realloc(repartitionHeader, strlen(repartitionHeader) + strlen(buffer) + 1); + strcat(repartitionHeader, buffer); + } + else + { +- repartitionHeader[0] = '\0'; ++ repartitionHeader = (char *)realloc(repartitionHeader, 2); ++ strcpy(repartitionHeader, ""); + } + + return(repartitionHeader); +@@ -931,23 +1140,28 @@ char* CStat::sRepartitionHeader(T_dynamicalRepartition * tabRepartition, + char* CStat::sRepartitionInfo(T_dynamicalRepartition * tabRepartition, + int sizeOfTab) + { +- static char repartitionInfo[MAX_REPARTITION_INFO_LENGTH]; ++ static char *repartitionInfo; + char buffer[MAX_CHAR_BUFFER_SIZE]; ++ int dlen = strlen(stat_delimiter); + + if(tabRepartition != NULL) + { + // if a repartition is present, this field match the repartition name ++ repartitionInfo = (char *)realloc(repartitionInfo, dlen + 1); + sprintf(repartitionInfo, stat_delimiter); + for(int i=0; i<(sizeOfTab-1); i++) + { + sprintf(buffer, "%lu%s", tabRepartition[i].nbInThisBorder, stat_delimiter); ++ repartitionInfo = (char *)realloc(repartitionInfo, strlen(repartitionInfo) + strlen(buffer) + 1); + strcat(repartitionInfo, buffer); + } + sprintf(buffer, "%lu%s", tabRepartition[sizeOfTab-1].nbInThisBorder, stat_delimiter); ++ repartitionInfo = (char *)realloc(repartitionInfo, strlen(repartitionInfo) + strlen(buffer) + 1); + strcat(repartitionInfo, buffer); + } + else + { ++ repartitionInfo = (char *)realloc(repartitionInfo, 2); + repartitionInfo[0] = '\0'; + } + +@@ -1033,22 +1247,15 @@ void CStat::displayData (FILE *f) + M_counters[CPT_C_OutgoingCallCreated]); + DISPLAY_PERIO ("Current Call", M_counters[CPT_C_CurrentCall]); + +- bool first = true; +- for (int i = 0; i < MAX_COUNTER; i++) { +- char s[20]; +- +- if (M_counters[CPT_C_Generic + i] == 0) { +- continue; +- } +- +- if (first) { ++ if (M_genericMap.size()) { + DISPLAY_CROSS_LINE (); +- first = false; +- } +- +- sprintf(s, "Generic counter %d", i + 1); ++ } ++ for (unsigned int i = 1; i < M_genericMap.size() + 1; i++) { ++ char *s = (char *)malloc(20 + strlen(M_genericDisplay[i])); ++ sprintf(s, "Counter %s", M_genericDisplay[i]); + +- DISPLAY_2VAL(s, M_counters[CPT_PD_Generic + i], M_counters[CPT_C_Generic + i]); ++ DISPLAY_2VAL(s, M_genericCounters[(i - 1) * GENERIC_TYPES + GENERIC_PD], M_genericCounters[(i - 1) * GENERIC_TYPES + GENERIC_C]); ++ free(s); + } + + DISPLAY_CROSS_LINE (); +@@ -1064,33 +1271,26 @@ void CStat::displayData (FILE *f) + + + DISPLAY_CROSS_LINE (); +- for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) { +- char s[15]; ++ for (int i = 1; i <= nRtds(); i++) { ++ char s[80]; + +- if (!main_scenario->rtd_stopped[i]) { +- continue; +- } ++ /* Skip if we aren't stopped. */ ++ assert(rtd_stopped[M_revRtdMap[i]] == true); + +- sprintf(s, "Response Time %d", i + 1); ++ sprintf(s, "Response Time %s", M_revRtdMap[i]); + DISPLAY_TXT_COL (s, +- msToHHMMSSmmm( (unsigned long)computeMean((E_CounterName)(CPT_PD_AverageResponseTime_Sum + i), (E_CounterName)(CPT_PD_NbOfCallUsedForAverageResponseTime + i)) ), +- msToHHMMSSmmm( (unsigned long)computeMean((E_CounterName)(CPT_C_AverageResponseTime_Sum + i), (E_CounterName)(CPT_C_NbOfCallUsedForAverageResponseTime + i)) ) ); ++ msToHHMMSSmmm( (unsigned long)computeRtdMean(i, GENERIC_PD)), ++ msToHHMMSSmmm( (unsigned long)computeRtdMean(i, GENERIC_C))); + } +- DISPLAY_TXT_COL ("Call Length", ++/* I Broke this! ++ DISPLAY_TXT_COL ("Call Length", + msToHHMMSSmmm( (unsigned long)computeMean(CPT_PD_AverageCallLength_Sum, CPT_PD_NbOfCallUsedForAverageCallLength)), + msToHHMMSSmmm( (unsigned long)computeMean(CPT_C_AverageCallLength_Sum, CPT_C_NbOfCallUsedForAverageCallLength) )); ++*/ + DISPLAY_CROSS_LINE (); + +- for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) { +- char s[50]; +- +- if (!main_scenario->rtd_stopped[i]) { +- continue; +- } +- +- sprintf(s, "Average Response Time Repartition, %d", i + 1); +- DISPLAY_INFO(s); +- displayRepartition(f, M_ResponseTimeRepartition[i], M_SizeOfResponseTimeRepartition); ++ for (int i = 1; i <= nRtds(); i++) { ++ displayRtdRepartition(f, i); + } + DISPLAY_INFO("Average Call Length Repartition"); + displayRepartition(f, M_CallLengthRepartition, M_SizeOfCallLengthRepartition); +@@ -1160,22 +1360,15 @@ void CStat::displayStat (FILE *f) + DISPLAY_PERIO ("Current Call", + M_counters[CPT_C_CurrentCall]); + +- bool first = true; +- for (int i = 0; i < MAX_COUNTER; i++) { +- char s[20]; +- +- if (M_counters[CPT_C_Generic + i] == 0) { +- continue; +- } +- +- if (first) { ++ if (M_genericMap.size()) { + DISPLAY_CROSS_LINE (); +- first = false; +- } +- +- sprintf(s, "Generic counter %d", i + 1); ++ } ++ for (unsigned int i = 1; i < M_genericMap.size() + 1; i++) { ++ char *s = (char *)malloc(20 + strlen(M_genericDisplay[i])); ++ sprintf(s, "Counter %s", M_genericDisplay[i]); + +- DISPLAY_2VAL(s, M_counters[CPT_PD_Generic + i], M_counters[CPT_C_Generic + i]); ++ DISPLAY_2VAL(s, M_genericCounters[(i - 1)* GENERIC_TYPES + GENERIC_PD], M_genericCounters[(i - 1) * GENERIC_TYPES + GENERIC_C]); ++ free(s); + } + + DISPLAY_CROSS_LINE (); +@@ -1190,18 +1383,13 @@ void CStat::displayStat (FILE *f) + // M_counters[CPT_C_UnexpectedMessage]); + + DISPLAY_CROSS_LINE (); +- for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) { +- char s[20]; +- +- if (!main_scenario->rtd_stopped[i]) { +- continue; +- } ++ for (int i = 1; i <= nRtds(); i++) { ++ char s[80]; + +- +- sprintf(s, "Response Time %d", i + 1); ++ sprintf(s, "Response Time %s", M_revRtdMap[i]); + DISPLAY_TXT_COL (s, +- msToHHMMSSmmm( (unsigned long)computeMean((E_CounterName)(CPT_PD_AverageResponseTime_Sum + i), (E_CounterName)(CPT_PD_NbOfCallUsedForAverageResponseTime + i)) ), +- msToHHMMSSmmm( (unsigned long)computeMean((E_CounterName)(CPT_C_AverageResponseTime_Sum + i), (E_CounterName)(CPT_C_NbOfCallUsedForAverageResponseTime + i)))); ++ msToHHMMSSmmm( (unsigned long)computeRtdMean(i, GENERIC_PD)), ++ msToHHMMSSmmm( (unsigned long)computeRtdMean(i, GENERIC_C))); + } + DISPLAY_TXT_COL ("Call Length", + msToHHMMSSmmm( (unsigned long)computeMean(CPT_PD_AverageCallLength_Sum, CPT_PD_NbOfCallUsedForAverageCallLength ) ), +@@ -1211,21 +1399,25 @@ void CStat::displayStat (FILE *f) + + void CStat::displayRepartition (FILE *f) + { +- DISPLAY_INFO("Average Response Time Repartition"); +- displayRepartition(f, +- M_ResponseTimeRepartition[0], +- M_SizeOfResponseTimeRepartition); ++ displayRtdRepartition(f, 1); + DISPLAY_INFO("Average Call Length Repartition"); + displayRepartition(f, + M_CallLengthRepartition, + M_SizeOfCallLengthRepartition); + } + +-void CStat::displaySecondaryRepartition (FILE *f, int which) ++void CStat::displayRtdRepartition (FILE *f, int which) + { +- DISPLAY_INFO("Average Response Time Repartition"); ++ if (which > nRtds()) { ++ DISPLAY_INFO (" "); ++ return; ++ } ++ ++ char s[80]; ++ snprintf(s, sizeof(s), "Average Response Time Repartition %s", M_revRtdMap[which]); ++ DISPLAY_INFO(s); + displayRepartition(f, +- M_ResponseTimeRepartition[which], ++ M_ResponseTimeRepartition[which - 1], + M_SizeOfResponseTimeRepartition); + } + +@@ -1311,6 +1503,8 @@ void CStat::dumpData () + << "FailedCmdNotSent(C)" << stat_delimiter + << "FailedRegexpDoesntMatch(P)" << stat_delimiter + << "FailedRegexpDoesntMatch(C)" << stat_delimiter ++ << "FailedRegexpShouldntMatch(P)" << stat_delimiter ++ << "FailedRegexpShouldntMatch(C)" << stat_delimiter + << "FailedRegexpHdrNotFound(P)" << stat_delimiter + << "FailedRegexpHdrNotFound(C)" << stat_delimiter + << "FailedOutboundCongestion(P)" << stat_delimiter +@@ -1326,23 +1520,27 @@ void CStat::dumpData () + << "Retransmissions(P)" << stat_delimiter + << "Retransmissions(C)" << stat_delimiter + << "AutoAnswered(P)" << stat_delimiter +- << "AutoAnswered(C)" << stat_delimiter; +- +- for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) { +- char s_P[30]; +- char s_C[30]; +- +- if (!main_scenario->rtd_stopped[i]) { +- continue; +- } +- +- sprintf(s_P, "ResponseTime%d(P)%s", i + 1, stat_delimiter); +- sprintf(s_C, "ResponseTime%d(C)%s", i + 1, stat_delimiter); ++ << "AutoAnswered(C)" << stat_delimiter ++ << "Warnings(P)" << stat_delimiter ++ << "Warnings(C)" << stat_delimiter ++ << "FatalErrors(P)" << stat_delimiter ++ << "FatalErrors(C)" << stat_delimiter ++ << "WatchdogMajor(P)" << stat_delimiter ++ << "WatchdogMajor(C)" << stat_delimiter ++ << "WatchdogMinor(P)" << stat_delimiter ++ << "WatchdogMinor(C)" << stat_delimiter; ++ ++ for (int i = 1; i <= nRtds(); i++) { ++ char s_P[80]; ++ char s_C[80]; ++ ++ sprintf(s_P, "ResponseTime%s(P)%s", M_revRtdMap[i], stat_delimiter); ++ sprintf(s_C, "ResponseTime%s(C)%s", M_revRtdMap[i], stat_delimiter); + + (*M_outputStream) << s_P << s_C; + +- sprintf(s_P, "ResponseTime%dStDev(P)%s", i + 1, stat_delimiter); +- sprintf(s_C, "ResponseTime%dStDev(C)%s", i + 1, stat_delimiter); ++ sprintf(s_P, "ResponseTime%sStDev(P)%s", M_revRtdMap[i], stat_delimiter); ++ sprintf(s_C, "ResponseTime%sStDev(C)%s", M_revRtdMap[i], stat_delimiter); + + (*M_outputStream) << s_P << s_C; + } +@@ -1351,19 +1549,15 @@ void CStat::dumpData () + << "CallLength(C)" << stat_delimiter; + (*M_outputStream) << "CallLengthStDev(P)" << stat_delimiter + << "CallLengthStDev(C)" << stat_delimiter; +- for (int i = 0; i < MAX_COUNTER; i++) { +- (*M_outputStream) << "GenericCounter" << (i + 1) << "(P)" << stat_delimiter; +- (*M_outputStream) << "GenericCounter" << (i + 1) << "(C)" << stat_delimiter; ++ for (unsigned int i = 1; i < M_genericMap.size() + 1; i++) { ++ (*M_outputStream) << M_revGenericMap[i] << "(P)" << stat_delimiter; ++ (*M_outputStream) << M_revGenericMap[i] << "(C)" << stat_delimiter; + } +- for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) { +- char s[30]; ++ for (int i = 1; i <= nRtds(); i++) { ++ char s[80]; + +- if (!main_scenario->rtd_stopped[i]) { +- continue; +- } +- +- sprintf(s, "ResponseTimeRepartition%d", i + 1); +- (*M_outputStream) << sRepartitionHeader(M_ResponseTimeRepartition[i], ++ sprintf(s, "ResponseTimeRepartition%s", M_revRtdMap[i]); ++ (*M_outputStream) << sRepartitionHeader(M_ResponseTimeRepartition[i - 1], + M_SizeOfResponseTimeRepartition, + s); + } +@@ -1414,6 +1608,8 @@ void CStat::dumpData () + << M_counters[CPT_C_FailedCallCmdNotSent] << stat_delimiter + << M_counters[CPT_PL_FailedCallRegexpDoesntMatch] << stat_delimiter + << M_counters[CPT_C_FailedCallRegexpDoesntMatch] << stat_delimiter ++ << M_counters[CPT_PL_FailedCallRegexpShouldntMatch] << stat_delimiter ++ << M_counters[CPT_C_FailedCallRegexpShouldntMatch] << stat_delimiter + << M_counters[CPT_PL_FailedCallRegexpHdrNotFound] << stat_delimiter + << M_counters[CPT_C_FailedCallRegexpHdrNotFound] << stat_delimiter + << M_counters[CPT_PL_FailedOutboundCongestion] << stat_delimiter +@@ -1422,36 +1618,29 @@ void CStat::dumpData () + << M_counters[CPT_C_FailedTimeoutOnRecv] << stat_delimiter + << M_counters[CPT_PL_FailedTimeoutOnSend] << stat_delimiter + << M_counters[CPT_C_FailedTimeoutOnSend] << stat_delimiter +- << M_G_counters[CPT_G_PL_OutOfCallMsgs - E_NB_COUNTER - 1] << stat_delimiter +- << M_G_counters[CPT_G_C_OutOfCallMsgs - E_NB_COUNTER - 1] << stat_delimiter ++ << M_G_counters[CPT_G_PL_OutOfCallMsgs - E_NB_COUNTER - 1] << stat_delimiter ++ << M_G_counters[CPT_G_C_OutOfCallMsgs - E_NB_COUNTER - 1] << stat_delimiter + << M_G_counters[CPT_G_PL_DeadCallMsgs - E_NB_COUNTER - 1] << stat_delimiter + << M_G_counters[CPT_G_C_DeadCallMsgs - E_NB_COUNTER - 1] << stat_delimiter + << M_counters[CPT_PL_Retransmissions] << stat_delimiter + << M_counters[CPT_C_Retransmissions] << stat_delimiter + << M_G_counters[CPT_G_PL_AutoAnswered - E_NB_COUNTER - 1] << stat_delimiter +- << M_G_counters[CPT_G_C_AutoAnswered - E_NB_COUNTER - 1] << stat_delimiter; ++ << M_G_counters[CPT_G_C_AutoAnswered - E_NB_COUNTER - 1] << stat_delimiter ++ << M_G_counters[CPT_G_PL_Warnings - E_NB_COUNTER - 1] << stat_delimiter ++ << M_G_counters[CPT_G_C_Warnings - E_NB_COUNTER - 1] << stat_delimiter ++ << M_G_counters[CPT_G_PL_FatalErrors - E_NB_COUNTER - 1] << stat_delimiter ++ << M_G_counters[CPT_G_C_FatalErrors - E_NB_COUNTER - 1] << stat_delimiter ++ << M_G_counters[CPT_G_PL_WatchdogMajor - E_NB_COUNTER - 1] << stat_delimiter ++ << M_G_counters[CPT_G_C_WatchdogMajor - E_NB_COUNTER - 1] << stat_delimiter ++ << M_G_counters[CPT_G_PL_WatchdogMinor - E_NB_COUNTER - 1] << stat_delimiter ++ << M_G_counters[CPT_G_C_WatchdogMinor - E_NB_COUNTER - 1] << stat_delimiter; + + // SF917289 << M_counters[CPT_C_UnexpectedMessage] << stat_delimiter; +- for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) { +- if (!main_scenario->rtd_stopped[i]) { +- continue; +- } +- +- (*M_outputStream) +- << msToHHMMSSmmm( (unsigned long)computeMean((E_CounterName)(CPT_PL_AverageResponseTime_Sum + i), +- (E_CounterName)(CPT_PL_NbOfCallUsedForAverageResponseTime + i))) << stat_delimiter; +- (*M_outputStream) +- << msToHHMMSSmmm( (unsigned long)computeMean((E_CounterName)(CPT_C_AverageResponseTime_Sum + i), +- (E_CounterName)(CPT_C_NbOfCallUsedForAverageResponseTime + i))) << stat_delimiter; +- +- (*M_outputStream) +- << msToHHMMSSmmm( (unsigned long)computeStdev((E_CounterName)(CPT_PL_AverageResponseTime_Sum + i), +- (E_CounterName)(CPT_PL_NbOfCallUsedForAverageResponseTime + i), +- (E_CounterName)(CPT_PL_AverageResponseTime_Squares + i)) ) << stat_delimiter; +- (*M_outputStream) +- << msToHHMMSSmmm( (unsigned long)computeStdev((E_CounterName)(CPT_C_AverageResponseTime_Sum + i), +- (E_CounterName)(CPT_C_NbOfCallUsedForAverageResponseTime + i), +- (E_CounterName)(CPT_C_AverageResponseTime_Squares + i)) ) << stat_delimiter; ++ for (int i = 1; i <= nRtds(); i++) { ++ (*M_outputStream) << msToHHMMSSmmm( (unsigned long)computeRtdMean(i, GENERIC_PL)) << stat_delimiter; ++ (*M_outputStream) << msToHHMMSSmmm( (unsigned long)computeRtdMean(i, GENERIC_C)) << stat_delimiter; ++ (*M_outputStream) << msToHHMMSSmmm( (unsigned long)computeRtdStdev(i, GENERIC_PL)) << stat_delimiter; ++ (*M_outputStream) << msToHHMMSSmmm( (unsigned long)computeRtdStdev(i, GENERIC_C)) << stat_delimiter; + } + (*M_outputStream) + << msToHHMMSSmmm( (unsigned long)computeMean(CPT_PL_AverageCallLength_Sum, CPT_PL_NbOfCallUsedForAverageCallLength) ) << stat_delimiter; +@@ -1466,15 +1655,12 @@ void CStat::dumpData () + CPT_C_NbOfCallUsedForAverageCallLength, + CPT_C_AverageCallLength_Squares )) << stat_delimiter; + +- for (int i = 0; i < MAX_COUNTER; i++) { +- (*M_outputStream) << M_counters[CPT_PL_Generic + i] << stat_delimiter; +- (*M_outputStream) << M_counters[CPT_C_Generic + i] << stat_delimiter; ++ for (unsigned int i = 0; i < M_genericMap.size(); i++) { ++ (*M_outputStream) << M_genericCounters[GENERIC_TYPES * i + GENERIC_PL] << stat_delimiter; ++ (*M_outputStream) << M_genericCounters[GENERIC_TYPES * i + GENERIC_C] << stat_delimiter; + } + +- for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) { +- if (!main_scenario->rtd_stopped[i]) { +- continue; +- } ++ for (int i = 0; i < nRtds(); i++) { + (*M_outputStream) + << sRepartitionInfo(M_ResponseTimeRepartition[i], + M_SizeOfResponseTimeRepartition); +@@ -1509,7 +1695,7 @@ void CStat::dumpDataRtt () + } + #endif + } +- ++ + if(M_headerAlreadyDisplayedRtt == false) { + (*M_outputStreamRtt) << "Date_ms" << stat_delimiter + << "response_time_ms" << stat_delimiter +@@ -1520,16 +1706,16 @@ void CStat::dumpDataRtt () + for (unsigned int L_i = 0; L_i < M_counterDumpRespTime ; L_i ++) { + (*M_outputStreamRtt) << M_dumpRespTime[L_i].date << stat_delimiter ; + (*M_outputStreamRtt) << M_dumpRespTime[L_i].rtt << stat_delimiter ; +- (*M_outputStreamRtt) << M_dumpRespTime[L_i].rtd_no << endl; ++ (*M_outputStreamRtt) << M_revRtdMap[M_dumpRespTime[L_i].rtd_no] << endl; + (*M_outputStreamRtt).flush(); + M_dumpRespTime[L_i].date = 0.0; + M_dumpRespTime[L_i].rtt = 0.0; + M_dumpRespTime[L_i].rtd_no = 0; + } +- ++ + // flushing the output file + (*M_outputStreamRtt).flush(); +- ++ + M_counterDumpRespTime = 0; + } + +@@ -1587,7 +1773,7 @@ char* CStat::formatTime (struct timeval* P_tv, bool microseconds) + L_currentDate->tm_sec, + (double)P_tv->tv_usec/(double)1000.0); + } else { +- sprintf(L_time, "%4.4d-%2.2d-%2.2d\t%2.2d:%2.2d:%2.2d:%3.3d\t%10.10d.%6.6d", ++ sprintf(L_time, "%4.4d-%2.2d-%2.2d\t%2.2d:%2.2d:%2.2d:%3.3d\t%10.10ld.%6.6ld", + L_currentDate->tm_year + 1900, + L_currentDate->tm_mon + 1, + L_currentDate->tm_mday, +@@ -1596,7 +1782,7 @@ char* CStat::formatTime (struct timeval* P_tv, bool microseconds) + L_currentDate->tm_sec, + (int) (P_tv->tv_usec)/1000, + (long) (P_tv->tv_sec), +- (long) (P_tv->tv_usec)); ++ (long) (P_tv->tv_usec)); + } + } + return (L_time); +@@ -1612,6 +1798,9 @@ long CStat::computeDiffTimeInMs (struct timeval* tf, struct timeval* ti) + return (v1*1000 + v2/1000); + } + ++CSample::~CSample() { ++} ++ + + /* Implementation of a fixed distribution. */ + CFixed::CFixed(double value) { +diff --git a/stat.hpp b/stat.hpp +index af90e70..1521c84 100644 +--- a/stat.hpp ++++ b/stat.hpp +@@ -29,10 +29,7 @@ + #define DEFAULT_FILE_NAME (char*)"dumpFile" + #define DEFAULT_EXTENSION (char*)".csv" + +-#define MAX_REPARTITION_HEADER_LENGTH 1024 +-#define MAX_REPARTITION_INFO_LENGTH 1024 + #define MAX_CHAR_BUFFER_SIZE 1024 +-#define MAX_COUNTER 5 + + #include + #include +@@ -47,11 +44,7 @@ + #include + #endif + +-/* MAX_RTD_INFO_LENGTH defines the number of RTD begin and end points a single +- * call can have. If you need more than five, you can increase this number, +- * but you also need to insert entries into the E_CounterName enum in stat.hpp. +- */ +-#define MAX_RTD_INFO_LENGTH 5 ++#include "variables.hpp" + + using namespace std; + +@@ -98,6 +91,7 @@ public: + E_CREATE_INCOMING_CALL, + E_CALL_FAILED, + E_CALL_SUCCESSFULLY_ENDED, ++ E_RESET_C_COUNTERS, + E_RESET_PD_COUNTERS, + E_RESET_PL_COUNTERS, + E_ADD_CALL_DURATION, +@@ -110,12 +104,17 @@ public: + E_FAILED_CALL_REJECTED, + E_FAILED_CMD_NOT_SENT, + E_FAILED_REGEXP_DOESNT_MATCH, ++ E_FAILED_REGEXP_SHOULDNT_MATCH, + E_FAILED_REGEXP_HDR_NOT_FOUND, + E_FAILED_OUTBOUND_CONGESTION, + E_FAILED_TIMEOUT_ON_RECV, + E_FAILED_TIMEOUT_ON_SEND, + E_OUT_OF_CALL_MSGS, ++ E_WATCHDOG_MAJOR, ++ E_WATCHDOG_MINOR, + E_DEAD_CALL_MSGS, ++ E_FATAL_ERRORS, ++ E_WARNING, + E_RETRANSMISSION, + E_AUTO_ANSWERED, + E_ADD_GENERIC_COUNTER +@@ -137,21 +136,6 @@ public: + CPT_C_NbOfCallUsedForAverageCallLength, + CPT_C_AverageCallLength_Sum, + CPT_C_AverageCallLength_Squares, +- CPT_C_NbOfCallUsedForAverageResponseTime, +- CPT_C_NbOfCallUsedForAverageResponseTime_2, +- CPT_C_NbOfCallUsedForAverageResponseTime_3, +- CPT_C_NbOfCallUsedForAverageResponseTime_4, +- CPT_C_NbOfCallUsedForAverageResponseTime_5, // This must match or exceed MAX_RTD_INFO +- CPT_C_AverageResponseTime_Sum, +- CPT_C_AverageResponseTime_Sum_2, +- CPT_C_AverageResponseTime_Sum_3, +- CPT_C_AverageResponseTime_Sum_4, +- CPT_C_AverageResponseTime_Sum_5, // This must match or exceed MAX_RTD_INFO +- CPT_C_AverageResponseTime_Squares, +- CPT_C_AverageResponseTime_Squares_2, +- CPT_C_AverageResponseTime_Squares_3, +- CPT_C_AverageResponseTime_Squares_4, +- CPT_C_AverageResponseTime_Squares_5, + CPT_C_FailedCallCannotSendMessage, + CPT_C_FailedCallMaxUdpRetrans, + CPT_C_FailedCallTcpConnect, +@@ -160,15 +144,11 @@ public: + CPT_C_FailedCallCallRejected, + CPT_C_FailedCallCmdNotSent, + CPT_C_FailedCallRegexpDoesntMatch, ++ CPT_C_FailedCallRegexpShouldntMatch, + CPT_C_FailedCallRegexpHdrNotFound, + CPT_C_FailedOutboundCongestion, + CPT_C_FailedTimeoutOnRecv, + CPT_C_FailedTimeoutOnSend, +- CPT_C_Generic, +- CPT_C_Generic_2, +- CPT_C_Generic_3, +- CPT_C_Generic_4, +- CPT_C_Generic_5, // This must match or exceed MAX_COUNTER + CPT_C_Retransmissions, + + // Periodic Display counter +@@ -204,15 +184,11 @@ public: + CPT_PD_FailedCallCallRejected, + CPT_PD_FailedCallCmdNotSent, + CPT_PD_FailedCallRegexpDoesntMatch, ++ CPT_PD_FailedCallRegexpShouldntMatch, + CPT_PD_FailedCallRegexpHdrNotFound, + CPT_PD_FailedOutboundCongestion, + CPT_PD_FailedTimeoutOnRecv, + CPT_PD_FailedTimeoutOnSend, +- CPT_PD_Generic, +- CPT_PD_Generic_2, +- CPT_PD_Generic_3, +- CPT_PD_Generic_4, +- CPT_PD_Generic_5, // This must match or exceed MAX_COUNTER + CPT_PD_Retransmissions, + + // Periodic logging counter +@@ -249,15 +225,11 @@ public: + CPT_PL_FailedCallCallRejected, + CPT_PL_FailedCallCmdNotSent, + CPT_PL_FailedCallRegexpDoesntMatch, ++ CPT_PL_FailedCallRegexpShouldntMatch, + CPT_PL_FailedCallRegexpHdrNotFound, + CPT_PL_FailedOutboundCongestion, + CPT_PL_FailedTimeoutOnRecv, + CPT_PL_FailedTimeoutOnSend, +- CPT_PL_Generic, +- CPT_PL_Generic_2, +- CPT_PL_Generic_3, +- CPT_PL_Generic_4, +- CPT_PL_Generic_5, + CPT_PL_Retransmissions, + + E_NB_COUNTER, +@@ -266,15 +238,27 @@ public: + // Cumulative counters + CPT_G_C_OutOfCallMsgs, + CPT_G_C_DeadCallMsgs, ++ CPT_G_C_FatalErrors, ++ CPT_G_C_Warnings, ++ CPT_G_C_WatchdogMajor, ++ CPT_G_C_WatchdogMinor, + CPT_G_C_AutoAnswered, + // Periodic Display counter + CPT_G_PD_OutOfCallMsgs, + CPT_G_PD_DeadCallMsgs, ++ CPT_G_PD_FatalErrors, ++ CPT_G_PD_Warnings, ++ CPT_G_PD_WatchdogMajor, ++ CPT_G_PD_WatchdogMinor, + CPT_G_PD_AutoAnswered, // must be last (RESET_PD_COUNTER) + + // Periodic logging counter + CPT_G_PL_OutOfCallMsgs, + CPT_G_PL_DeadCallMsgs, ++ CPT_G_PL_FatalErrors, ++ CPT_G_PL_Warnings, ++ CPT_G_PL_WatchdogMajor, ++ CPT_G_PL_WatchdogMinor, + CPT_G_PL_AutoAnswered, // must be last (RESET_PL_COUNTER) + + E_NB_G_COUNTER, +@@ -377,8 +361,7 @@ public: + void displayData (FILE *f); + void displayStat(FILE *f); + void displayRepartition(FILE *f); +- void displaySecondaryRepartition (FILE *f, int which); +- ++ void displayRtdRepartition (FILE *f, int which); + + /** + * Dump data periodically in the file M_FileName +@@ -428,12 +411,38 @@ public: + */ + static char* msToHHMMSSmmm (unsigned long P_ms); + ++ /* Get a counter ID by name. */ ++ int findCounter(const char *counter, bool alloc); ++ int findRtd(const char *name, bool start); ++ void validateRtds(); ++ int nRtds(); + + private: + unsigned long long M_counters[E_NB_COUNTER]; + static unsigned long long M_G_counters[E_NB_G_COUNTER - E_NB_COUNTER]; + +- T_dynamicalRepartition* M_ResponseTimeRepartition[MAX_RTD_INFO_LENGTH]; ++#define GENERIC_C 0 ++#define GENERIC_PD 1 ++#define GENERIC_PL 2 ++#define GENERIC_TYPES 3 ++ unsigned long long *M_genericCounters; ++ ++ str_int_map M_genericMap; ++ int_str_map M_revGenericMap; ++ int_str_map M_genericDisplay; ++ ++ str_int_map rtd_started; ++ str_int_map rtd_stopped; ++ ++#define RTD_COUNT 0 ++#define RTD_SUM 1 ++#define RTD_SUMSQ 2 ++#define RTD_TYPES 3 ++ unsigned long long *M_rtdInfo; ++ str_int_map M_rtdMap; ++ int_str_map M_revRtdMap; ++ ++ T_dynamicalRepartition** M_ResponseTimeRepartition; + T_dynamicalRepartition* M_CallLengthRepartition; + int M_SizeOfResponseTimeRepartition; + int M_SizeOfCallLengthRepartition; +@@ -553,6 +562,10 @@ private: + */ + double computeMean(E_CounterName P_SumCounter, + E_CounterName P_NbOfCallUsed); ++ ++ double computeRtdMean(int which, int type); ++ double computeRtdStdev(int which, int type); ++ + /** + * Effective C++ + * +@@ -577,6 +590,7 @@ public: + virtual int textDescr(char *s, int len) = 0; + virtual int timeDescr(char *s, int len) = 0; + virtual double cdfInv(double percentile) = 0; ++ virtual ~CSample(); + private: + }; + +diff --git a/task.cpp b/task.cpp +index 5856921..bcaac76 100644 +--- a/task.cpp ++++ b/task.cpp +@@ -110,6 +110,8 @@ task_list *timewheel::task2list(task *task) { + unsigned int wake_sigbits = wake; + unsigned int base_sigbits = wheel_base; + ++ assert(wheel_base <= clock_tick); ++ + if (wake == 0) { + return &forever_list; + } +@@ -188,6 +190,10 @@ int timewheel::expire_paused_tasks() { + + void timewheel::add_paused_task(task *task, bool increment) { + task_list::iterator task_it; ++ if (task->wake() && task->wake() < wheel_base) { ++ task->add_to_runqueue(); ++ return; ++ } + task_list *list = task2list(task); + task_it = list->insert(list->end(), task); + task->pauselist = list; +@@ -220,8 +226,18 @@ void task::setRunning() { + } + + void task::setPaused() { ++ if (running) { + if (!remove_from_runqueue()) { +- ERROR("Tried to remove a running call that wasn't running!\n"); ++ WARNING("Tried to remove a running call that wasn't running!\n"); ++ assert(0); + } +- add_to_paused_tasks(true); ++ } else { ++ paused_tasks.remove_paused_task(this); ++ } ++ assert(running == false); ++ add_to_paused_tasks(true); ++} ++ ++void task::abort() { ++ delete this; + } +diff --git a/task.hpp b/task.hpp +index 1512d41..da42a2b 100644 +--- a/task.hpp ++++ b/task.hpp +@@ -80,7 +80,7 @@ public: + virtual bool run() = 0; + + /* Our abort action. */ +- virtual void abort() = 0; ++ virtual void abort(); + + /* Dump task info to error log. */ + virtual void dump() = 0; +diff --git a/variables.cpp b/variables.cpp +index d354386..96b6489 100644 +--- a/variables.cpp ++++ b/variables.cpp +@@ -39,6 +39,8 @@ bool CCallVariable::isSet() + return(false); + } else if (M_type == E_VT_BOOL) { + return M_bool; ++ } else if (M_type == E_VT_DOUBLE) { ++ return M_double; + } + return (M_type != E_VT_UNDEFINED); + } +@@ -333,7 +335,7 @@ int AllocVariableTable::find(const char *varName, bool allocate) { + } + + char *AllocVariableTable::getName(int i) { +- int thisLevel = i & (1 << LEVEL_BITS); ++ int thisLevel = i & ((1 << LEVEL_BITS) - 1); + assert(thisLevel <= level); + if (thisLevel == level) { + return variableRevMap[i]; +@@ -342,6 +344,16 @@ char *AllocVariableTable::getName(int i) { + return av_parent->getName(i); + } + ++void AllocVariableTable::dump() { ++ if (av_parent) { ++ av_parent->dump(); ++ } ++ WARNING("%d level %d variables:", variableMap.size(), level); ++ for (str_int_map::iterator i = variableMap.begin(); i != variableMap.end(); i++) { ++ WARNING("%s", i->first.c_str()); ++ } ++} ++ + void AllocVariableTable::validate() { + for (str_int_map::iterator var_it = variableMap.begin(); var_it != variableMap.end(); var_it++) { + if (variableReferences[var_it->second] < 2) { +diff --git a/variables.hpp b/variables.hpp +index 67d6c8d..89a5527 100644 +--- a/variables.hpp ++++ b/variables.hpp +@@ -24,6 +24,8 @@ + #ifndef _CVARIABLE + #define _CVARIABLE + ++#include ++#include + #include + #include + +@@ -117,6 +119,7 @@ public: + int find(const char *name, bool allocate); + char *getName(int i); + void validate(); ++ void dump(); + private: + AllocVariableTable *av_parent; + str_int_map variableMap; +diff --git a/watchdog.cpp b/watchdog.cpp +new file mode 100644 +index 0000000..185c71c +--- /dev/null ++++ b/watchdog.cpp +@@ -0,0 +1,81 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Author : Richard GAYRAUD - 04 Nov 2003 ++ * Marc LAMBERTON ++ * Olivier JACQUES ++ * Herve PELLAN ++ * David MANSUTTI ++ * Francois-Xavier Kowalski ++ * Gerard Lyonnaz ++ * From Hewlett Packard Company. ++ * F. Tarek Rogers ++ * Peter Higginson ++ * Vincent Luba ++ * Shriram Natarajan ++ * Guillaume Teissier from FTR&D ++ * Clement Chen ++ * Wolfgang Beck ++ * Charles P Wright from IBM Research ++ */ ++#include "sipp.hpp" ++ ++void watchdog::dump() { ++ WARNING("Watchdog Task: interval = %d, major_threshold = %d (%d triggers left), minor_threshold = %d (%d triggers left)", interval, major_threshold, major_maxtriggers, minor_threshold, minor_maxtriggers); ++} ++ ++watchdog::watchdog(int interval, int reset_interval, int major_threshold, int major_maxtriggers, int minor_threshold, int minor_maxtriggers) { ++ this->interval = interval; ++ this->reset_interval = reset_interval; ++ this->major_threshold = major_threshold; ++ this->major_maxtriggers = major_maxtriggers; ++ this->minor_threshold = minor_threshold; ++ this->minor_maxtriggers = minor_maxtriggers; ++ major_triggers = 0; ++ minor_triggers = 0; ++ last_trigger = last_fire = getmilliseconds(); ++} ++ ++bool watchdog::run() { ++ getmilliseconds(); ++ if (last_fire + this->major_threshold < clock_tick) { ++ CStat::globalStat(CStat::E_WATCHDOG_MAJOR); ++ last_trigger = clock_tick; ++ WARNING("The major watchdog timer %dms has been tripped (%d), %d trips remaining.", major_threshold, clock_tick - last_fire, major_maxtriggers - major_triggers); ++ if ((this->major_maxtriggers != -1) && (++major_triggers > this->major_maxtriggers)) { ++ ERROR("The watchdog timer has tripped the major threshold of %dms too many times (%d out of %d allowed) (%d out of %d minor %dms timeouts tripped)\n", major_threshold, major_triggers, major_maxtriggers, minor_threshold, minor_triggers, minor_maxtriggers); ++ } ++ } else if (last_fire + this->minor_threshold < clock_tick) { ++ last_trigger = clock_tick; ++ CStat::globalStat(CStat::E_WATCHDOG_MINOR); ++ WARNING("The minor watchdog timer %dms has been tripped (%d), %d trips remaining.", minor_threshold, clock_tick - last_fire, minor_maxtriggers - minor_triggers); ++ if ((this->minor_maxtriggers != -1) && (++minor_triggers > this->minor_maxtriggers)) { ++ ERROR("The watchdog timer has tripped the minor threshold of %dms too many times (%d out of %d allowed) (%d out of %d major %dms timeouts tripped)\n", minor_threshold, minor_triggers, minor_maxtriggers, major_threshold, major_triggers, major_maxtriggers); ++ } ++ } ++ ++ if (reset_interval && (major_triggers || minor_triggers) && (last_trigger + reset_interval < clock_tick)) { ++ WARNING("Resetting watchdog timer trigger counts, as it has not been triggered in over %dms.", clock_tick - last_trigger); ++ major_triggers = minor_triggers = 0; ++ } ++ ++ last_fire = clock_tick; ++ setPaused(); ++ return true; ++} ++ ++unsigned int watchdog::wake() { ++ return last_fire + interval; ++} +diff --git a/watchdog.hpp b/watchdog.hpp +new file mode 100644 +index 0000000..4e2aca3 +--- /dev/null ++++ b/watchdog.hpp +@@ -0,0 +1,58 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Author : Richard GAYRAUD - 04 Nov 2003 ++ * Marc LAMBERTON ++ * Olivier JACQUES ++ * Herve PELLAN ++ * David MANSUTTI ++ * Francois-Xavier Kowalski ++ * Gerard Lyonnaz ++ * From Hewlett Packard Company. ++ * F. Tarek Rogers ++ * Peter Higginson ++ * Vincent Luba ++ * Shriram Natarajan ++ * Guillaume Teissier from FTR&D ++ * Clement Chen ++ * Wolfgang Beck ++ * Charles P Wright from IBM Research ++ */ ++ ++#ifndef WATCHDOG_HPP ++#define WATCHDOG_HPP ++ ++#include "task.hpp" ++ ++class watchdog : public task { ++public: ++ unsigned int wake(); ++ watchdog(int interval, int reset, int major_threshold, int major_maxtriggers, int minor_threshold, int minor_maxtriggers); ++ bool run(); ++ void dump(); ++private: ++ int interval; ++ int reset_interval; ++ int minor_threshold; ++ int major_threshold; ++ int minor_maxtriggers; ++ int major_maxtriggers; ++ unsigned long last_fire; ++ unsigned long last_trigger; ++ int major_triggers; ++ int minor_triggers; ++}; ++ ++#endif +diff --git a/xp_parser.c b/xp_parser.c +index 1a2a3a5..ce0ca43 100644 +--- a/xp_parser.c ++++ b/xp_parser.c +@@ -77,7 +77,6 @@ int xp_replace(char *source, char *dest, char *search, char *replace) + * into other elements. */ + char * xp_find_start_tag_end(char *ptr) + { +- char *optr = ptr; + while(*ptr) { + if (*ptr == '<') { + if ((strstr(ptr,"