/* * $Id: sipsak.c 397 2006-01-28 21:11:50Z calrissian $ * * Copyright (C) 2002-2004 Fhg Fokus * Copyright (C) 2004-2005 Nils Ohlmeier * * This file belongs to sipsak, a free sip testing tool. * * sipsak 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. * * sipsak 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. */ /* sipsak written by nils ohlmeier (ohlmeier@fokus.fraunhofer.de). based up on a modifyed version of shoot. return codes are now: 0 for an received 200, 1 for all other received responses, 2 for local errors, and 3 for remote errors. */ /* changes by jiri@iptel.org; now messages can be really received; status code returned is 2 for some local errors , 0 for success and 1 for remote error -- ICMP/timeout; can be used to test if a server is alive; 1xx messages are now ignored; windows support dropped */ #include "sipsak.h" #ifdef HAVE_UNISTD_H # ifdef HAVE_SYS_TYPES_H # include # endif # include #endif #ifdef TIME_WITH_SYS_TIME # include # include #else # ifdef HAVE_SYS_TIME_H # include # else # include # endif #endif #ifdef HAVE_SYS_WAIT_H # include #endif #ifdef HAVE_GETOPT_H # include #endif #ifdef HAVE_SYSLOG_H # include #endif #include "helper.h" #include "header_f.h" #include "shoot.h" #include "exit_code.h" static void sigchld_handler(int signo) { int chld_status; pid_t chld; while ((chld = waitpid(-1, &chld_status, WNOHANG)) > 0); } void print_version() { printf("%s %s by Nils Ohlmeier\n", PACKAGE_NAME, PACKAGE_VERSION); printf(" Copyright (C) 2002-2004 FhG Fokus\n"); printf(" Copyright (C) 2004-2005 Nils Ohlmeier\n"); printf(" report bugs to %s\n\n", PACKAGE_BUGREPORT); printf( " shoot : sipsak [-f FILE] [-L] -s SIPURI\n" " trace : sipsak -T -s SIPURI\n" " usrloc : sipsak -U [-I|M] [-b NUMBER] [-e NUMBER] [-x NUMBER] [-z NUMBER] -s SIPURI\n" " usrloc : sipsak -I|M [-b NUMBER] [-e NUMBER] -s SIPURI\n" " usrloc : sipsak -U [-C SIPURI] [-x NUMBER] -s SIPURI\n" " message: sipsak -M [-B STRING] [-O STRING] [-c SIPURI] -s SIPURI\n" " flood : sipsak -F [-e NUMBER] -s SIPURI\n" " random : sipsak -R [-t NUMBER] -s SIPURI\n\n" " additional parameter in every mode:\n" ); printf(" [-a PASSWORD] [-d] [-i] [-H HOSTNAME] [-l PORT] [-m NUMBER] [-n] " "[-N]\n" " [-r PORT] [-v] [-V] [-w]\n\n" ); } #ifdef HAVE_GETOPT_LONG void print_long_help() { print_version(); printf( " --help displays this help message\n" " --version prints version string only\n" " --filename=FILE the file which contains the SIP message to send\n" " use - for standard input\n" " --no-crlf de-activate CR (\\r) insertion\n" " --sip-uri=SIPURI the destination server uri in form\n" " sip:[user@]servername[:port]\n" " --traceroute activates the traceroute mode\n" ); printf(" --usrloc-mode activates the usrloc mode\n" " --invite-mode simulates a successful calls with itself\n" " --message-mode sends messages to itself\n" " --contact=SIPURI use the given uri as Contact in REGISTER\n" " --appendix-begin=NUMBER the starting number appendix to the user name (default: 0)\n" " --appendix-end=NUMBER the ending numer of the appendix to the user name\n" " --sleep=NUMBER sleep number ms before sending next request\n" ); printf(" --expires=NUMBER the expires header field value (default: 15)\n" " --remove-bindings=NUMBER activates randomly removing of user bindings\n" " --flood-mode activates the flood mode\n" " --random-mode activates the random modues (dangerous)\n" " --trash-chars=NUMBER the maximum number of trashed character in random mode\n" " (default: request length)\n" ); printf(" --local-port=PORT the local port to use (default: any)\n" " --remote-port=PORT the remote port to use (default: 5060)\n" " --outbound-proxy=HOSTNAME request target (outbound proxy)\n" " --hostname=HOSTNAME overwrites the local hostname in all headers\n" " --max-forwards=NUMBER the value for the max-forwards header field\n" #ifdef OLDSTYLE_FQDN " --numeric use IP instead of FQDN in the Via-Line\n" #else " --numeric use FQDN instead of IP in the Via-Line\n" #endif ); printf(" --processes=NUMBER Divide the workflow among the number of processes\n" " --auth-username=STRING username for authentication\n" ); printf(" --no-via deactivate the insertion of a Via-Line\n" " --password=PASSWORD password for authentication\n" " (if omitted password=username)\n" " --ignore-redirects ignore redirects\n" " --verbose each v produces more verbosity (max. 3)\n" " --extract-ip extract IP from the warning in reply\n" " --replace-string=STRING replacement for a special mark in the message\n" " --replace activates replacement of variables\n" ); printf(" --nagios-code returns exit codes Nagios compliant\n" " --nagios-warn=NUMBER return Nagios warning if retrans > number\n" " --message-body=STRING send a message with string as body\n" " --disposition=STRING Content-Disposition value\n" " --search=REGEXP search for a RegExp in replies and return error\n" " on failfure\n" " --timing=NUMBER number of test runs and print just the timings\n" " --symmetric send and received on the same port\n" " --from=SIPURI use the given uri as From in MESSAGE\n" " --timeout-factor=NUMBER timeout multiplier for INVITE transactions\n" " on non-reliable transports (default: 64)\n" " --timer-t1=NUMBER timeout T1 in ms (default: %i)\n" " --transport=STRING specify transport to be used\n" " --headers=STRING adds additional headers to the request\n" " --local-ip=STRING specify local ip address to be used\n" " --authhash=STRING ha1 hash for authentication instead of password\n" " --sylog=NUMBER log exit message to syslog with given log level\n" , DEFAULT_TIMEOUT ); #ifdef WITH_TLS_TRANSP printf(" --tls-ca-cert=FILE file with the cert of the root CA\n" " --tls-client-cert=FILE file with the cert which sipsak will send\n" " --tls-ignore-cert-failure ignore failures during the TLS handshake\n" ); #endif exit_code(0, __PRETTY_FUNCTION__, NULL); } #endif /* prints out some usage help and exits */ void print_help() { print_version(); printf( " -h displays this help message\n" " -V prints version string only\n" " -f FILE the file which contains the SIP message to send\n" " use - for standard input\n" " -L de-activate CR (\\r) insertion in files\n" " -s SIPURI the destination server uri in form\n" " sip:[user@]servername[:port]\n" " -T activates the traceroute mode\n" " -U activates the usrloc mode\n" " -I simulates a successful calls with itself\n" " -M sends messages to itself\n" ); printf( " -C SIPURI use the given uri as Contact in REGISTER\n" " -b NUMBER the starting number appendix to the user name (default: 0)\n" " -e NUMBER the ending numer of the appendix to the user name\n" " -o NUMBER sleep number ms before sending next request\n" " -x NUMBER the expires header field value (default: 15)\n" " -z NUMBER activates randomly removing of user bindings\n" " -F activates the flood mode\n" ); printf( " -R activates the random modues (dangerous)\n" " -t NUMBER the maximum number of trashed character in random mode\n" " (default: request length)\n" " -l PORT the local port to use (default: any)\n" " -r PORT the remote port to use (default: 5060)\n" " -p HOSTNAME request target (outbound proxy)\n" ); printf( " -H HOSTNAME overwrites the local hostname in all headers\n" " -m NUMBER the value for the max-forwards header field\n" #ifdef OLDSTYLE_FQDN " -n use IP instead of FQDN in the Via-Line\n" #else " -n use FQDN instead of IP in the Via-Line\n" #endif " -i deactivate the insertion of a Via-Line\n" " -a PASSWORD password for authentication\n" " (if omitted password=\"\")\n" " -u STRING Authentication username\n" ); printf( " -d ignore redirects\n" " -v each v produces more verbosity (max. 3)\n" " -w extract IP from the warning in reply\n" " -g STRING replacement for a special mark in the message\n" " -G activates replacement of variables\n" " -N returns exit codes Nagios compliant\n" " -q STRING search for a RegExp in replies and return error\n" " on failure\n"); printf(" -W NUMBER return Nagios warning if retrans > number\n" " -B STRING send a message with string as body\n" " -O STRING Content-Disposition value\n" " -P NUMBER Number of processes to start\n" " -A NUMBER number of test runs and print just timings\n" " -S use same port for receiving and sending\n" " -c SIPURI use the given uri as From in MESSAGE\n" " -D NUMBER timeout multiplier for INVITE transactions\n" " on non-reliable transports (default: 64)\n" " -Z NUMBER timeout T1 in ms (default: %i)\n" " -E STRING specify transport to be used\n" " -j STRING adds additional headers to the request\n" " -J STRING ha1 hash for authentication instead of password\n" " -k STRING specify local ip address to be used\n" " -K NUMBER log exit message to syslog with given log level\n" , DEFAULT_TIMEOUT ); exit_code(0, __PRETTY_FUNCTION__, NULL); } int main(int argc, char *argv[]) { FILE *pf; char buff[BUFSIZE]; int c, i, port; unsigned int tsp; char *scheme, *user, *host, *backup; pid_t pid; struct timespec ts; int upp; #ifdef HAVE_GETOPT_LONG int option_index = 0; static struct option l_opts[] = { {"help", 0, 0, 0}, {"version", 0, 0, 'V'}, {"filename", 1, 0, 'f'}, {"sip-uri", 1, 0, 's'}, {"traceroute-mode", 0, 0, 'T'}, {"usrloc-mode", 0, 0, 'U'}, {"invite-mode", 0, 0, 'I'}, {"message-mode", 0, 0, 'M'}, {"contact", 1, 0, 'C'}, {"appendix-begin", 1, 0, 'b'}, {"appendix-end", 1, 0, 'e'}, {"sleep", 1, 0, 'o'}, {"expires", 1, 0, 'x'}, {"remove-bindings", 1, 0, 'z'}, {"flood-mode", 0, 0, 'F'}, {"random-mode", 0, 0, 'R'}, {"trash-chars", 1, 0, 't'}, {"local-port", 1, 0, 'l'}, {"remote-port", 1, 0, 'r'}, {"outbound-proxy", 1, 0, 'p'}, {"hostname", 1, 0, 'H'}, {"max-fowards", 1, 0, 'm'}, {"numeric", 0, 0, 'n'}, {"no-via", 0, 0, 'i'}, {"password", 1, 0, 'a'}, {"ignore-redirects", 0, 0, 'd'}, {"verbose", 0, 0, 'v'}, {"extract-ip", 0, 0, 'w'}, {"replace-string", 0, 0, 'g'}, {"replace", 0, 0, 'G'}, {"nagios-code", 0, 0, 'N'}, {"nagios-warn", 1, 0, 'W'}, {"search", 1, 0, 'q'}, {"message-body", 1, 0, 'B'}, {"disposition", 1, 0, 'O'}, {"processes", 1, 0, 'P'}, {"auth-username", 1, 0, 'u'}, {"no-crlf", 0, 0, 'L'}, {"timing", 1, 0, 'A'}, {"symmetric", 0, 0, 'S'}, {"from", 1, 0, 'c'}, {"timeout-factor", 1, 0, 'D'}, {"timer-t1", 1, 0, 'Z'}, {"transport", 1, 0, 'E'}, {"headers", 1, 0, 'j'}, {"authhash", 1, 0, 'J'}, {"local-ip", 1, 0, 'k'}, {"syslog", 1, 0, 'K'}, #ifdef WITH_TLS_TRANSP {"tls-ca-cert", 1, 0, 0}, {"tls-client-cert", 1, 0, 0}, {"tls-ignore-cert-failure", 0, 0, 0}, #endif {0, 0, 0, 0} }; #endif /* some initialisation to be safe */ file_b=uri_b=trace=lport=usrloc=flood=verbose=randtrash=trashchar = 0; warning_ext=rand_rem=nonce_count=replace_b=invite=message=sysl = 0; sleep_ms=empty_contact=nagios_warn=timing=outbound_proxy=symmetric = 0; namebeg=nameend=maxforw= -1; #ifdef OLDSTYLE_FQDN numeric = 0; #else numeric = 1; #endif via_ins=redirects=fix_crlf=processes = 1; username=password=replace_str=hostname=contact_uri=mes_body = NULL; con_dis=auth_username=from_uri=headers=authhash=local_ip = NULL; scheme = user = host = backup = req = rep = rec = NULL; re = NULL; address= 0; transport=tsp = 0; rport = port = 0; expires_t = USRLOC_EXP_DEF; timer_t1 = SIP_T1; timer_t2 = 8; timer_final = 64; memset(buff, 0, BUFSIZE); memset(fqdn, 0, FQDN_SIZE); #ifdef WITH_TLS_TRANSP cert_file = ca_file = NULL; ignore_ca_fail = 0; # ifdef USE_GNUTLS tls_session = NULL; xcred = NULL; # else # ifdef USE_OPENSSL ctx = NULL; ssl = NULL; # endif #endif #endif /* WITH_TLS_TRANSP */ if (argc==1) { print_help(); } /* lots of command line switches to handle*/ #ifdef HAVE_GETOPT_LONG while ((c=getopt_long(argc, argv, "a:A:b:B:c:C:dD:e:E:f:Fg:GhH:iIj:J:k:K:l:Lm:MnNo:O:p:P:q:r:Rs:St:Tu:UvVwW:x:z:Z:", l_opts, &option_index)) != EOF){ #else while ((c=getopt(argc, argv, "a:A:b:B:c:C:dD:e:E:f:Fg:GhH:iIj:J:k:K:l:Lm:MnNo:O:p:P:q:r:Rs:St:Tu:UvVwW:x:z:Z:")) != EOF){ #endif switch(c){ #ifdef HAVE_GETOPT_LONG case 0: printf("long option %s", l_opts[option_index].name); if (optarg) { printf(" with arg %s", optarg); } printf("\n"); if (STRNCASECMP("help", l_opts[option_index].name, 4) == 0) { print_long_help(); } #ifdef WITH_TLS_TRANSP else if (STRNCASECMP("tls-ca-cert", l_opts[option_index].name, 11) == 0) { pf = fopen(optarg, "rb"); if (!pf){ fprintf(stderr, "error: unable to open the CA cert file '%s'.\n", optarg); exit_code(2, __PRETTY_FUNCTION__, "failed to open CA cert file"); } fclose(pf); ca_file=optarg; break; } else if (STRNCASECMP("tls-ignore-cert-failure", l_opts[option_index].name, 22) == 0) { ignore_ca_fail = 1; break; } else if (STRNCASECMP("tls-client-cert", l_opts[option_index].name, 14) == 0) { pf = fopen(optarg, "rb"); if (!pf){ fprintf(stderr, "error: unable to open the client cert file '%s'.\n", optarg); exit_code(2, __PRETTY_FUNCTION__, "failed to open client cert file"); } fclose(pf); cert_file=optarg; break; } #endif break; #endif case 'a': if (strlen(optarg) == 1 && STRNCASECMP(optarg, "-", 1) == 0) { password = str_alloc(SIPSAK_MAX_PASSWD_LEN); printf("Please enter the password (max. length %i): ", SIPSAK_MAX_PASSWD_LEN); if (read_stdin(password, SIPSAK_MAX_PASSWD_LEN, 1) == 0) { exit_code(0, __PRETTY_FUNCTION__, NULL); } } else { password=str_alloc(strlen(optarg) + 1); strncpy(password, optarg, strlen(optarg)); } break; case 'A': timing=str_to_int(0, optarg); break; case 'b': namebeg=str_to_int(0, optarg); break; case 'B': mes_body=str_alloc(strlen(optarg) + 1); strncpy(mes_body, optarg, strlen(optarg)); break; case 'c': backup=str_alloc(strlen(optarg)+1); strncpy(backup, optarg, strlen(optarg)); parse_uri(backup, &scheme, &user, &host, &port); if (scheme == NULL) { fprintf(stderr, "error: missing scheme in From URI\n"); exit_code(2, __PRETTY_FUNCTION__, "missing scheme in From URI"); } else if (user == NULL) { fprintf(stderr, "error: missing username in From URI\n"); exit_code(2, __PRETTY_FUNCTION__, "missing username in From URI"); } else if (host == NULL) { fprintf(stderr, "error: missing host in From URI\n"); exit_code(2, __PRETTY_FUNCTION__, "missing host in From URI"); } else { from_uri=str_alloc(strlen(optarg)+1); strncpy(from_uri, optarg, strlen(optarg)); } free(backup); break; case 'C': if ((strlen(optarg) == 5 && STRNCASECMP(optarg, "empty", 5) == 0) || (strlen(optarg) == 4 && STRNCASECMP(optarg, "none", 4) == 0)) { empty_contact = 1; } else if ((strlen(optarg) == 1 && STRNCASECMP(optarg, "*", 1) == 0) || (strlen(optarg) == 4 && STRNCASECMP(optarg, "star", 4) == 0)) { contact_uri=str_alloc(2); memcpy(contact_uri, "*", 1); } else { backup=str_alloc(strlen(optarg)+1); strncpy(backup, optarg, strlen(optarg)); parse_uri(backup, &scheme, &user, &host, &port); if (scheme == NULL) { fprintf(stderr, "error: REGISTER Contact uri doesn't not contain " "sip:, sips:, *, or is not empty\n"); exit_code(2, __PRETTY_FUNCTION__, "unsupported Contact for registration"); } /*else if (user == NULL) { fprintf(stderr, "error: missing username in Contact uri\n"); exit_code(2); }*/ else if (host == NULL) { fprintf(stderr, "error: missing host in Contact uri\n"); exit_code(2, __PRETTY_FUNCTION__, "missing host in Contact"); } else { contact_uri=str_alloc(strlen(optarg)+1); strncpy(contact_uri, optarg, strlen(optarg)); } free(backup); } break; case 'd': redirects=0; break; case 'D': timer_final = str_to_int(0, optarg); if (timer_final <= 0) { fprintf(stderr, "error: option D has to be above 0\n"); exit_code(2, __PRETTY_FUNCTION__, "option D has to be above 0"); } break; case 'e': nameend=str_to_int(0, optarg); break; case 'E': if (strlen(optarg) == 3 && STRNCASECMP(optarg, "udp", 3) == 0) { transport = SIP_UDP_TRANSPORT; } else if (strlen(optarg) == 3 && STRNCASECMP(optarg, "tcp", 3) == 0) { transport = SIP_TCP_TRANSPORT; } #ifdef WITH_TLS_TRANSP else if (strlen(optarg) == 3 && STRNCASECMP(optarg, "tls", 3) == 0) { transport = SIP_TLS_TRANSPORT; } #endif else { fprintf(stderr, "error: unsupported transport '%s', supported values: udp, tcp\n", optarg); exit_code(2, __PRETTY_FUNCTION__, "unsupported transport"); } break; case 'F': flood=1; break; case 'f': if (strlen(optarg) != 1 && STRNCASECMP(optarg, "-", 1) != 0) { /* file is opened in binary mode so that the cr-lf is preserved */ pf = fopen(optarg, "rb"); if (!pf){ fprintf(stderr, "error: unable to open the file '%s'.\n", optarg); exit_code(2, __PRETTY_FUNCTION__, "failed to open file from the f option"); } if (fread(buff, 1, sizeof(buff), pf) >= sizeof(buff)){ fprintf(stderr, "error:the file is too big. try files of less " "than %i bytes.\n", BUFSIZE); fprintf(stderr, " or recompile the program with bigger " "BUFSIZE defined.\n"); exit_code(2, __PRETTY_FUNCTION__, "file to big for buffer"); } fclose(pf); } else if (strlen(optarg) == 1 && STRNCASECMP(optarg, "-", 1) == 0) { if (read_stdin(&buff[0], sizeof(buff), 0) == 0) { exit_code(0, __PRETTY_FUNCTION__, NULL); } } else { fprintf(stderr, "error: unable to handle input file name: %s\n", optarg); exit_code(2, __PRETTY_FUNCTION__, "unsupported input file name"); } file_b=1; break; case 'g': replace_str=optarg; break; case 'G': replace_b=1; break; case 'h': print_help(); break; case 'H': hostname=optarg; break; case 'i': via_ins=0; break; case 'I': invite=1; break; case 'j': headers=optarg; break; case 'J': if (strlen(optarg) < SIPSAK_HASHHEXLEN_MD5) { fprintf(stderr, "error: authhash string is too short\n"); exit_code(2, __PRETTY_FUNCTION__, "authhash string is too short"); } authhash=optarg; break; case 'k': local_ip=optarg; break; case 'K': sysl=str_to_int(0, optarg); if (sysl < LOG_ALERT || sysl > LOG_DEBUG) { fprintf(stderr, "error: syslog value '%s' must be between ALERT (1) and DEBUG (7)\n", optarg); exit_code(2, __PRETTY_FUNCTION__, "unsupported syslog value for option K"); } #ifdef HAVE_SYSLOG_H openlog(PACKAGE_NAME, LOG_CONS|LOG_NOWAIT|LOG_PID, LOG_USER); #endif break; case 'l': lport=str_to_int(0, optarg); break; case 'L': fix_crlf=0; break; case 'm': maxforw=str_to_int(0, optarg); break; case 'M': message=1; break; case 'n': numeric = 0; break; case 'N': exit_mode=EM_NAGIOS; break; case 'o': sleep_ms = 0; if (strlen(optarg) == 4 && STRNCASECMP(optarg, "rand", 4) == 0) { sleep_ms = -2; } else { sleep_ms = str_to_int(0, optarg); } break; case 'O': con_dis=str_alloc(strlen(optarg) + 1); strncpy(con_dis, optarg, strlen(optarg)); break; case 'p': parse_uri(optarg, &scheme, &user, &host, &port); if (host == NULL) { fprintf(stderr, "error: missing in host in outbound proxy\n"); exit_code(2, __PRETTY_FUNCTION__, "missing host in outbound proxy"); } if (is_ip(host)) { address = getaddress(host); if (transport == 0) transport = SIP_UDP_TRANSPORT; } else { if (!port) { address = getsrvadr(host, &rport, &tsp); if (tsp != 0) transport = tsp; } if (!address) { address = getaddress(host); if (address && verbose > 1) printf("using A record: %s\n", host); } if (!address){ fprintf(stderr, "error:unable to determine the outbound proxy " "address\n"); exit_code(2, __PRETTY_FUNCTION__, "failed to resolve the outbound proxy"); } } if (port && !rport) { rport = port; } outbound_proxy=1; #ifdef DEBUG printf("address: %lu, rport: %i\n", address, rport); #endif break; case 'P': processes=str_to_int(0, optarg); break; case 'q': if (re) { /* previously allocated -- free */ regfree(re); } else { /* never tried -- allocate */ re=malloc(sizeof(regex_t)); }; if (!re) { fprintf(stderr, "Error: can't allocate RE\n"); exit_code(2, __PRETTY_FUNCTION__, "failed to allocate memory for regualr expression"); }; if (regcomp(re, optarg, REG_EXTENDED|REG_ICASE|REG_NEWLINE )!=0) { fprintf(stderr, "Error: compiling RE: %s\n", optarg ); exit_code(2, __PRETTY_FUNCTION__, "failed to compile regular expression"); }; break; case 'r': port = 0; port=str_to_int(0, optarg); if (rport) { fprintf(stderr, "warning: you are overwritting the destination port with the r argument\n"); } rport = port; break; case 'R': randtrash=1; break; case 's': parse_uri(optarg, &scheme, &user, &host, &port); if (scheme == NULL) { fprintf(stderr, "error: missing scheme in sip uri\n"); exit_code(2, __PRETTY_FUNCTION__, "missing scheme in target SIP URI"); } if (strlen(optarg) == 4 && STRNCASECMP(optarg,"sips",4) == 0){ fprintf(stderr, "error: sips is not supported yet\n"); exit_code(2, __PRETTY_FUNCTION__, "unsupported scheme SIPS in target URI"); } else if (strlen(optarg) != 3 || STRNCASECMP(optarg,"sip",3) != 0){ fprintf(stderr, "error: scheme of sip uri has to be sip\n"); exit_code(2, __PRETTY_FUNCTION__, "unsupported scheme in target URI"); } if (user != NULL) { username = user; } if (host != NULL) { domainname = host; } else { fprintf(stderr, "error: missing hostname in sip uri\n"); exit_code(2, __PRETTY_FUNCTION__, "missing host name in target URI"); } if (port && !rport) { rport = port; } if (is_ip(domainname) && !address) { address = getaddress(domainname); if (transport == 0) transport = SIP_UDP_TRANSPORT; } else { if (!rport && !address) { address = getsrvadr(domainname, &rport, &tsp); if (tsp != 0 && transport == 0) transport = tsp; } if (!address) { address = getaddress(domainname); if (address && verbose > 1) printf("using A record: %s\n", domainname); } if (!address){ fprintf(stderr, "error:unable to determine the IP address for: %s\n", domainname); exit_code(2, __PRETTY_FUNCTION__, "failed to resolve host from target URI"); } } if (port != 0) { backup = str_alloc(strlen(domainname)+1+6); snprintf(backup, strlen(domainname)+6, "%s:%i", domainname, port); domainname = backup; } uri_b=1; #ifdef DEBUG printf("address: %lu, rport: %i, username: '%s', domain: '%s'\n", address, rport, username, domainname); #endif break; case 'S': fprintf(stderr, "warning: symmetric does not work with a-symmetric servers\n"); symmetric=1; break; case 't': trashchar=str_to_int(0, optarg); break; case 'T': trace=1; break; case 'U': usrloc=1; break; case 'u': auth_username=str_alloc(strlen(optarg) + 1); strncpy(auth_username, optarg, strlen(optarg)); break; case 'v': verbose++; break; case 'V': printf("sipsak %s by Nils Ohlmeier\n Copyright (C) 2002-2004" " FhG Fokus\n Copyright (C) 2004-2005 Nils Ohlmeier\n", SIPSAK_VERSION); printf(" compiled with DEFAULT_TIMEOUT=%i, FQDN_SIZE=%i", DEFAULT_TIMEOUT, FQDN_SIZE); #ifdef RAW_SUPPORT printf(", RAW_SUPPORT"); #endif #ifdef HAVE_GETOPT_LONG printf(", LONG_OPTS"); #endif #ifdef HAVE_GNUTLS printf(", GNUTLS_MD5"); #else # ifdef HAVE_FULL_OPENSSL printf(", OPENSSL_MD5"); # else printf(", INTERNAL_MD5"); # endif #endif #ifdef HAVE_OPENSSL_SHA1 printf(", OPENSSL_SHA1"); #endif #ifdef WITH_TLS_TRANSP # ifdef USE_GNUTLS printf(", TLS_SUPPORT(GNUTLS)"); # else # ifdef USE_OPENSSL printf(", TLS_SUPPORT(OPENSSL)"); # endif # endif #endif #ifdef HAVE_CARES_H printf(", SRV_SUPPORT(ARES)"); #else # ifdef HAVE_RULI_H printf(", SRV_SUPPORT(RULI)"); # endif #endif #ifdef HAVE_STRCASESTR printf(", STR_CASE_INSENSITIVE"); #endif #ifdef HAVE_STRNCASECMP printf(", CMP_CASE_INSENSITIVE"); #endif #ifdef DEBUG printf(", DEBUG"); #endif printf("\n"); exit_code(0, __PRETTY_FUNCTION__, NULL); break; case 'w': warning_ext=1; break; case 'W': nagios_warn = str_to_int(0, optarg); break; case 'x': expires_t=str_to_int(0, optarg); break; case 'z': rand_rem=str_to_int(0, optarg); if (rand_rem < 0 || rand_rem > 100) { fprintf(stderr, "error: z option must between 0 and 100\n"); exit_code(2, __PRETTY_FUNCTION__, "value for option z out of range"); } break; case 'Z': timer_t1 = str_to_int(0, optarg); if (timer_t1 <= 0) { fprintf(stderr, "error: Z option must be above 0\n"); exit_code(2, __PRETTY_FUNCTION__, "value for option Z must be above 0"); } break; default: fprintf(stderr, "error: unknown parameter '%c'\n", c); exit_code(2, __PRETTY_FUNCTION__, "unknown parameter"); break; } } if (rport == 0) { rport = 5060; } if (rport > 65535 || rport <= 0) { fprintf(stderr, "error: invalid remote port: %i\n", rport); exit_code(2, __PRETTY_FUNCTION__, "remote port out of range"); } if (transport == 0) { transport = SIP_UDP_TRANSPORT; } timer_t2 = timer_t2 * timer_t1; timer_final = timer_final * timer_t1; /* replace LF with CRLF if we read from a file */ if ((file_b) && (fix_crlf)) { insert_cr(buff); } if (headers) { backup = str_alloc(strlen(headers) + 30); // FIXME strcpy(backup, headers); headers = backup; replace_string(headers, "\\n", "\r\n"); backup = headers + strlen(headers) - 1; if (*backup != '\n') { strcpy(backup + 1, "\r\n"); } if (file_b) insert_header(buff, headers, 1); } /* lots of conditions to check */ if (trace) { if (usrloc || flood || randtrash) { fprintf(stderr, "error: trace can't be combined with usrloc, random or " "flood\n"); exit_code(2, __PRETTY_FUNCTION__, "trace mode can't be combined with other modes"); } if (!uri_b) { fprintf(stderr, "error: for trace mode a SIPURI is realy needed\n"); exit_code(2, __PRETTY_FUNCTION__, "missing URI for trace mode"); } if (file_b) { fprintf(stderr, "warning: file will be ignored for tracing."); } if (!username) { fprintf(stderr, "error: for trace mode without a file the SIPURI have to " "contain a username\n"); exit_code(2, __PRETTY_FUNCTION__, "missing username in target URI"); } if (!via_ins){ fprintf(stderr, "warning: Via-Line is needed for tracing. Ignoring -i\n"); via_ins=1; } if (!warning_ext) { fprintf(stderr, "warning: IP extract from warning activated to be more " "informational\n"); warning_ext=1; } if (maxforw==-1) maxforw=255; } else if (usrloc || invite || message) { if (trace || flood || randtrash) { fprintf(stderr, "error: usrloc can't be combined with trace, random or " "flood\n"); exit_code(2, __PRETTY_FUNCTION__, "usrloc mode can't be combined with other modes"); } if (!username || !uri_b) { fprintf(stderr, "error: for the USRLOC mode you have to give a SIPURI with " "a username\n at least\n"); exit_code(2, __PRETTY_FUNCTION__, "missing target URI or username in URI"); } if (namebeg>0 && nameend==-1) { fprintf(stderr, "error: if a starting numbers is given also an ending " "number have to be specified\n"); exit_code(2, __PRETTY_FUNCTION__, "missing end number"); } if (invite && message) { fprintf(stderr, "error: invite and message tests are XOR\n"); exit_code(2, __PRETTY_FUNCTION__, "INVITE and MESSAGE tests are XOR"); } if (!usrloc && invite && !lport) { fprintf(stderr, "warning: Do NOT use the usrloc invite mode without " "registering sipsak before.\n See man page for " "details.\n"); exit_code(2, __PRETTY_FUNCTION__, "don't use usrloc INVITE mode without registerting before"); } if (contact_uri!=NULL) { if (invite || message) { fprintf(stderr, "error: Contact uri is not support for invites or " "messages\n"); exit_code(2, __PRETTY_FUNCTION__, "Contact URI not supported for INVITE or MESSAGE mode"); } if (nameend!=-1 || namebeg!=-1) { fprintf(stderr, "warning: ignoring starting or ending number if Contact" " is given\n"); nameend=namebeg=0; } if (rand_rem) { fprintf(stderr, "warning: ignoring -z option when Contact is given\n"); rand_rem=0; } } if (via_ins) { if (verbose > 1) { fprintf(stderr, "warning: Deactivated Via insertion in usrloc mode.\n Please use option -i to surpress this warning.\n"); } via_ins=0; } if (nameend==-1) nameend=0; if (namebeg==-1) namebeg=0; } else if (flood) { if (trace || usrloc || randtrash) { fprintf(stderr, "error: flood can't be combined with trace, random or " "usrloc\n"); exit_code(2, __PRETTY_FUNCTION__, "flood mode can't be combined with other modes"); } if (!uri_b) { fprintf(stderr, "error: we need at least a sip uri for flood\n"); exit_code(2, __PRETTY_FUNCTION__, "missing target URI"); } if (redirects) { fprintf(stderr, "warning: redirects are not expected in flood. " "disableing\n"); redirects=0; } } else if (randtrash) { if (trace || usrloc || flood) { fprintf(stderr, "error: random can't be combined with trace, flood or " "usrloc\n"); exit_code(2, __PRETTY_FUNCTION__, "random mode can't be combined with other modes"); } if (!uri_b) { fprintf(stderr, "error: need at least a sip uri for random\n"); exit_code(2, __PRETTY_FUNCTION__, "missing target URI"); } if (redirects) { fprintf(stderr, "warning: redirects are not expected in random. " "disableing\n"); redirects=0; } if (verbose) { fprintf(stderr, "warning: random characters may destroy your terminal " "output\n"); } } else if (mes_body) { if (!message) { fprintf(stderr, "warning: to send a message mode (-M) is required. activating\n"); message=1; } if (!uri_b) { fprintf(stderr, "error: need at least a sip uri to send a meesage\n"); exit_code(2, __PRETTY_FUNCTION__, "missing target SIP URI"); } if (nameend==-1) nameend=0; if (namebeg==-1) namebeg=0; } else { if (!uri_b) { fprintf(stderr, "error: a spi uri is needed at least\n"); exit_code(2, __PRETTY_FUNCTION__, "missing target SIP URI"); } } switch (transport) { #ifdef WITH_TLS_TRANSP case SIP_TLS_TRANSPORT: transport_str = TRANSPORT_TLS_STR; break; #endif /* WITH_TLS_TRANSP */ case SIP_TCP_TRANSPORT: transport_str = TRANSPORT_TCP_STR; break; case SIP_UDP_TRANSPORT: transport_str = TRANSPORT_UDP_STR; break; default: fprintf(stderr, "unknown transport: %i\n", transport); exit_code(2, __PRETTY_FUNCTION__, "unknown transport"); } #ifdef WITH_TLS_TRANSP if (transport == SIP_TLS_TRANSPORT) { # ifdef USE_GNUTLS gnutls_global_init(); //gnutls_anon_allocate_client_credentials(&anoncred); gnutls_certificate_allocate_credentials(&xcred); if (ca_file != NULL) { // set the trusted CA file gnutls_certificate_set_x509_trust_file(xcred, ca_file, GNUTLS_X509_FMT_PEM); } # else # ifdef USE_OPENSSL SSL_library_init(); SSL_load_error_strings(); # endif # endif } #endif /* WITH_TLS_TRANSP */ /* determine our hostname */ get_fqdn(); /* this is not a cryptographic random number generator, but hey this is only a test-tool => should be satisfying*/ srand(time(0) ^ (getpid() + (getpid() << 15))); if (processes > 1) { if (signal(SIGCHLD , sigchld_handler) == SIG_ERR ) { fprintf(stderr, "error: Could not install SIGCHLD handler\n"); exit_code(2, __PRETTY_FUNCTION__, "failed to install SIGCHLD handler"); } } for(i = 0; i < processes - 1; i++) { if ((pid = fork()) < 0) { fprintf(stderr, "error: Cannot fork\n"); exit_code(2, __PRETTY_FUNCTION__, "failed to fork"); } if (pid == 0){ /* child */ upp = (nameend - namebeg + 1) / processes; namebeg = namebeg + upp * i; nameend = namebeg + upp; shoot(&buff[0], sizeof(buff)); } else { if (lport) { lport++; } } /* Delay execution of children so that the * time of the first transmission gets spread over * the retransmission interval evenly */ ts.tv_sec = 0; ts.tv_nsec = (float)DEFAULT_TIMEOUT / (float)processes * (float)1000 * (float)1000; nanosleep(&ts, 0); } /* here we go...*/ if (processes > 1) { upp = (nameend - namebeg + 1) / processes; namebeg = namebeg + upp * i; nameend = namebeg + upp; } shoot(&buff[0], sizeof(buff)); /* normaly we won't come back here, but to satisfy the compiler */ return 0; }