1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; either version 2 of the License, or
5  *  (at your option) any later version.
6  *
7  *  This program is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *  GNU General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
15  *
16  *  Author : Richard GAYRAUD - 04 Nov 2003
17  *           Olivier Jacques
18  *           From Hewlett Packard Company.
19  *           Shriram Natarajan
20  *           Peter Higginson
21  *           Eric Miller
22  *           Venkatesh
23  *           Enrico Hartung
24  *           Nasir Khan
25  *           Lee Ballard
26  *           Guillaume Teissier from FTR&D
27  *           Wolfgang Beck
28  *           Venkatesh
29  *           Vlad Troyanker
30  *           Charles P Wright from IBM Research
31  *           Amit On from Followap
32  *           Jan Andres from Freenet
33  *           Ben Evans from Open Cloud
34  *           Marc Van Diest from Belgacom
35  *           Stefan Esser
36  *           Andy Aicken
37  */
38 
39 #include "sipp.hpp"
40 #include "message.hpp"
41 
42 struct KeywordMap {
43     const char *keyword;
44     MessageCompType type;
45 };
46 
47 typedef std::map<std::string, customKeyword> kw_map;
48 kw_map keyword_map;
49 
50 /* These keywords take no parameters. */
51 struct KeywordMap SimpleKeywords[] = {
52     {"remote_ip", E_Message_Remote_IP },
53     {"remote_host", E_Message_Remote_Host },
54     {"remote_port", E_Message_Remote_Port },
55     {"transport", E_Message_Transport },
56     {"local_ip", E_Message_Local_IP },
57     {"local_ip_type", E_Message_Local_IP_Type },
58     {"local_port", E_Message_Local_Port },
59     {"server_ip", E_Message_Server_IP },
60     {"media_ip", E_Message_Media_IP },
61 #ifdef PCAPPLAY
62     {"auto_media_port", E_Message_Auto_Media_Port },
63 #endif
64 #ifdef RTP_STREAM
65   {"rtpstream_audio_port", E_Message_RTPStream_Audio_Port },
66   {"rtpstream_video_port", E_Message_RTPStream_Video_Port },
67 #endif
68     {"media_port", E_Message_Media_Port },
69     {"media_ip_type", E_Message_Media_IP_Type },
70     {"call_number", E_Message_Call_Number },
71     {"dynamic_id", E_Message_DynamicId }, // wrapping global counter
72     {"call_id", E_Message_Call_ID },
73     {"cseq", E_Message_CSEQ },
74     {"pid", E_Message_PID },
75     {"service", E_Message_Service },
76     {"branch", E_Message_Branch },
77     {"msg_index", E_Message_Index },
78     {"next_url", E_Message_Next_Url },
79     {"len", E_Message_Len },
80     {"peer_tag_param", E_Message_Peer_Tag_Param },
81     {"last_Request_URI", E_Message_Last_Request_URI },
82     {"last_cseq_number", E_Message_Last_CSeq_Number },
83     {"last_message", E_Message_Last_Message },
84     {"routes", E_Message_Routes },
85     {"tdmmap", E_Message_TDM_Map },
86     {"clock_tick", E_Message_ClockTick },
87     {"users", E_Message_Users },
88     {"userid", E_Message_UserID },
89     {"timestamp", E_Message_Timestamp },
90     {"date", E_Message_Date },
91     {"sipp_version", E_Message_SippVersion },
92 };
93 
94 #define KEYWORD_SIZE 256
95 
quoted_strchr(const char * s,int c)96 static char* quoted_strchr(const char* s, int c)
97 {
98     const char* p;
99 
100     for (p = s; *p && *p != c; p++) {
101         if (*p == '"') {
102             p++;
103             p += strcspn(p, "\"");
104         }
105     }
106 
107     return *p == c ? const_cast<char*>(p) : NULL;
108 }
109 
SendingMessage(scenario * msg_scenario,char * const_src,bool skip_sanity)110 SendingMessage::SendingMessage(scenario *msg_scenario, char *const_src, bool skip_sanity)
111 {
112     char * src = strdup(const_src);
113     char * osrc = src;
114     char * literal;
115     int    literalLen;
116     char * dest;
117     char * key;
118     char   current_line[MAX_HEADER_LEN];
119     char * line_mark = NULL;
120     char * tsrc;
121     int    num_cr = get_cr_number(src);
122 
123     this->msg_scenario = msg_scenario;
124 
125     dest = literal = (char *)malloc(strlen(src) + num_cr + 1);
126 
127     current_line[0] = '\0';
128     *dest = 0;
129 
130     while(*src) {
131         if (current_line[0] == '\0') {
132             line_mark = strchr(src, '\n');
133             if (line_mark) {
134                 int header_len =  line_mark - src;
135                 if (header_len > MAX_HEADER_LEN-1)
136                     header_len = MAX_HEADER_LEN-1;
137                 memcpy(current_line, src, header_len);
138                 current_line[header_len] = '\0';
139             }
140         }
141 
142         /* This hex encoding could be done in XML parsing, allowing us to skip
143          * these conditionals and branches. */
144         if ((*src == '\\') && (*(src+1) == 'x')) {
145             /* Allows any hex coded char like '\x5B' ([) */
146             src += 2;
147             if (isxdigit(*src)) {
148                 int val = get_decimal_from_hex(*src);
149                 src++;
150                 if (isxdigit(*src)) {
151                     val = (val << 4) + get_decimal_from_hex(*src);
152                 }
153                 *dest++ = val & 0xff;
154             }
155             src++;
156         } else if (*src == '\n') {
157             *dest++ = '\r';
158             *dest++ = *src++;
159             current_line[0] = '\0';
160         } else if (*src != '[') {
161             *dest++ = *src++;
162         } else {
163             /* We have found a keyword, store the literal that we have been generating. */
164             literalLen = dest - literal;
165             if (literalLen) {
166                 *dest = '\0';
167                 literal = (char *)realloc(literal, literalLen + 1);
168                 if (!literal) {
169                     ERROR("Out of memory!");
170                 }
171 
172                 MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent));
173                 if (!newcomp) {
174                     ERROR("Out of memory!");
175                 }
176 
177                 newcomp->type = E_Message_Literal;
178                 newcomp->literal = literal;
179                 newcomp->literalLen = literalLen; // length without the terminator
180                 messageComponents.push_back(newcomp);
181             } else {
182                 free(literal);
183             }
184 
185             dest = literal = (char *)malloc(strlen(src) + num_cr + 1);
186             *dest = '\0';
187 
188             /* Now lets determine which keyword we have. */
189             MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent));
190             if (!newcomp) {
191                 ERROR("Out of memory!");
192             }
193 
194             char keyword [KEYWORD_SIZE+1];
195             src++;
196 
197             tsrc = quoted_strchr(src, '[');
198             key = quoted_strchr(src, ']');
199 
200             if ((tsrc) && (tsrc<key)) {
201                 memcpy(keyword, src-1,  tsrc - src + 1);
202                 src=tsrc+1;
203                 dest += sprintf(dest, "%s", keyword);
204             }
205 
206             if((!key) || ((key - src) > KEYWORD_SIZE) || (!(key - src))) {
207                 ERROR("Syntax error or invalid [keyword] in scenario while parsing '%s'", current_line);
208             }
209             memcpy(keyword, src,  key - src);
210             keyword[key - src] = 0;
211             src = key + 1;
212             // allow +/-n for numeric variables
213             newcomp->offset = 0;
214             if ((strncmp(keyword, "authentication", strlen("authentication")) &&
215                     strncmp(keyword, "tdmmap", strlen("tdmmap"))) &&
216                     ((key = strchr(keyword,'+')) || (key = strchr(keyword,'-')))) {
217                 if (isdigit(*(key+1))) {
218                     newcomp->offset = atoi(key);
219                     *key = 0;
220                 }
221             }
222 
223             char *spc = NULL;
224             char ospc;
225             if ((spc = strchr(keyword, ' '))) {
226                 ospc = *spc;
227                 *spc = '\0';
228             }
229             kw_map::iterator it = keyword_map.find(keyword);
230             if (spc) {
231                 *spc = ospc;
232             }
233 
234             if (it != keyword_map.end()) {
235                 newcomp->type = E_Message_Custom;
236                 newcomp->comp_param.fxn = it->second;
237                 messageComponents.push_back(newcomp);
238                 continue;
239             }
240 
241             bool simple_keyword = false;
242             for (unsigned int i = 0; i < sizeof(SimpleKeywords)/sizeof(SimpleKeywords[0]); i++) {
243                 if (!strcmp(keyword, SimpleKeywords[i].keyword)) {
244                     newcomp->type = SimpleKeywords[i].type;
245                     simple_keyword = true;
246                     break;
247                 }
248             }
249 
250             if (simple_keyword) {
251                 messageComponents.push_back(newcomp);
252                 continue;
253             }
254 
255             if(!strncmp(keyword, "field", strlen("field"))) {
256                 newcomp->type = E_Message_Injection;
257 
258                 /* Parse out the interesting things like file and number. */
259                 newcomp->comp_param.field_param.field = atoi(keyword + strlen("field"));
260 
261                 char fileName[KEYWORD_SIZE];
262                 getKeywordParam(keyword, "file=", fileName);
263                 if (fileName[0] == '\0') {
264                     if (!default_file) {
265                         ERROR("No injection file was specified!\n");
266                     }
267                     newcomp->comp_param.field_param.filename = strdup(default_file);
268                 } else {
269                     newcomp->comp_param.field_param.filename = strdup(fileName);
270                 }
271                 if (inFiles.find(newcomp->comp_param.field_param.filename) == inFiles.end()) {
272                     ERROR("Invalid injection file: %s\n", fileName);
273                 }
274 
275                 char line[KEYWORD_SIZE];
276                 getKeywordParam(keyword, "line=", line);
277                 if (line[0]) {
278                     /* Turn this into a new message component. */
279                     newcomp->comp_param.field_param.line = new SendingMessage(msg_scenario, line, true);
280                 }
281             } else if(!strncmp(keyword, "file", strlen("file"))) {
282                 newcomp->type = E_Message_File;
283 
284                 /* Parse out the interesting things like file and number. */
285                 char fileName[KEYWORD_SIZE];
286                 getKeywordParam(keyword, "name=", fileName);
287                 if (fileName[0] == '\0') {
288                     ERROR("No name specified for 'file' keyword!\n");
289                 }
290                 /* Turn this into a new message component. */
291                 newcomp->comp_param.filename = new SendingMessage(msg_scenario, fileName, true);
292             } else if(*keyword == '$') {
293                 newcomp->type = E_Message_Variable;
294                 if (!msg_scenario) {
295                     ERROR("SendingMessage with variable usage outside of scenario!");
296                 }
297                 newcomp->varId = msg_scenario->get_var(keyword + 1, "Variable keyword");
298             } else if(!strncmp(keyword, "fill", strlen("fill"))) {
299                 newcomp->type = E_Message_Fill;
300                 char filltext[KEYWORD_SIZE];
301                 char varName[KEYWORD_SIZE];
302 
303                 getKeywordParam(keyword, "text=", filltext);
304                 if (filltext[0] == '\0') {
305                     strcpy(filltext, "X");
306                 }
307                 getKeywordParam(keyword, "variable=", varName);
308 
309                 newcomp->literal = strdup(filltext);
310                 newcomp->literalLen = strlen(newcomp->literal);
311                 if (!msg_scenario) {
312                     ERROR("SendingMessage with variable usage outside of scenario!");
313                 }
314                 newcomp->varId = msg_scenario->get_var(varName, "Fill Variable");
315             } else if(!strncmp(keyword, "last_", strlen("last_"))) {
316                 newcomp->type = E_Message_Last_Header;
317                 newcomp->literal = strdup(keyword + strlen("last_"));
318                 newcomp->literalLen = strlen(newcomp->literal);
319             } else if(!strncmp(keyword, "authentication", strlen("authentication"))) {
320                 parseAuthenticationKeyword(msg_scenario, newcomp, keyword);
321             }
322 #ifndef PCAPPLAY
323             else if(!strcmp(keyword, "auto_media_port")) {
324                 ERROR("The %s keyword requires PCAPPLAY.\n", keyword);
325             }
326 #endif
327             else {
328                 // scan for the generic parameters - must be last test
329 
330                 int i = 0;
331                 while (generic[i]) {
332                     char *msg1 = *generic[i];
333                     char *msg2 = *(generic[i] + 1);
334                     if(!strcmp(keyword, msg1)) {
335                         newcomp->type = E_Message_Literal;
336                         newcomp->literal = strdup(msg2);
337                         newcomp->literalLen = strlen(newcomp->literal);
338                         break;
339                     }
340                     ++i;
341                 }
342                 if (!generic[i]) {
343                     ERROR("Unsupported keyword '%s' in xml scenario file",
344                           keyword);
345                 }
346             }
347 
348             messageComponents.push_back(newcomp);
349         }
350     }
351     if (literal[0]) {
352         *dest++ = '\0';
353         literalLen = dest - literal;
354         literal = (char *)realloc(literal, literalLen);
355         if (!literal) {
356             ERROR("Out of memory!");
357         }
358 
359         MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent));
360         if (!newcomp) {
361             ERROR("Out of memory!");
362         }
363 
364         newcomp->type = E_Message_Literal;
365         newcomp->literal = literal;
366         newcomp->literalLen = literalLen-1;
367         messageComponents.push_back(newcomp);
368     } else {
369         free(literal);
370     }
371 
372     if (skip_sanity) {
373         cancel = response = ack = false;
374         method = NULL;
375         free(osrc);
376         return;
377     }
378 
379     if (numComponents() < 1) {
380         ERROR("Can not create a message that is empty!");
381     }
382     if (getComponent(0)->type != E_Message_Literal) {
383         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);
384     }
385 
386     char *p = method = strdup(getComponent(0)->literal);
387     char *q;
388     while (isspace(*p)) {
389         p++;
390     }
391     if (!(q = strchr(method, ' '))) {
392         ERROR("You can not use a keyword for the METHOD or to generate \"SIP/2.0\" to ensure proper [cseq] operation!%s\n", osrc);
393     }
394     *q++ = '\0';
395     while (isspace(*q)) {
396         q++;
397     }
398     if (!strcmp(method, "SIP/2.0")) {
399         char *endptr;
400         code = strtol(q, &endptr, 10);
401         if (*endptr && !isspace(*endptr)) {
402             ERROR("Invalid reply code: %s\n", q);
403         }
404         if (code < 100 || code >= 700) {
405             ERROR("Response codes must be in the range of 100-700");
406         }
407         response = true;
408         ack = false;
409         cancel = false;
410         free(method);
411         method = NULL;
412     } else {
413         if (p != method) {
414             memmove(method, p, strlen(p) + 1);
415         }
416         method = (char *)realloc(method, strlen(method) + 1);
417         if (!method) {
418             ERROR("Out of memory");
419         }
420         ack = (!strcmp(method, "ACK"));
421         cancel = (!strcmp(method, "CANCEL"));
422         response = false;
423     };
424     free(osrc);
425 }
426 
~SendingMessage()427 SendingMessage::~SendingMessage()
428 {
429     for (int i = 0; i < numComponents(); i++) {
430         freeMessageComponent(messageComponents[i]);
431     }
432     free(method);
433 }
434 
isAck()435 bool SendingMessage::isAck()
436 {
437     return ack;
438 }
isCancel()439 bool SendingMessage::isCancel()
440 {
441     return cancel;
442 }
isResponse()443 bool SendingMessage::isResponse()
444 {
445     return response;
446 }
getMethod()447 char *SendingMessage::getMethod()
448 {
449     return method;
450 }
getCode()451 int SendingMessage::getCode()
452 {
453     return code;
454 }
455 
getQuotedParam(char * dest,char * src,int * len)456 void SendingMessage::getQuotedParam(char * dest, char * src, int * len)
457 {
458     *len=0;
459     /* Allows any hex coded string like '0x5B07F6' */
460     while (char c = *src++) {
461         switch(c) {
462         case '"':
463             (*len)++;
464             *dest = '\0';
465             return;
466         case '\\':
467             c = *src++;
468             (*len)++;
469             if (c == 0) {
470                 *dest = '\0';
471                 return;
472             }
473             /* Fall-Through. */
474         default:
475             *dest++ = c;
476             (*len)++;
477         }
478     }
479     *dest = '\0';
480 }
481 
getHexStringParam(char * dest,char * src,int * len)482 void SendingMessage::getHexStringParam(char * dest, char * src, int * len)
483 {
484     *len=0;
485     /* Allows any hex coded string like '0x5B07F6' */
486     while (isxdigit(*src)) {
487         int val = get_decimal_from_hex(*src);
488         src++;
489         if (isxdigit(*src)) {
490             val = (val << 4) + get_decimal_from_hex(*src);
491             src++;
492         }
493         *dest++ = val & 0xff;
494         (*len)++;
495     }
496 }
497 
getKeywordParam(char * src,const char * param,char * output)498 void SendingMessage::getKeywordParam(char * src, const char * param, char * output)
499 {
500     char *key, *tmp;
501     int len;
502 
503     len = 0;
504     key = NULL;
505     if ((tmp = strstr(src, param))) {
506         tmp += strlen(param);
507         key = tmp;
508         if ((*key == '0') && (*(key+1) == 'x')) {
509             key += 2;
510             getHexStringParam(output, key, &len);
511         } else if (*key == '\"') {
512             key++;
513             getQuotedParam(output, key, &len);
514         } else {
515             while (*key) {
516                 if (((key - src) > KEYWORD_SIZE) || (!(key - src))) {
517                     ERROR("Syntax error parsing '%s' parameter", param);
518                 } else if (*key == ']' || *key < 33 || *key > 126) {
519                     break;
520                 }
521                 key++;
522             }
523             strncpy(output, tmp, key-tmp);
524             output[key-tmp] = '\0';
525         }
526     } else {
527         output[0] = '\0';
528     }
529 }
530 
parseAuthenticationKeyword(scenario * msg_scenario,struct MessageComponent * dst,char * keyword)531 void SendingMessage::parseAuthenticationKeyword(scenario *msg_scenario, struct MessageComponent *dst, char *keyword)
532 {
533     char my_auth_user[KEYWORD_SIZE + 1];
534     char my_auth_pass[KEYWORD_SIZE + 1];
535     char my_aka[KEYWORD_SIZE + 1];
536 
537     dst->type = E_Message_Authentication;
538 
539     memset(my_auth_user,0,KEYWORD_SIZE);
540     memset(my_auth_pass,0,KEYWORD_SIZE);
541     /* Look for optional username and password parameters */
542     getKeywordParam(keyword, "username=", my_auth_user);
543     getKeywordParam(keyword, "password=", my_auth_pass);
544 
545     if(*my_auth_user == '\0') {
546         strncpy(my_auth_user, auth_username ? auth_username : service,
547                 sizeof(my_auth_user) - 1);
548     }
549     if(*my_auth_pass == '\0') {
550         strncpy(my_auth_pass, auth_password, sizeof(my_auth_pass) - 1);
551     }
552 
553 
554     dst->comp_param.auth_param.auth_user = new SendingMessage(msg_scenario, my_auth_user, true /* skip sanity */);
555     dst->comp_param.auth_param.auth_pass = new SendingMessage(msg_scenario, my_auth_pass, true);
556 
557     /* add aka_OP, aka_AMF, aka_K */
558     getKeywordParam(keyword, "aka_K=", my_aka);
559     if (my_aka[0]==0) {
560         memcpy(my_aka,my_auth_pass,16);
561         my_aka[16]=0;
562     }
563     dst->comp_param.auth_param.aka_K = new SendingMessage(msg_scenario, my_aka, true);
564 
565     getKeywordParam(keyword, "aka_OP=", my_aka);
566     dst->comp_param.auth_param.aka_OP = new SendingMessage(msg_scenario, my_aka, true);
567     getKeywordParam(keyword, "aka_AMF=", my_aka);
568     dst->comp_param.auth_param.aka_AMF = new SendingMessage(msg_scenario, my_aka, true);
569 }
570 
freeMessageComponent(struct MessageComponent * comp)571 void SendingMessage::freeMessageComponent(struct MessageComponent *comp)
572 {
573     free(comp->literal);
574     if (comp->type == E_Message_Authentication) {
575         if (comp->comp_param.auth_param.auth_user) {
576             delete comp->comp_param.auth_param.auth_user;
577         }
578         if (comp->comp_param.auth_param.auth_pass) {
579             delete comp->comp_param.auth_param.auth_pass;
580         }
581         if (comp->comp_param.auth_param.aka_K) {
582             delete comp->comp_param.auth_param.aka_K;
583         }
584         if (comp->comp_param.auth_param.aka_AMF) {
585             delete comp->comp_param.auth_param.aka_AMF;
586         }
587         if (comp->comp_param.auth_param.aka_OP) {
588             delete comp->comp_param.auth_param.aka_OP;
589         }
590     } else if (comp->type == E_Message_Injection) {
591         free(comp->comp_param.field_param.filename);
592     }
593     free(comp);
594 }
595 
numComponents()596 int SendingMessage::numComponents()
597 {
598     return messageComponents.size();
599 }
getComponent(int i)600 struct MessageComponent *SendingMessage::getComponent(int i) {
601     return messageComponents[i];
602 }
603 
604 /* This is very simplistic and does not yet allow any arguments, but it is a start. */
registerKeyword(char * keyword,customKeyword fxn)605 int registerKeyword(char *keyword, customKeyword fxn)
606 {
607     if (keyword_map.find(keyword) != keyword_map.end()) {
608         ERROR("Can not register keyword '%s', already registered!\n", keyword);
609     }
610     keyword_map[keyword] = fxn;
611     return 0;
612 }
613