void SendingMessage::getHexStringParam(char * dest, char * src, int * len) { *len=0; /* Allows any hex coded string like '0x5B07F6' */ while (isxdigit(*src)) { int val = get_decimal_from_hex(*src); src++; if (isxdigit(*src)) { val = (val << 4) + get_decimal_from_hex(*src); src++; } *dest++ = val & 0xff; (*len)++; } }
SendingMessage::SendingMessage(scenario *msg_scenario, char *const_src, bool skip_sanity) { char * src = strdup(const_src); char * osrc = src; char * literal; int literalLen; char * dest; char * key; char current_line[MAX_HEADER_LEN]; char * line_mark = NULL; char * tsrc; int num_cr = get_cr_number(src); this->msg_scenario = msg_scenario; dest = literal = (char *)malloc(strlen(src) + num_cr + 1); literalLen = 0; current_line[0] = '\0'; *dest = 0; while(*src) { if (current_line[0] == '\0') { line_mark = strchr(src, '\n'); if (line_mark) { int header_len = line_mark - src; if (header_len > MAX_HEADER_LEN-1) header_len = MAX_HEADER_LEN-1; memcpy(current_line, src, header_len); current_line[header_len] = '\0'; } } /* This hex encoding could be done in XML parsing, allowing us to skip * these conditionals and branches. */ if ((*src == '\\') && (*(src+1) == 'x')) { /* Allows any hex coded char like '\x5B' ([) */ src += 2; if (isxdigit(*src)) { int val = get_decimal_from_hex(*src); src++; if (isxdigit(*src)) { val = (val << 4) + get_decimal_from_hex(*src); } *dest++ = val & 0xff; } src++; } else if (*src == '\n') { *dest++ = '\r'; *dest++ = *src++; current_line[0] = '\0'; } else if (*src != '[') { *dest++ = *src++; } else { /* We have found a keyword, store the literal that we have been generating. */ 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!"); } 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. */ MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent)); if (!newcomp) { ERROR("Out of memory!"); } char keyword [KEYWORD_SIZE+1]; src++; /* Like strchr, but don't count things in quotes. */ for(tsrc = src; *tsrc; tsrc++) { if (*tsrc == '\"') { do { tsrc++; } while(*tsrc && *tsrc != '\"'); if (!*tsrc) { break; } } if (*tsrc == '[') break; } if (*tsrc != '[') { tsrc = NULL; } /* Like strchr, but don't count things in quotes. */ for(key = src; *key; key++) { if (*key == '\"') { do { key++; } while(*key && *key != '\"'); } if (*key == ']') break; } if (*key != ']') { key = NULL; } if ((tsrc) && (tsrc<key)) { memcpy(keyword, src-1, tsrc - src + 1); src=tsrc+1; dest += sprintf(dest, "%s", keyword); } if((!key) || ((key - src) > KEYWORD_SIZE) || (!(key - src))) { ERROR("Syntax error or invalid [keyword] in scenario while parsing '%s'", current_line); } memcpy(keyword, src, key - src); keyword[key - src] = 0; src = key + 1; // allow +/-n for numeric variables newcomp->offset = 0; if ((strncmp(keyword, "authentication", strlen("authentication")) && strncmp(keyword, "tdmmap", strlen("tdmmap"))) && ((key = strchr(keyword,'+')) || (key = strchr(keyword,'-')))) { if (isdigit(*(key+1))) { newcomp->offset = atoi(key); *key = 0; } } 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 (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; break; } } if (simple_keyword) { messageComponents.push_back(newcomp); continue; } if(!strncmp(keyword, "field", strlen("field"))) { newcomp->type = E_Message_Injection; /* Parse out the interesting things like file and number. */ newcomp->comp_param.field_param.field = atoi(keyword + strlen("field")); char fileName[KEYWORD_SIZE]; getKeywordParam(keyword, "file=", fileName); if (fileName[0] == '\0') { if (!default_file) { ERROR("No injection file was specified!\n"); } newcomp->comp_param.field_param.filename = strdup(default_file); } else { newcomp->comp_param.field_param.filename = strdup(fileName); } if (inFiles.find(newcomp->comp_param.field_param.filename) == inFiles.end()) { ERROR("Invalid injection file: %s\n", fileName); } char line[KEYWORD_SIZE]; getKeywordParam(keyword, "line=", line); if (line[0]) { /* 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) { ERROR("SendingMessage with variable usage outside of scenario!"); } newcomp->varId = msg_scenario->get_var(keyword + 1, "Variable keyword"); } else if(!strncmp(keyword, "fill", strlen("fill"))) { newcomp->type = E_Message_Fill; char filltext[KEYWORD_SIZE]; char varName[KEYWORD_SIZE]; getKeywordParam(keyword, "text=", filltext); if (filltext[0] == '\0') { strcpy(filltext, "X"); } getKeywordParam(keyword, "variable=", varName); newcomp->literal = strdup(filltext); newcomp->literalLen = strlen(newcomp->literal); if (!msg_scenario) { ERROR("SendingMessage with variable usage outside of scenario!"); } newcomp->varId = msg_scenario->get_var(varName, "Fill Variable"); } 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); } #ifndef PCAPPLAY else if(!strcmp(keyword, "auto_media_port") || !strcmp(keyword, "media_port") || !strcmp(keyword, "media_ip") || !strcmp(keyword, "media_ip_type")) { ERROR("The %s keyword requires PCAPPLAY.\n", keyword); } #endif else { // scan for the generic parameters - must be last test int i = 0; while (generic[i]) { char *msg1 = *generic[i]; char *msg2 = *(generic[i] + 1); if(!strcmp(keyword, msg1)) { newcomp->type = E_Message_Literal; newcomp->literal = strdup(msg2); newcomp->literalLen = strlen(newcomp->literal); break; } ++i; } if (!generic[i]) { ERROR("Unsupported keyword '%s' in xml scenario file", keyword); } } messageComponents.push_back(newcomp); } } if (literal[0]) { *dest++ = '\0'; literalLen = dest - literal; literal = (char *)realloc(literal, literalLen); if (!literal) { 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; newcomp->literalLen = literalLen-1; messageComponents.push_back(newcomp); } else { free(literal); } if (skip_sanity) { cancel = response = ack = false; method = NULL; free(osrc); return; } if (numComponents() < 1) { ERROR("Can not create a message that is empty!"); } if (getComponent(0)->type != E_Message_Literal) { ERROR("You can not use a keyword for the METHOD or to generate \"SIP/2.0\" to ensure proper [cseq] operation!\n%s\n", osrc); } char *p = method = strdup(getComponent(0)->literal); char *q; while (isspace(*p)) { p++; } if (!(q = strchr(method, ' '))) { ERROR("You can not use a keyword for the METHOD or to generate \"SIP/2.0\" to ensure proper [cseq] operation!%s\n", osrc); } *q++ = '\0'; while (isspace(*q)) { q++; } if (!strcmp(method, "SIP/2.0")) { char *endptr; code = strtol(q, &endptr, 10); if (*endptr && !isspace(*endptr)) { ERROR("Invalid reply code: %s\n", q); } if (code < 100 || code >= 700) { ERROR("Response codes must be in the range of 100-700"); } response = true; ack = false; cancel = false; free(method); method = NULL; } else { if (p != method) { memmove(method, p, strlen(p) + 1); } method = (char *)realloc(method, strlen(method) + 1); if (!method) { ERROR("Out of memory"); } ack = (!strcmp(method, "ACK")); cancel = (!strcmp(method, "CANCEL")); response = false; }; free(osrc); }
SendingMessage::SendingMessage(scenario *msg_scenario, const char *src, bool skip_sanity, int dialog_number, bool use_txn) : ack(false),cancel(false), response(false) { // should we parse out the _n portion of call here or later? Here would be faster and more // in keepin with the existing style, I suppose... const char *osrc = src; char * literal; int literalLen; char * dest; char * key; char current_line[MAX_HEADER_LEN]; const char * line_mark = NULL; int num_cr = get_cr_number(src); this->msg_scenario = msg_scenario; this->dialog_number = dialog_number; dest = literal = (char *)malloc(strlen(src) + num_cr + 1); literalLen = 0; current_line[0] = '\0'; *dest = 0; while(*src) { /* If start of new line, copy through next \n into current_line */ if (current_line[0] == '\0') { line_mark = strchr(src, '\n'); if (line_mark) { int header_len = line_mark - src; if (header_len > MAX_HEADER_LEN-1) header_len = MAX_HEADER_LEN-1; memcpy(current_line, src, header_len); current_line[header_len] = '\0'; } } /* This hex encoding could be done in XML parsing, allowing us to skip * these conditionals and branches. */ if ((*src == '\\') && (*(src+1) == 'x')) { /* Allows any hex coded char like '\x5B' ([) */ src += 2; if (isxdigit(*src)) { int val = get_decimal_from_hex(*src); src++; if (isxdigit(*src)) { val = (val << 4) + get_decimal_from_hex(*src); } *dest++ = val & 0xff; } src++; } else if (*src == '\n') { *dest++ = '\r'; *dest++ = *src++; current_line[0] = '\0'; } else if (*src != '[') { *dest++ = *src++; } else { /* We have found a keyword, store the literal that we have been generating. */ literalLen = dest - literal; if (literalLen) { *dest = '\0'; literal = (char *)realloc(literal, literalLen + 1); if (!literal) { REPORT_ERROR("Out of memory!"); } MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent)); if (!newcomp) { REPORT_ERROR("Out of memory!"); } newcomp->type = E_Message_Literal; newcomp->literal = literal; newcomp->literalLen = literalLen; // length without the terminator newcomp->dialog_number = dialog_number; 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. */ MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent)); if (!newcomp) { REPORT_ERROR("Out of memory!"); } newcomp->dialog_number = dialog_number; char keyword [KEYWORD_SIZE+1]; src++; /* Like strchr, but don't count things in quotes. */ // cast away const of src [*UGLY*] for(key = (char *) src; *key && *key != '\n'; key++) { if (*key == '\"') { do { key++; } while(*key && *key != '\"' && *key != '\n'); } if (*key == ']') break; } if (*key == '\n') REPORT_ERROR("Cannot have end of line characters in the middle of keywords. Possibly a missing ']' or a duplicate 'CDATA[' section in scenario while parsing '%s'.", current_line); if (*key != ']') { key = NULL; } if((!key) || ((key - src) > KEYWORD_SIZE) || (!(key - src))) { REPORT_ERROR("Syntax error or invalid [keyword] in scenario while parsing '%s'", current_line); } memcpy(keyword, src, key - src); keyword[key - src] = 0; src = key + 1; // allow +/-n for numeric variables newcomp->offset = 0; if ((strncmp(keyword, "authentication", strlen("authentication")) && strncmp(keyword, "tdmmap", strlen("tdmmap")))) { newcomp->offset = parseOffset(keyword); /* end the string before the +/- sign so that the parser doesn't read it as an unrecognized keyword */ if(((key = strchr(keyword,'+')) || (key = strchr(keyword,'-'))) && isdigit(*(key+1))) *key = 0; } 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 (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; break; } } // check ip addresses for optional specifier for ipv6 esc bool ip_keyword = false; if (!simple_keyword) { for (unsigned int i = 0; i< sizeof(IP_Keywords)/sizeof(IP_Keywords[0]); i++) { //exact match or contains full keyword and a space if ( !strcmp(keyword, IP_Keywords[i].keyword) || ( !strncmp(keyword, IP_Keywords[i].keyword, strlen(IP_Keywords[i].keyword) ) && ( strlen(keyword)> strlen(IP_Keywords[i].keyword )) && ( keyword[strlen(IP_Keywords[i].keyword)] == ' ') ) ) { newcomp->type = IP_Keywords[i].type; if ((IP_Keywords[i].type == E_Message_Local_IP2)&&(strlen(local_ip2)==0)) REPORT_ERROR("Scenario uses [local_ip2], requires use of -i2 command line argument"); if( strstr(keyword, "no_square_bracket") ) { // want the no escape version of the ip address switch (IP_Keywords[i].type) { case (E_Message_Remote_IP): newcomp->type = E_Message_Remote_IP_noesc; break; case (E_Message_Local_IP): newcomp->type = E_Message_Local_IP_noesc; break; case (E_Message_Local_IP2): newcomp->type = E_Message_Local_IP2_noesc; break; case (E_Message_Server_IP): newcomp->type = E_Message_Server_IP_noesc; break; case(E_Message_Media_IP): newcomp->type = E_Message_Media_IP_noesc; break; default: REPORT_ERROR("Unknown ip address specifier type %d while building message to send", IP_Keywords[i].keyword); break; } } ip_keyword = true; break; } }//for } bool dialog_keyword = false; for (unsigned int i = 0; i < sizeof(DialogSpecificKeywords)/sizeof(DialogSpecificKeywords[0]); i++) { if (strstr(keyword, DialogSpecificKeywords[i].keyword) == keyword) { newcomp->type = DialogSpecificKeywords[i].type; parse_dialog_number(keyword, newcomp); if ((use_txn) && (i <= HighestTransactionSpecificKeywordsIndex) && (newcomp->dialog_number != dialog_number)) { REPORT_ERROR("Cannot use 'dialog=' attribute in [%s] keyword when also specifying 'use_txn' for the message.\n", keyword); } parse_encoding(keyword, newcomp); dialog_keyword = true; break; } } if (newcomp->type == E_Message_Peer_Tag_Param || newcomp->type == E_Message_Remote_Tag_Param || newcomp->type == E_Message_Remote_Tag ) { parse_generated(keyword, newcomp); } if (simple_keyword || dialog_keyword || ip_keyword) { messageComponents.push_back(newcomp); continue; } /* Now handle all the keywords which are neither simple nor determined solely by dialog */ if(!strncmp(keyword, "field", strlen("field"))) { newcomp->type = E_Message_Injection; /* Parse out the interesting things like file and number. */ newcomp->comp_param.field_param.field = atoi(keyword + strlen("field")); char fileName[KEYWORD_SIZE]; getKeywordParam(keyword, "file=", fileName); if (fileName[0] == '\0') { if (!default_file) { REPORT_ERROR("No injection file was specified!\n"); } newcomp->comp_param.field_param.filename = strdup(default_file); } else { newcomp->comp_param.field_param.filename = strdup(fileName); } if (inFiles.find(newcomp->comp_param.field_param.filename) == inFiles.end()) { REPORT_ERROR("Invalid injection file: %s\n", fileName); } char line[KEYWORD_SIZE]; getKeywordParam(keyword, "line=", line); if (line[0]) { /* Turn this into a new message component. */ newcomp->comp_param.field_param.line = new SendingMessage(msg_scenario, line, true, dialog_number); } } 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') { REPORT_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, dialog_number); } else if(*keyword == '$') { newcomp->type = E_Message_Variable; if (!msg_scenario) { REPORT_ERROR("SendingMessage with variable usage outside of scenario!"); } newcomp->varId = msg_scenario->get_var(keyword + 1, "Variable keyword"); } else if(!strncmp(keyword, "fill", strlen("fill"))) { newcomp->type = E_Message_Fill; char filltext[KEYWORD_SIZE]; char varName[KEYWORD_SIZE]; getKeywordParam(keyword, "text=", filltext); if (filltext[0] == '\0') { strcpy(filltext, "X"); } getKeywordParam(keyword, "variable=", varName); newcomp->literal = strdup(filltext); newcomp->literalLen = strlen(newcomp->literal); if (!msg_scenario) { REPORT_ERROR("SendingMessage with variable usage outside of scenario!"); } newcomp->varId = msg_scenario->get_var(varName, "Fill Variable"); } else if(!strncmp(keyword, "last_", strlen("last_"))) { newcomp->type = E_Message_Last_Header; // parse optional dialog/value only parameter bool is_dialog_number = parse_dialog_number(keyword, newcomp); bool is_value_only = parse_value_only(keyword, newcomp); if (is_dialog_number || is_value_only) { // if dialog= specified, only copy header portion if (use_txn && strstr(keyword, "dialog=")) { REPORT_ERROR("Cannot use 'dialog=' attribute in [last_] keyword when also specifying 'use_txn' for the message.\n"); } const char *diagptr = strstr(keyword, "dialog="); const char *valueptr = strstr(keyword, "value_only="); const char *index; if(diagptr) { // if both pointers exist start from the one closer to the beginning if(valueptr) index = (diagptr < valueptr) ? diagptr : valueptr; else index = diagptr; } else if (valueptr) { index = valueptr; } else REPORT_ERROR("Incorrect formatting of options in \"last_\" header"); //Back up the pointer to the end of the last_* so we can extract * correctly while ((index > keyword) && (*(index-1) == ' ')) index--; newcomp->literal = strndup(keyword + strlen("last_"), index - keyword - strlen("last_")); } else newcomp->literal = strdup(keyword + strlen("last_")); newcomp->literalLen = strlen(newcomp->literal); } else if(!strncmp(keyword, "authentication", strlen("authentication"))) { parseAuthenticationKeyword(msg_scenario, newcomp, keyword); } #ifndef PCAPPLAY else if(!strcmp(keyword, "auto_media_port") || !strcmp(keyword, "media_port") || !strcmp(keyword, "media_ip") || !strcmp(keyword, "media_ip_type")) { REPORT_ERROR("The %s keyword requires PCAPPLAY.\n", keyword); } #endif #ifndef _USE_OPENSSL else if(!strcmp(keyword, "authentication")) { REPORT_ERROR("The %s keyword requires OpenSSL.\n", keyword); } #endif else { // scan for the generic parameters - must be last test int i = 0; while (generic[i]) { char *msg1 = *generic[i]; char *msg2 = *(generic[i] + 1); if(!strcmp(keyword, msg1)) { newcomp->type = E_Message_Literal; newcomp->literal = strdup(msg2); newcomp->literalLen = strlen(newcomp->literal); break; } ++i; } if (!generic[i]) { REPORT_ERROR("Unsupported keyword '%s' in xml scenario file", keyword); } } messageComponents.push_back(newcomp); } } if (literal[0]) { *dest++ = '\0'; literalLen = dest - literal; literal = (char *)realloc(literal, literalLen); if (!literal) { REPORT_ERROR("Out of memory!"); } MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent)); if (!newcomp) { REPORT_ERROR("Out of memory!"); } newcomp->type = E_Message_Literal; newcomp->literal = literal; newcomp->literalLen = literalLen-1; messageComponents.push_back(newcomp); } else { free(literal); } if (skip_sanity) { cancel = response = ack = false; method = NULL; return; } if (numComponents() < 1) { REPORT_ERROR("Can not create a message that is empty!"); } if (getComponent(0)->type != E_Message_Literal) { REPORT_ERROR("You can not use a keyword for the METHOD or to generate \"SIP/2.0\" to ensure proper [cseq] operation!\n%s\n", osrc); } char *p = method = strdup(getComponent(0)->literal); char *q; while (isspace(*p)) { p++; } if (!(q = strchr(method, ' '))) { REPORT_ERROR("You can not use a keyword for the METHOD or to generate \"SIP/2.0\" to ensure proper [cseq] operation!%s\n", osrc); } *q++ = '\0'; while (isspace(*q)) { q++; } if (!strcmp(method, "SIP/2.0")) { char *endptr; code = strtol(q, &endptr, 10); if (*endptr && !isspace(*endptr)) { REPORT_ERROR("Invalid reply code: %s\n", q); } if (code < 100 || code >= 700) { REPORT_ERROR("Response codes must be in the range of 100-700"); } response = true; ack = false; cancel = false; free(method); method = NULL; } else { if (p != method) { memmove(method, p, strlen(p) + 1); } method = (char *)realloc(method, strlen(method) + 1); if (!method) { REPORT_ERROR("Out of memory"); } ack = (!strcmp(method, "ACK")); cancel = (!strcmp(method, "CANCEL")); response = false; } }