You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
590 lines
17 KiB
590 lines
17 KiB
/*
|
|
* 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.
|
|
* Shriram Natarajan
|
|
* Peter Higginson
|
|
* Eric Miller
|
|
* Venkatesh
|
|
* Enrico Hartung
|
|
* Nasir Khan
|
|
* Lee Ballard
|
|
* Guillaume Teissier from FTR&D
|
|
* Wolfgang Beck
|
|
* Venkatesh
|
|
* Vlad Troyanker
|
|
* Charles P Wright from IBM Research
|
|
* Amit On from Followap
|
|
* Jan Andres from Freenet
|
|
* Ben Evans from Open Cloud
|
|
* Marc Van Diest from Belgacom
|
|
* Stefan Esser
|
|
* Andy Aicken
|
|
*/
|
|
|
|
#include "sipp.hpp"
|
|
#include "message.hpp"
|
|
|
|
struct KeywordMap {
|
|
char *keyword;
|
|
MessageCompType type;
|
|
};
|
|
|
|
typedef std::map<std::string, customKeyword> kw_map;
|
|
kw_map keyword_map;
|
|
|
|
/* These keywords take no parameters. */
|
|
struct KeywordMap SimpleKeywords[] = {
|
|
{"remote_ip", E_Message_Remote_IP },
|
|
{"remote_host", E_Message_Remote_Host },
|
|
{"remote_port", E_Message_Remote_Port },
|
|
{"transport", E_Message_Transport },
|
|
{"local_ip", E_Message_Local_IP },
|
|
{"local_ip_type", E_Message_Local_IP_Type },
|
|
{"local_port", E_Message_Local_Port },
|
|
{"server_ip", E_Message_Server_IP },
|
|
{"media_ip", E_Message_Media_IP },
|
|
#ifdef PCAPPLAY
|
|
{"auto_media_port", E_Message_Auto_Media_Port },
|
|
#endif
|
|
{"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 },
|
|
{"service", E_Message_Service },
|
|
{"branch", E_Message_Branch },
|
|
{"msg_index", E_Message_Index },
|
|
{"next_url", E_Message_Next_Url },
|
|
{"len", E_Message_Len },
|
|
{"peer_tag_param", E_Message_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 },
|
|
{"routes", E_Message_Routes },
|
|
{"tdmmap", E_Message_TDM_Map },
|
|
{"clock_tick", E_Message_ClockTick },
|
|
{"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 * 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;
|
|
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;
|
|
}
|
|
}
|
|
|
|
SendingMessage::~SendingMessage() {
|
|
for (int i = 0; i < numComponents(); i++) {
|
|
freeMessageComponent(messageComponents[i]);
|
|
}
|
|
free(method);
|
|
}
|
|
|
|
bool SendingMessage::isAck() { return ack; }
|
|
bool SendingMessage::isCancel() { return cancel; }
|
|
bool SendingMessage::isResponse() { return response; }
|
|
char *SendingMessage::getMethod() { return method; }
|
|
int SendingMessage::getCode() { return code; }
|
|
|
|
void SendingMessage::getQuotedParam(char * dest, char * src, int * len)
|
|
{
|
|
*len=0;
|
|
/* Allows any hex coded string like '0x5B07F6' */
|
|
while (char c = *src++) {
|
|
switch(c) {
|
|
case '"':
|
|
*len++;
|
|
*dest = '\0';
|
|
return;
|
|
case '\\':
|
|
c = *src++;
|
|
*len++;
|
|
if (c == 0) {
|
|
*dest = '\0';
|
|
return;
|
|
}
|
|
/* Fall-Through. */
|
|
default:
|
|
*dest++ = c;
|
|
*len++;
|
|
}
|
|
}
|
|
*dest = '\0';
|
|
}
|
|
|
|
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)++;
|
|
}
|
|
}
|
|
|
|
void SendingMessage::getKeywordParam(char * src, char * param, char * output)
|
|
{
|
|
char *key, *tmp;
|
|
int len;
|
|
|
|
len = 0;
|
|
key = NULL;
|
|
if ((tmp = strstr(src, param))) {
|
|
tmp += strlen(param);
|
|
key = tmp;
|
|
if ((*key == '0') && (*(key+1) == 'x')) {
|
|
key += 2;
|
|
getHexStringParam(output, key, &len);
|
|
key += len * 2;
|
|
} else if (*key == '\"') {
|
|
key++;
|
|
getQuotedParam(output, key, &len);
|
|
key += len;
|
|
} else {
|
|
while (*key) {
|
|
if (((key - src) > KEYWORD_SIZE) || (!(key - src))) {
|
|
ERROR("Syntax error parsing '%s' parameter", param);
|
|
} else if (*key == ']' || *key < 33 || *key > 126) {
|
|
break;
|
|
}
|
|
key++;
|
|
}
|
|
strncpy(output, tmp, key-tmp);
|
|
output[key-tmp] = '\0';
|
|
}
|
|
} else {
|
|
output[0] = '\0';
|
|
}
|
|
}
|
|
|
|
void SendingMessage::parseAuthenticationKeyword(scenario *msg_scenario, struct MessageComponent *dst, char *keyword) {
|
|
char my_auth_user[KEYWORD_SIZE + 1];
|
|
char my_auth_pass[KEYWORD_SIZE + 1];
|
|
char my_aka[KEYWORD_SIZE + 1];
|
|
|
|
dst->type = E_Message_Authentication;
|
|
|
|
memset(my_auth_user,0,KEYWORD_SIZE);
|
|
memset(my_auth_pass,0,KEYWORD_SIZE);
|
|
/* Look for optional username and password parameters */
|
|
getKeywordParam(keyword, "username=", my_auth_user);
|
|
getKeywordParam(keyword, "password=", my_auth_pass);
|
|
|
|
if(*my_auth_user == '\0') {
|
|
strcpy(my_auth_user, auth_username ? auth_username : service);
|
|
}
|
|
if(*my_auth_pass == '\0') {
|
|
strcpy(my_auth_pass, auth_password);
|
|
}
|
|
|
|
|
|
dst->comp_param.auth_param.auth_user = new SendingMessage(msg_scenario, my_auth_user, true /* skip sanity */);
|
|
dst->comp_param.auth_param.auth_pass = new SendingMessage(msg_scenario, my_auth_pass, true);
|
|
|
|
/* add aka_OP, aka_AMF, aka_K */
|
|
getKeywordParam(keyword, "aka_K=", my_aka);
|
|
if (my_aka[0]==0){
|
|
memcpy(my_aka,my_auth_pass,16);
|
|
my_aka[16]=0;
|
|
}
|
|
dst->comp_param.auth_param.aka_K = new SendingMessage(msg_scenario, my_aka, true);
|
|
|
|
getKeywordParam(keyword, "aka_OP=", my_aka);
|
|
dst->comp_param.auth_param.aka_OP = new SendingMessage(msg_scenario, my_aka, true);
|
|
getKeywordParam(keyword, "aka_AMF=", my_aka);
|
|
dst->comp_param.auth_param.aka_AMF = new SendingMessage(msg_scenario, my_aka, true);
|
|
}
|
|
|
|
void SendingMessage::freeMessageComponent(struct MessageComponent *comp) {
|
|
free(comp->literal);
|
|
if (comp->type == E_Message_Authentication) {
|
|
if (comp->comp_param.auth_param.auth_user) {
|
|
delete comp->comp_param.auth_param.auth_user;
|
|
}
|
|
if (comp->comp_param.auth_param.auth_pass) {
|
|
delete comp->comp_param.auth_param.auth_pass;
|
|
}
|
|
if (comp->comp_param.auth_param.aka_K) {
|
|
delete comp->comp_param.auth_param.aka_K;
|
|
}
|
|
if (comp->comp_param.auth_param.aka_AMF) {
|
|
delete comp->comp_param.auth_param.aka_AMF;
|
|
}
|
|
if (comp->comp_param.auth_param.aka_OP) {
|
|
delete comp->comp_param.auth_param.aka_OP;
|
|
}
|
|
} else if (comp->type == E_Message_Injection) {
|
|
free(comp->comp_param.field_param.filename);
|
|
}
|
|
free(comp);
|
|
}
|
|
|
|
int SendingMessage::numComponents() {
|
|
return messageComponents.size();
|
|
}
|
|
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;
|
|
}
|