/* * 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 * Michael Dwyer from Cibation */ #include #include #include #include #include #include #ifdef PCAPPLAY #include "send_packets.h" #endif #include "sipp.hpp" #include "assert.h" #ifdef _USE_OPENSSL extern SSL *ssl_list[]; extern struct pollfd pollfiles[]; extern SSL_CTX *sip_trp_ssl_ctx; #endif extern map map_perip_fd; call_map calls; call_list running_calls; timewheel paused_calls; socket_call_map_map socket_to_calls; #ifdef PCAPPLAY /* send_packets pthread wrapper */ void *send_wrapper(void *); #endif /************** Call map and management routines **************/ call_map * get_calls() { return & calls; } static unsigned int next_number = 1; unsigned int get_tdm_map_number(unsigned int number) { unsigned int nb = 0; unsigned int i=0; unsigned int interval=0; unsigned int random=0; bool found = false; /* Find a number in the tdm_map which is not in use */ interval = (tdm_map_a+1) * (tdm_map_b+1) * (tdm_map_c+1); random = rand() % interval; while ((icall_socket = socket; add_call_to_socket(socket, this); } return socket; } struct sipp_socket *call::dissociate_socket() { struct sipp_socket *ret = this->call_socket; remove_call_from_socket(this->call_socket, this); this->call_socket = NULL; return ret; } call * add_call(char * call_id , bool use_ipv6, int userId) { return add_call(call_id, use_ipv6, userId, false /* Is not automatic. */); } call * add_call(char * call_id , bool use_ipv6, int userId, bool isAutomatic) { call * new_call; unsigned int nb; if(!next_number) { next_number ++; } if (use_tdmmap) { nb = get_tdm_map_number(next_number); if (nb != 0) { /* Mark the entry in the list as busy */ tdm_map[nb - 1] = true; } else { /* Can't create the new call */ WARNING("Can't create new outgoing call: all tdm_map circuits busy"); return NULL; } } new_call = new call(call_id, userId, use_ipv6, isAutomatic); if(!new_call) { ERROR("Memory Overflow"); } /* All calls must exist in the map. */ calls[std::string(call_id)] = new_call; /* All calls start off in the running state. */ add_running_call(new_call); new_call -> number = next_number; new_call -> tdm_map_number = nb - 1; /* Vital counters update */ if (!isAutomatic) { next_number++; } else { /* We do not update the call_id counter, for we create here a call */ /* to answer to an out of call message */ } open_calls++; /* Statistics update */ calls_since_last_rate_change++; total_calls ++; if(open_calls > open_calls_peak) { open_calls_peak = open_calls; open_calls_peak_time = clock_tick / 1000; } return new_call; } call * add_call(char * call_id , struct sipp_socket *socket) { call *new_call = add_call(call_id, socket->ss_ipv6, 0 /* No User. */, false /* Not Auto. */); new_call->associate_socket(socket); return new_call; } call * add_call(char * call_id , struct sipp_socket *socket, bool isAutomatic) { call *new_call = add_call(call_id, socket->ss_ipv6, 0 /* No User. */, isAutomatic); new_call->associate_socket(socket); return new_call; } call * add_call(int userId, bool ipv6) { static char call_id[MAX_HEADER_LEN]; char * src = call_id_string; int count = 0; if(!next_number) { next_number ++; } while (*src && count < MAX_HEADER_LEN-1) { if (*src == '%') { ++src; switch(*src++) { case 'u': count += snprintf(&call_id[count], MAX_HEADER_LEN-count-1,"%u", next_number); break; case 'p': count += snprintf(&call_id[count], MAX_HEADER_LEN-count-1,"%u", pid); break; case 's': count += snprintf(&call_id[count], MAX_HEADER_LEN-count-1,"%s", local_ip); break; default: // treat all unknown sequences as %% call_id[count++] = '%'; break; } } else { call_id[count++] = *src++; } } call_id[count] = 0; return add_call(call_id, ipv6, userId); } call * get_call(char * call_id) { call * call_ptr; call_map::iterator call_it ; call_it = calls.find(call_map::key_type(call_id)); call_ptr = (call_it != calls.end()) ? call_it->second : NULL ; return call_ptr; } void delete_call(char * call_id) { call * call_ptr; call_map::iterator call_it ; call_it = calls.find(call_map::key_type(call_id)); call_ptr = (call_it != calls.end()) ? call_it->second : NULL ; if(call_ptr) { if (use_tdmmap) tdm_map[call_ptr->tdm_map_number] = false; calls.erase(call_it); if (call_ptr->running) { remove_running_call(call_ptr); } else { paused_calls.remove_paused_call(call_ptr); } delete call_ptr; open_calls--; } else { if (start_calls == 0) { ERROR("Call not found"); } } } void delete_calls(void) { call * call_ptr; call_map::iterator call_it ; call_it = calls.begin(); while (call_it != calls.end()) { call_ptr = (call_it != calls.end()) ? call_it->second : NULL ; WARNING_P1("Aborting call with Call-Id '%s'", call_ptr->id); call_ptr->abortCall(); call_it = calls.begin(); } } /* Routines for running calls. */ /* Get the overall list of running calls. */ call_list * get_running_calls() { return & running_calls; } /* Put this call in the run queue. */ void add_running_call(call *call) { call->runit = running_calls.insert(running_calls.end(), call); call->running = true; } /* Remove this call from the run queue. */ bool remove_running_call(call *call) { if (!call->running) { return false; } running_calls.erase(call->runit); call->running = false; return true; } /* When should this call wake up? */ unsigned int call_wake(call *call) { unsigned int wake = 0; if (call->paused_until) { wake = call->paused_until; } if (call->next_retrans && (!wake || (call->next_retrans < wake))) { wake = call->next_retrans; } if (call->recv_timeout && (!wake || (call->recv_timeout < wake))) { wake = call->recv_timeout; } return wake; } call_list *timewheel::call2list(call *call) { unsigned int wake = call_wake(call); unsigned int wake_sigbits = wake; unsigned int base_sigbits = wheel_base; if (wake == 0) { return &forever_list; } wake_sigbits /= LEVEL_ONE_SLOTS; base_sigbits /= LEVEL_ONE_SLOTS; if (wake_sigbits == base_sigbits) { return &wheel_one[wake % LEVEL_ONE_SLOTS]; } wake_sigbits /= LEVEL_TWO_SLOTS; base_sigbits /= LEVEL_TWO_SLOTS; if (wake_sigbits == base_sigbits) { return &wheel_two[(wake / LEVEL_ONE_SLOTS) % LEVEL_TWO_SLOTS]; } assert(wake_sigbits < LEVEL_THREE_SLOTS); return &wheel_three[wake_sigbits]; } int expire_paused_calls() { return paused_calls.expire_paused_calls(); } int paused_calls_count() { return paused_calls.size(); } void remove_paused_call(call *call) { assert(!call->running); paused_calls.remove_paused_call(call); } /* Iterate through our sorted set of paused calls, removing those that * should no longer be paused, and adding them to the run queue. */ int timewheel::expire_paused_calls() { int found = 0; while (wheel_base < clock_tick) { int slot1 = wheel_base % LEVEL_ONE_SLOTS; /* Migrate calls from slot2 when we hit 0. */ if (slot1 == 0) { int slot2 = (wheel_base / LEVEL_ONE_SLOTS) % LEVEL_TWO_SLOTS; /* If slot2 is also zero, we must migrate calls from slot3 into slot2. */ if (slot2 == 0) { int slot3 = ((wheel_base / LEVEL_ONE_SLOTS) / LEVEL_TWO_SLOTS); assert(slot3 < LEVEL_THREE_SLOTS); for (call_list::iterator l3it = wheel_three[slot3].begin(); l3it != wheel_three[slot3].end(); l3it++) { /* Migrate this call to wheel two. */ add_paused_call(*l3it, false); } wheel_three[slot3].clear(); } for (call_list::iterator l2it = wheel_two[slot2].begin(); l2it != wheel_two[slot2].end(); l2it++) { /* Migrate this call to wheel one. */ add_paused_call(*l2it, false); } wheel_two[slot2].clear(); } found += wheel_one[slot1].size(); for(call_list::iterator it = wheel_one[slot1].begin(); it != wheel_one[slot1].end(); it++) { add_running_call(*it); count--; } wheel_one[slot1].clear(); wheel_base++; } return found; } void timewheel::add_paused_call(call *call, bool increment) { call_list *list = call2list(call); call->pauseit = list->insert(list->end(), call); if (increment) { count++; } } void timewheel::remove_paused_call(call *call) { call_list *list = call2list(call); list->erase(call->pauseit); count--; } timewheel::timewheel() { count = 0; wheel_base = clock_tick; } int timewheel::size() { return count; } /* The caller must delete this list. */ call_list *get_calls_for_socket(struct sipp_socket *socket) { call_list *l = new call_list; socket_call_map_map::iterator map_it = socket_to_calls.find(socket); /* No map defined for this socket. */ if (map_it == socket_to_calls.end()) { return l; } call_map *socket_call_map = (call_map *) map_it->second; call_map::iterator call_it; for (call_it = socket_call_map->begin(); call_it != socket_call_map->end(); call_it++) { l->insert(l->end(), call_it->second); } return l; } void add_call_to_socket(struct sipp_socket *socket, call *call) { socket_call_map_map::iterator map_it = socket_to_calls.find(socket); /* No map defined for this socket. */ if (map_it == socket_to_calls.end()) { socket_to_calls.insert(socket_map_pair(socket, new call_map)); map_it = socket_to_calls.find(socket); assert(map_it != socket_to_calls.end()); } call_map *socket_call_map = (call_map *) map_it->second; socket_call_map->insert(string_call_pair(call->id, call)); } void remove_call_from_socket(struct sipp_socket *socket, call *call) { socket_call_map_map::iterator map_it = socket_to_calls.find(socket); /* We must have a map for this socket. */ assert(map_it != socket_to_calls.end()); call_map *socket_call_map = (call_map *) map_it->second; call_map::iterator call_it = socket_call_map->find(call->id); /* And our call must exist in the map. */ assert(call_it != socket_call_map->end()); socket_call_map->erase(call_it); /* If we have no more calls, we can delete this entry. */ if (socket_call_map->begin() == socket_call_map->end()) { delete socket_call_map; socket_to_calls.erase(map_it); } } #ifdef PCAPPLAY /******* Media information management *************************/ /* * Look for "c=IN IP4 " pattern in the message and extract the following value * which should be IP address */ uint32_t get_remote_ip_media(char *msg) { char pattern[] = "c=IN IP4 "; char *begin, *end; char ip[32]; begin = strstr(msg, pattern); if (!begin) { /* Can't find what we're looking at -> return no address */ return INADDR_NONE; } begin += sizeof("c=IN IP4 ") - 1; end = strstr(begin, "\r\n"); if (!end) return INADDR_NONE; memset(ip, 0, 32); strncpy(ip, begin, end - begin); return inet_addr(ip); } /* * Look for "c=IN IP6 " pattern in the message and extract the following value * which should be IPv6 address */ uint8_t get_remote_ipv6_media(char *msg, struct in6_addr addr) { char pattern[] = "c=IN IP6 "; char *begin, *end; char ip[128]; memset(&addr, 0, sizeof(addr)); memset(ip, 0, 128); begin = strstr(msg, pattern); if (!begin) { /* Can't find what we're looking at -> return no address */ return 0; } begin += sizeof("c=IN IP6 ") - 1; end = strstr(begin, "\r\n"); if (!end) return 0; strncpy(ip, begin, end - begin); if (!inet_pton(AF_INET6, ip, &addr)) { return 0; } return 1; } /* * Look for "m=audio " pattern in the message and extract the following value * which should be port number */ uint16_t get_remote_audio_port_media(char *msg) { char pattern[] = "m=audio "; char *begin, *end; char number[6]; begin = strstr(msg, pattern); if (!begin) { /* m=audio not found */ return 0; } begin += sizeof("m=audio ") - 1; end = strstr(begin, "\r\n"); if (!end) ERROR("get_remote_audio_port_media: no CRLF found"); memset(number, 0, sizeof(number)); strncpy(number, begin, sizeof(number) - 1); return atoi(number); } /* * Look for "m=video " pattern in the message and extract the following value * which should be port number */ uint16_t get_remote_video_port_media(char *msg) { char pattern[] = "m=video "; char *begin, *end; char number[5]; begin = strstr(msg, pattern); if (!begin) { /* m=video not found */ return 0; } begin += sizeof("m=video ") - 1; end = strstr(begin, "\r\n"); if (!end) ERROR("get_remote_video_port_media: no CRLF found"); memset(number, 0, 5); strncpy(number, begin, end - begin); return atoi(number); } /* * IPv{4,6} compliant */ void call::get_remote_media_addr(char *msg) { uint16_t video_port, audio_port; if (media_ip_is_ipv6) { struct in6_addr ip_media; if (get_remote_ipv6_media(msg, ip_media)) { audio_port = get_remote_audio_port_media(msg); if (audio_port) { /* We have audio in the SDP: set the to_audio addr */ (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_flowinfo = 0; (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_scope_id = 0; (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_family = AF_INET6; (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_port = audio_port; (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_addr = ip_media; } video_port = get_remote_video_port_media(msg); if (video_port) { /* We have video in the SDP: set the to_video addr */ (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_flowinfo = 0; (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_scope_id = 0; (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_family = AF_INET6; (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_port = video_port; (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_addr = ip_media; } hasMediaInformation = 1; } } else { uint32_t ip_media; ip_media = get_remote_ip_media(msg); if (ip_media != INADDR_NONE) { audio_port = get_remote_audio_port_media(msg); if (audio_port) { /* We have audio in the SDP: set the to_audio addr */ (_RCAST(struct sockaddr_in *, &(play_args_a.to)))->sin_family = AF_INET; (_RCAST(struct sockaddr_in *, &(play_args_a.to)))->sin_port = audio_port; (_RCAST(struct sockaddr_in *, &(play_args_a.to)))->sin_addr.s_addr = ip_media; } video_port = get_remote_video_port_media(msg); if (video_port) { /* We have video in the SDP: set the to_video addr */ (_RCAST(struct sockaddr_in *, &(play_args_v.to)))->sin_family = AF_INET; (_RCAST(struct sockaddr_in *, &(play_args_v.to)))->sin_port = video_port; (_RCAST(struct sockaddr_in *, &(play_args_v.to)))->sin_addr.s_addr = ip_media; } hasMediaInformation = 1; } } } #endif /******* Very simple hash for retransmission detection *******/ unsigned long hash(char * msg) { unsigned long hash = 0; int c; while (c = *msg++) hash = c + (hash << 6) + (hash << 16) - hash; return hash; } /******************* Call class implementation ****************/ call::call(char * p_id, int userId, bool ipv6, bool isAutomatic) : use_ipv6(ipv6) { memset(this, 0, sizeof(call)); id = strdup(p_id); start_time = clock_tick; call_established=false ; count_in_stats=true ; ack_is_pending=false ; last_recv_msg = NULL; cseq = base_cseq; nb_last_delay = 0; tdm_map_number = 0; #ifdef _USE_OPENSSL m_ctx_ssl = NULL ; m_bio = NULL ; #endif call_remote_socket = 0; // initialising the CallVariable with the Scenario variable int i; if (maxVariableUsed >= 0) { M_callVariableTable = new CCallVariable *[maxVariableUsed + 1]; } for(i=0; i<=maxVariableUsed; i++) { if (variableUsed[i]) { M_callVariableTable[i] = new CCallVariable(); if (M_callVariableTable[i] == NULL) { ERROR ("call variable allocation failed"); } } else { M_callVariableTable[i] = NULL; } } // If not updated by a message we use the start time // information to compute rtd information for (i = 0; i < MAX_RTD_INFO_LENGTH; i++) { start_time_rtd[i] = getmicroseconds(); rtd_done[i] = false; } // by default, last action result is NO_ERROR last_action_result = call::E_AR_NO_ERROR; this->userId = userId; /* For automatic answer calls to an out of call request, we must not */ /* increment the input files line numbers to not disturb */ /* the input files read mechanism (otherwise some lines risk */ /* to be systematically skipped */ if (!isAutomatic) { m_lineNumber = new file_line_map(); for (file_map::iterator file_it = inFiles.begin(); file_it != inFiles.end(); file_it++) { (*m_lineNumber)[file_it->first] = file_it->second->nextLine(userId); } } #ifdef PCAPPLAY memset(&(play_args_a.to), 0, sizeof(struct sockaddr_storage)); memset(&(play_args_v.to), 0, sizeof(struct sockaddr_storage)); memset(&(play_args_a.from), 0, sizeof(struct sockaddr_storage)); memset(&(play_args_v.from), 0, sizeof(struct sockaddr_storage)); hasMediaInformation = 0; media_thread = 0; #endif peer_tag = NULL; recv_timeout = 0; send_timeout = 0; } call::~call() { deleted += 1; if(comp_state) { comp_free(&comp_state); } if(count_in_stats) { CStat::instance()->computeStat(CStat::E_ADD_CALL_DURATION, clock_tick - start_time); } sipp_close_socket(dissociate_socket()); if (call_remote_socket) { sipp_close_socket(call_remote_socket); } /* Deletion of the call variable */ for(int i=0; i<=maxVariableUsed; i++) { if(M_callVariableTable[i] != NULL) { delete M_callVariableTable[i] ; M_callVariableTable[i] = NULL; } } if(M_callVariableTable) { delete M_callVariableTable; } delete m_lineNumber; if (userId) { freeUsers.push_front(userId); } if(id) { free(id); } if(last_recv_msg) { free(last_recv_msg); } if(last_send_msg) { free(last_send_msg); } if(peer_tag) { free(peer_tag); } if(dialog_route_set) { free(dialog_route_set); } if(next_req_url) { free(next_req_url); } #ifdef _USE_OPENSSL if(dialog_authentication) { free(dialog_authentication); } #endif call_established= false ; } void call::connect_socket_if_needed() { bool existing; if(call_socket) return; if(!multisocket) return; if(transport == T_UDP) { struct sockaddr_storage saddr; if(toolMode != MODE_CLIENT) return; char peripaddr[256]; if (!peripsocket) { if ((associate_socket(new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) { ERROR_NO("Unable to get a UDP socket"); } } else { char *tmp = peripaddr; getFieldFromInputFile(ip_file, peripfield, tmp); map::iterator i; i = map_perip_fd.find(peripaddr); if (i == map_perip_fd.end()) { // Socket does not exist if ((associate_socket(new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) { ERROR_NO("Unable to get a UDP socket"); } else { /* Ensure that it stays persistent, because it is recorded in the map. */ call_socket->ss_count++; map_perip_fd[peripaddr] = call_socket; } } else { // Socket exists already associate_socket(i->second); existing = true; i->second->ss_count++; } } if (existing) { return; } memset(&saddr, 0, sizeof(struct sockaddr_storage)); memcpy(&saddr, local_addr_storage->ai_addr, SOCK_ADDR_SIZE( _RCAST(struct sockaddr_storage *,local_addr_storage->ai_addr))); if (use_ipv6) { saddr.ss_family = AF_INET6; } else { saddr.ss_family = AF_INET; } if (peripsocket) { struct addrinfo * h ; struct addrinfo hints; memset((char*)&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; hints.ai_family = PF_UNSPEC; getaddrinfo(peripaddr, NULL, &hints, &h); memcpy(&saddr, h->ai_addr, SOCK_ADDR_SIZE( _RCAST(struct sockaddr_storage *,h->ai_addr))); if (use_ipv6) { (_RCAST(struct sockaddr_in6 *, &saddr))->sin6_port = htons(local_port); } else { (_RCAST(struct sockaddr_in *, &saddr))->sin_port = htons(local_port); } } if (sipp_bind_socket(call_socket, &saddr, &call_port)) { ERROR_NO("Unable to bind UDP socket"); } } else { /* TCP or TLS. */ struct sockaddr_storage *L_dest = &remote_sockaddr; if ((associate_socket(new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) { ERROR_NO("Unable to get a TCP socket"); } if (existing) { return; } sipp_customize_socket(call_socket); if (use_remote_sending_addr) { L_dest = &remote_sending_sockaddr; } if (sipp_connect_socket(call_socket, L_dest)) { if (reset_number > 0) { if(errno == EINVAL){ /* This occurs sometime on HPUX but is not a true INVAL */ WARNING("Unable to connect a TCP socket, remote peer error"); } else { WARNING("Unable to connect a TCP socket"); } start_calls = 1; } else { if(errno == EINVAL){ /* This occurs sometime on HPUX but is not a true INVAL */ ERROR("Unable to connect a TCP socket, remote peer error"); } else { ERROR_NO("Unable to connect a TCP socket"); } } } } } bool lost(int index) { static int inited = 0; double percent = global_lost; if(!lose_packets) return false; if (scenario[index]->lost >= 0) { percent = scenario[index]->lost; } if (percent == 0) { return false; } if(!inited) { srand((unsigned int) time(NULL)); inited = 1; } return (((double)rand() / (double)RAND_MAX) < (percent / 100.0)); } int call::send_raw(char * msg, int index) { struct sipp_socket *sock; int rc; if (useShortMessagef == 1) { struct timeval currentTime; GET_TIME (¤tTime); char* cs=get_header_content(msg,"CSeq:"); TRACE_SHORTMSG((s, "%s\tS\t%s\tCSeq:%s\t%s\n", CStat::instance()->formatTime(¤tTime),id, cs, get_first_line(msg))); } if((index!=-1) && (lost(index))) { TRACE_MSG((s, "%s message voluntary lost (while sending).", TRANSPORT_TO_STRING(transport))); if(comp_state) { comp_free(&comp_state); } scenario[index] -> nb_lost++; return 0; } sock = call_socket; if ((use_remote_sending_addr) && (toolMode == MODE_SERVER)) { if (!call_remote_socket) { struct sockaddr_storage *L_dest = &remote_sending_sockaddr; if((call_remote_socket= new_sipp_socket(use_ipv6, transport)) == NULL) { ERROR_NO("Unable to get a socket for rsa option"); } sipp_customize_socket(call_remote_socket); if(transport != T_UDP) { if (sipp_connect_socket(call_remote_socket, L_dest)) { if(errno == EINVAL){ /* This occurs sometime on HPUX but is not a true INVAL */ ERROR_P1("Unable to connect a %s socket for rsa option, remote peer error", TRANSPORT_TO_STRING(transport)); } else { ERROR_NO("Unable to connect a socket for rsa option"); } } } } sock=call_remote_socket ; } rc = write_socket(sock, msg, strlen(msg), WS_BUFFER); if(rc == -1 && errno == EWOULDBLOCK) { return -1; } if(rc < 0) { CStat::instance()->computeStat(CStat::E_CALL_FAILED); CStat::instance()->computeStat(CStat::E_FAILED_CANNOT_SEND_MSG); delete_call(id); } return rc; /* OK */ } /* This method is used to send messages that are not */ /* part of the XML scenario */ void call::sendBuffer(char * msg) { /* call send_raw but with a special scenario index */ if (send_raw(msg, -1) < 0) { ERROR_NO("Error sending raw message"); } } char * call::compute_cseq(char * src) { static char cseq[MAX_HEADER_LEN]; /* If we find a CSeq in incoming msg */ char * last_header = get_last_header("CSeq:"); if(last_header) { int i; /* Extract the integer value of the last CSeq */ last_header = strstr(last_header, ":"); last_header++; while(isspace(*last_header)) last_header++; sscanf(last_header,"%d", &i); /* Add 1 to the last CSeq value */ sprintf(cseq, "%s%d", "CSeq: ", (i+1)); } else { sprintf(cseq, "%s", "CSeq: 2"); } return cseq; } char * call::get_header_field_code(char *msg, char * name) { static char code[MAX_HEADER_LEN]; char * last_header; int i; last_header = NULL; i = 0; /* If we find the field in msg */ last_header = get_header_content(msg, name); if(last_header) { /* Extract the integer value of the field */ while(isspace(*last_header)) last_header++; sscanf(last_header,"%d", &i); sprintf(code, "%s %d", name, i); } return code; } char * call::get_last_header(char * name) { int len; if((!last_recv_msg) || (!strlen(last_recv_msg))) { return NULL; } len = strlen(name); /* Ideally this check should be moved to the XML parser so that it is not * along a critical path. We could also handle lowercasing there. */ if (len > MAX_HEADER_LEN) { ERROR_P2("call::get_last_header: Header to parse bigger than %d (%zu)", MAX_HEADER_LEN, strlen(name)); } if (name[len - 1] == ':') { return get_header(last_recv_msg, name, false); } else { char with_colon[MAX_HEADER_LEN]; sprintf(with_colon, "%s:", name); return get_header(last_recv_msg, with_colon, false); } } char * call::get_header_content(char* message, char * name) { return get_header(message, name, true); } /* If content is true, we only return the header's contents. */ char * call::get_header(char* message, char * name, bool content) { /* non reentrant. consider accepting char buffer as param */ static char last_header[MAX_HEADER_LEN * 10]; char * src, *dest, *start, *ptr; /* Are we searching for a short form header? */ bool short_form = false; bool first_time = true; char src_tmp[MAX_HEADER_LEN + 1]; /* returns empty string in case of error */ last_header[0] = '\0'; if((!message) || (!strlen(message))) { return last_header; } /* for safety's sake */ if (NULL == name || NULL == strrchr(name, ':')) { WARNING_P1("Can not searching for header (no colon): %s", name ? name : "(null)"); return last_header; } do { snprintf(src_tmp, MAX_HEADER_LEN, "\n%s", name); src = message; dest = last_header; while(src = strcasestr2(src, src_tmp)) { if (content || !first_time) { /* just want the header's content */ src += strlen(name) + 1; } else { src++; } first_time = false; ptr = strchr(src, '\n'); /* Multiline headers always begin with a tab or a space * on the subsequent lines */ while((ptr) && ((*(ptr+1) == ' ' ) || (*(ptr+1) == '\t') )) { ptr = strchr(ptr + 1, '\n'); } if(ptr) { *ptr = 0; } // Add "," when several headers are present if (dest != last_header) { /* Remove trailing whitespaces, tabs, and CRs */ while ((dest > last_header) && ((*(dest-1) == ' ') || (*(dest-1) == '\r') || (*(dest-1) == '\n') || (*(dest-1) == '\t'))) { *(--dest) = 0; } dest += sprintf(dest, ","); } dest += sprintf(dest, "%s", src); if(ptr) { *ptr = '\n'; } src++; } /* We found the header. */ if(dest != last_header) { break; } /* We didn't find the header, even in its short form. */ if (short_form) { return last_header; } /* We should retry with the short form. */ short_form = true; if (!strcasecmp(name, "call-id:")) { name = "i:"; } else if (!strcasecmp(name, "contact:")) { name = "m:"; } else if (!strcasecmp(name, "content-encoding:")) { name = "e:"; } else if (!strcasecmp(name, "content-length:")) { name = "l:"; } else if (!strcasecmp(name, "content-type:")) { name = "c:"; } else if (!strcasecmp(name, "from:")) { name = "f:"; } else if (!strcasecmp(name, "to:")) { name = "t:"; } else if (!strcasecmp(name, "via:")) { name = "v:"; } else { /* There is no short form to try. */ return last_header; } } while (1); *(dest--) = 0; /* Remove trailing whitespaces, tabs, and CRs */ while ((dest > last_header) && ((*dest == ' ') || (*dest == '\r')|| (*dest == '\t'))) { *(dest--) = 0; } /* Remove leading whitespaces */ for (start = last_header; *start == ' '; start++); /* remove enclosed CRs in multilines */ /* don't remove enclosed CRs for multiple headers (e.g. Via) (Rhys) */ while((ptr = strstr(last_header, "\r\n")) != NULL && ( *(ptr + 2) == ' ' || *(ptr + 2) == '\r' || *(ptr + 2) == '\t') ) { /* Use strlen(ptr) to include trailing zero */ memmove(ptr, ptr+1, strlen(ptr)); } /* Remove illegal double CR characters */ while((ptr = strstr(last_header, "\r\r")) != NULL) { memmove(ptr, ptr+1, strlen(ptr)); } /* Remove illegal double Newline characters */ while((ptr = strstr(last_header, "\n\n")) != NULL) { memmove(ptr, ptr+1, strlen(ptr)); } return start; } char * call::get_first_line(char * message) { /* non reentrant. consider accepting char buffer as param */ static char last_header[MAX_HEADER_LEN * 10]; char * src, *dest; /* returns empty string in case of error */ memset(last_header, 0, sizeof(last_header)); if((!message) || (!strlen(message))) { return last_header; } src = message; dest = last_header; int i=0; while (*src){ if((*src=='\n')||(*src=='\r')){ break; } else { last_header[i]=*src; } i++; src++; } return last_header; } /* Return the last request URI from the To header. On any error returns the * empty string. The caller must free the result. */ char * call::get_last_request_uri () { char * tmp; char * tmp2; char * last_request_uri; int tmp_len; char * last_To = get_last_header("To:"); if (!last_To) { return strdup(""); } tmp = strchr(last_To, '<'); if (!tmp) { return strdup(""); } tmp++; tmp2 = strchr(last_To, '>'); if (!tmp2) { return strdup(""); } tmp_len = strlen(tmp) - strlen(tmp2); if (tmp_len < 0) { return strdup(""); } if(!(last_request_uri = (char *) malloc(tmp_len+1))) ERROR("Cannot allocate !\n"); memset(last_request_uri, 0, sizeof(last_request_uri)); if(tmp && (tmp_len > 0)){ strncpy(last_request_uri, tmp, tmp_len); } last_request_uri[tmp_len] = '\0'; return last_request_uri; } char * call::send_scene(int index, int *send_status) { static char msg_buffer[SIPP_MAX_MSG_SIZE]; #define MAX_MSG_NAME_SIZE 30 static char msg_name[MAX_MSG_NAME_SIZE]; char *L_ptr1 ; char *L_ptr2 ; /* Socket port must be known before string substitution */ connect_socket_if_needed(); assert(call_socket); if (call_socket->ss_congested) { *send_status = -1; return NULL; } if(scenario[index] -> send_scheme) { char * dest; dest = createSendingMessage(scenario[index] -> send_scheme, index); strcpy(msg_buffer, dest); if (dest) { L_ptr1=msg_name ; L_ptr2=msg_buffer ; while ((*L_ptr2 != ' ') && (*L_ptr2 != '\n') && (*L_ptr2 != '\t')) { *L_ptr1 = *L_ptr2; L_ptr1 ++; L_ptr2 ++; } *L_ptr1 = '\0' ; } if (strcmp(msg_name,"ACK") == 0) { call_established = true ; ack_is_pending = false ; } if(send_status) { *send_status = send_raw(msg_buffer, index); } else { send_raw(msg_buffer, index); } } else { ERROR("Unsupported 'send' message in scenario"); } return msg_buffer; } void call::do_bookkeeping(int index) { /* If this message increments a counter, do it now. */ if(int counter = scenario[index] -> counter) { CStat::instance()->computeStat(CStat::E_ADD_GENERIC_COUNTER, 1, counter - 1); } /* If this message can be used to compute RTD, do it now */ if(int rtd = scenario[index] -> start_rtd) { start_time_rtd[rtd - 1] = getmicroseconds(); } if(int rtd = scenario[index] -> stop_rtd) { if (!rtd_done[rtd - 1]) { unsigned long long start = start_time_rtd[rtd - 1]; unsigned long long end = getmicroseconds(); if(dumpInRtt) { CStat::instance()->computeRtt(start, end, rtd); } CStat::instance()->computeStat(CStat::E_ADD_RESPONSE_TIME_DURATION, (end - start) / 1000, rtd - 1); if (!scenario[index] -> repeat_rtd) { rtd_done[rtd - 1] = true; } } } } bool call::next() { int test = scenario[msg_index]->test; /* What is the next message index? */ /* Default without branching: use the next message */ int new_msg_index = msg_index+1; /* If branch needed, overwrite this default */ if ( scenario[msg_index]->next && ((test == -1) || (test <= maxVariableUsed && M_callVariableTable[test] != NULL && M_callVariableTable[test]->isSet())) ) { /* Branching possible, check the probability */ int chance = scenario[msg_index]->chance; if ((chance <= 0) || (rand() > chance )) { /* Branch == overwrite with the 'next' attribute value */ new_msg_index = labelArray[scenario[msg_index]->next]; } } msg_index=new_msg_index; recv_timeout = 0; if(msg_index >= scenario_len) { // Call end -> was it successful? if(call::last_action_result != call::E_AR_NO_ERROR) { switch(call::last_action_result) { case call::E_AR_REGEXP_DOESNT_MATCH: CStat::instance()->computeStat(CStat::E_CALL_FAILED); CStat::instance()->computeStat(CStat::E_FAILED_REGEXP_DOESNT_MATCH); break; case call::E_AR_HDR_NOT_FOUND: CStat::instance()->computeStat(CStat::E_CALL_FAILED); CStat::instance()->computeStat(CStat::E_FAILED_REGEXP_HDR_NOT_FOUND); break; case call::E_AR_NO_ERROR: case call::E_AR_STOP_CALL: /* Do nothing. */ break; } } else { CStat::instance()->computeStat(CStat::E_CALL_SUCCESSFULLY_ENDED); } delete_call(id); return false; } return run(); } bool call::run() { bool bInviteTransaction = false; int actionResult = 0; assert(running); clock_tick = getmilliseconds(); if(msg_index >= scenario_len) { ERROR_P3("Scenario overrun for call %s (%p) (index = %d)\n", id, this, msg_index); } /* Manages retransmissions or delete if max retrans reached */ if(next_retrans && (next_retrans < clock_tick)) { nb_retrans++; if ( (0 == strncmp (last_send_msg, "INVITE", 6)) ) { bInviteTransaction = true; } if((nb_retrans > (bInviteTransaction ? max_invite_retrans : max_non_invite_retrans)) || (nb_retrans > max_udp_retrans)) { scenario[last_send_index] -> nb_timeout ++; if (scenario[last_send_index]->on_timeout) { // action on timeout WARNING_P3("Call-Id: %s, timeout on max UDP retrans for message %d, jumping to label %d ", id, msg_index, scenario[last_send_index]->on_timeout); msg_index = labelArray[scenario[last_send_index]->on_timeout]; next_retrans = 0; recv_timeout = 0; if (msg_index < scenario_len) { return true; } // here if asked to go to the last label delete the call CStat::instance()->computeStat(CStat::E_CALL_FAILED); CStat::instance()->computeStat(CStat::E_FAILED_MAX_UDP_RETRANS); if (default_behavior) { // Abort the call by sending proper SIP message return(abortCall()); } else { // Just delete existing call delete_call(id); return false; } } CStat::instance()->computeStat(CStat::E_CALL_FAILED); CStat::instance()->computeStat(CStat::E_FAILED_MAX_UDP_RETRANS); if (default_behavior) { // Abort the call by sending proper SIP message WARNING_P1("Aborting call on UDP retransmission timeout for Call-ID '%s'", id); return(abortCall()); } else { // Just delete existing call delete_call(id); return false; } } else { nb_last_delay *= 2; if (DEFAULT_T2_TIMER_VALUE < nb_last_delay) { if (!bInviteTransaction) { nb_last_delay = DEFAULT_T2_TIMER_VALUE; } } if(send_raw(last_send_msg, last_send_index) < -1) { return false; } scenario[last_send_index] -> nb_sent_retrans++; CStat::instance()->computeStat(CStat::E_RETRANSMISSION); next_retrans = clock_tick + nb_last_delay; } } if(paused_until) { /* Process a pending pause instruction until delay expiration */ if(paused_until > clock_tick) { if (!remove_running_call(this)) { ERROR("Tried to remove a running call that wasn't running!\n"); } paused_calls.add_paused_call(this, true); return true; } /* Our pause is over. */ paused_until = 0; return next(); } else if(scenario[msg_index] -> pause_distribution || scenario[msg_index]->pause_variable) { unsigned int pause; if (scenario[msg_index]->pause_distribution) { pause = (int)(scenario[msg_index] -> pause_distribution -> sample()); } else { int varId = scenario[msg_index]->pause_variable; if(varId <= maxVariableUsed && M_callVariableTable[varId]) { pause = (int) M_callVariableTable[varId]->getDouble(); } else { pause = 0; } } if (pause < 0) { pause = 0; } if (pause > INT_MAX) { pause = INT_MAX; } paused_until = clock_tick + pause; /* Increment the number of sessions in pause state */ ++scenario[msg_index]->sessions; return run(); /* In case delay is 0 */ } #ifdef __3PCC__ else if(scenario[msg_index] -> M_type == MSG_TYPE_SENDCMD) { int send_status; if(next_retrans) { return true; } send_status = sendCmdMessage(msg_index); if(send_status != 0) { /* Send error */ return false; /* call deleted */ } scenario[msg_index] -> M_nbCmdSent++; next_retrans = 0; return(next()); } #endif else if(scenario[msg_index] -> M_type == MSG_TYPE_NOP) { do_bookkeeping(msg_index); actionResult = executeAction(NULL, msg_index); return(next()); } else if(scenario[msg_index] -> send_scheme) { char * msg_snd; int send_status; /* Do not send a new message until the previous one which had * retransmission enabled is acknowledged */ if(next_retrans) { if (!remove_running_call(this)) { ERROR("Tried to remove a running call that wasn't running!\n"); } paused_calls.add_paused_call(this, true); return true; } /* Handle counters and RTDs for this message. */ do_bookkeeping(msg_index); /* decide whether to increment cseq or not * basically increment for anything except response, ACK or CANCEL * Note that cseq is only used by the [cseq] keyword, and * not by default */ int incr_cseq = 0; if (!scenario[msg_index]->send_scheme->isAck() && !scenario[msg_index]->send_scheme->isCancel() && !scenario[msg_index]->send_scheme->isResponse()) { ++cseq; incr_cseq = 1; } msg_snd = send_scene(msg_index, &send_status); if(send_status == -1 && errno == EWOULDBLOCK) { if (incr_cseq) --cseq; /* Have we set the timeout yet? */ if (send_timeout) { /* If we have actually timed out. */ if (clock_tick > send_timeout) { WARNING_P2("Call-Id: %s, send timeout on message %d: aborting call", id, msg_index); CStat::instance()->computeStat(CStat::E_CALL_FAILED); CStat::instance()->computeStat(CStat::E_FAILED_TIMEOUT_ON_SEND); if (default_behavior) { return (abortCall()); } else { delete_call(id); return false; } } } else if (scenario[msg_index]->timeout) { /* Initialize the send timeout to the per message timeout. */ send_timeout = clock_tick + scenario[msg_index]->timeout; } else if (defl_send_timeout) { /* Initialize the send timeout to the global timeout. */ send_timeout = clock_tick + defl_send_timeout; } return true; /* No step, nothing done, retry later */ } else if(send_status < 0) { /* Send error */ /* The timeout will not be sent, so the timeout is no longer needed. */ send_timeout = 0; return false; /* call deleted */ } /* We have sent the message, so the timeout is no longer needed. */ send_timeout = 0; last_send_index = msg_index; last_send_msg = (char *) realloc(last_send_msg, strlen(msg_snd) + 1); strcpy(last_send_msg, msg_snd); if(last_recv_hash) { /* We are sending just after msg reception. There is a great * chance that we will be asked to retransmit this message */ recv_retrans_hash = last_recv_hash; recv_retrans_recv_index = last_recv_index; recv_retrans_send_index = msg_index; /* Prevent from detecting the cause relation between send and recv * in the next valid send */ last_recv_hash = 0; } /* Update retransmission information */ if(scenario[msg_index] -> retrans_delay) { if((transport == T_UDP) && (retrans_enabled)) { next_retrans = clock_tick + scenario[msg_index] -> retrans_delay; nb_retrans = 0; nb_last_delay = scenario[msg_index]->retrans_delay; } } else { next_retrans = 0; } #ifdef PCAPPLAY actionResult = executeAction(msg_snd, msg_index); #endif /* Update scenario statistics */ scenario[msg_index] -> nb_sent++; return next(); } else if (scenario[msg_index]->M_type == MSG_TYPE_RECV #ifdef __3PCC__ || scenario[msg_index]->M_type == MSG_TYPE_RECVCMD #endif ) { if (recv_timeout) { if(recv_timeout > clock_tick || recv_timeout > getmilliseconds()) { if (!remove_running_call(this)) { ERROR("Tried to remove a running call that wasn't running!\n"); } paused_calls.add_paused_call(this, true); return true; } recv_timeout = 0; ++scenario[msg_index]->nb_timeout; if (scenario[msg_index]->on_timeout == 0) { // if you set a timeout but not a label, the call is aborted WARNING_P2("Call-Id: %s, receive timeout on message %d without label to jump to (ontimeout attribute): aborting call", id, msg_index); CStat::instance()->computeStat(CStat::E_CALL_FAILED); CStat::instance()->computeStat(CStat::E_FAILED_TIMEOUT_ON_RECV); if (default_behavior) { return (abortCall()); } else { delete_call(id); return false; } } WARNING_P3("Call-Id: %s, receive timeout on message %d, jumping to label %d", id, msg_index, scenario[msg_index]->on_timeout); msg_index = labelArray[scenario[msg_index]->on_timeout]; recv_timeout = 0; if (msg_index < scenario_len) return true; // special case - the label points to the end - finish the call CStat::instance()->computeStat(CStat::E_CALL_FAILED); CStat::instance()->computeStat(CStat::E_FAILED_TIMEOUT_ON_RECV); if (default_behavior) { return (abortCall()); } else { delete_call(id); return false; } } else if ((scenario[msg_index]->timeout) || (defl_recv_timeout)) { if (scenario[msg_index]->timeout) // If timeout is specified on message receive, use it recv_timeout = getmilliseconds() + scenario[msg_index]->timeout; else // Else use the default timeout if specified recv_timeout = getmilliseconds() + defl_recv_timeout; return true; } else { /* We are going to wait forever. */ if (!remove_running_call(this)) { ERROR("Tried to remove a running call that wasn't running!\n"); } paused_calls.add_paused_call(this, true); } } return true; } bool call::process_unexpected(char * msg) { char buffer[MAX_HEADER_LEN]; char *desc = buffer; scenario[msg_index] -> nb_unexp++; if (default_behavior) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "Aborting "); } else { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "Continuing "); } desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "call on unexpected message for Call-Id '%s': ", id); if (scenario[msg_index] -> M_type == MSG_TYPE_RECV) { if (scenario[msg_index] -> recv_request) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while expecting '%s' ", scenario[msg_index] -> recv_request); } else { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while expecting '%d' ", scenario[msg_index] -> recv_response); } } else if (scenario[msg_index] -> M_type == MSG_TYPE_SEND) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while sending "); } else if (scenario[msg_index] -> M_type == MSG_TYPE_PAUSE) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while pausing "); } else if (scenario[msg_index] -> M_type == MSG_TYPE_SENDCMD) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while sending command "); } else if (scenario[msg_index] -> M_type == MSG_TYPE_RECVCMD) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while expecting command "); } else { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while in message type %d ", scenario[msg_index]->M_type); } desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "(index %d)", msg_index); WARNING_P2("%s, received '%s'", buffer, msg); TRACE_MSG((s, "-----------------------------------------------\n" "Unexpected %s message received:\n\n%s\n", TRANSPORT_TO_STRING(transport), msg)); if (default_behavior) { // if twin socket call => reset the other part here if (twinSippSocket && (msg_index > 0)) { //WARNING_P2("call-ID '%s', internal-cmd: abort_call %s",id, ""); sendCmdBuffer (createSendingMessage((char*)"call-id: [call_id]\ninternal-cmd: abort_call\n\n", -1)); } // usage of last_ keywords => for call aborting last_recv_msg = (char *) realloc(last_recv_msg, strlen(msg) + 1); strcpy(last_recv_msg, msg); CStat::instance()->computeStat(CStat::E_CALL_FAILED); CStat::instance()->computeStat(CStat::E_FAILED_UNEXPECTED_MSG); return (abortCall()); } else { // Do not abort call nor send anything in reply if default behavior is disabled return false; } } bool call::abortCall() { int is_inv; char * src_send = NULL ; char * src_recv = NULL ; if (last_send_msg != NULL) { is_inv = !strncmp(last_send_msg, "INVITE", 6); } else { is_inv = false; } if ((toolMode != MODE_SERVER) && (msg_index > 0)) { if ((call_established == false) && (is_inv)) { src_recv = last_recv_msg ; char L_msg_buffer[SIPP_MAX_MSG_SIZE]; L_msg_buffer[0] = '\0'; char * L_param = L_msg_buffer; // Answer unexpected errors (4XX, 5XX and beyond) with an ACK // Contributed by F. Tarek Rogers if((src_recv) && (get_reply_code(src_recv) >= 400)) { strcpy(L_param, "ACK [last_Request_URI] SIP/2.0\n"); sprintf(L_param, "%s%s", L_param, "[last_Via]\n"); sprintf(L_param, "%s%s", L_param, "[last_From]\n"); sprintf(L_param, "%s%s", L_param, "[last_To]\n"); sprintf(L_param, "%s%s", L_param, "Call-ID: [call_id]\n"); char * cseq; cseq = get_header_field_code(src_recv,(char *) "CSeq:"); if (cseq != NULL) { sprintf(L_param, "%s%s ACK\n", L_param, cseq); } sprintf(L_param, "%s%s", L_param, "Contact: \n"); sprintf(L_param, "%s%s", L_param, "Max-Forwards: 70\n"); sprintf(L_param, "%s%s", L_param, "Subject: Performance Test\n"); sprintf(L_param, "%s%s", L_param, "Content-Length: 0\n\n"); sendBuffer(createSendingMessage((char*)(L_param), -2)); } else if (src_recv) { /* Call is not established and the reply is not a 4XX, 5XX */ /* And we already received a message. */ if (ack_is_pending == true) { char * cseq = NULL; /* If an ACK is expected from the other side, send it * and send a BYE afterwards */ ack_is_pending = false; /* Send an ACK */ strcpy(L_param, "ACK [last_Request_URI] SIP/2.0\n"); sprintf(L_param, "%s%s", L_param, "Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]\n"); sprintf(L_param, "%s%s", L_param, "[last_From]\n"); sprintf(L_param, "%s%s", L_param, "[last_To]\n"); sprintf(L_param, "%s%s", L_param, "Call-ID: [call_id]\n"); src_send = last_send_msg ; cseq = get_header_field_code(src_recv,"CSeq:"); if (cseq != NULL) { sprintf(L_param, "%s%s ACK\n", L_param, cseq); } sprintf(L_param, "%s%s", L_param, "Max-Forwards: 70\n"); sprintf(L_param, "%s%s", L_param, "Contact: \n"); sprintf(L_param, "%s%s", L_param, "Content-Length: 0\n\n"); sendBuffer(createSendingMessage((char*)(L_param),-1)); /* Send the BYE */ cseq = NULL; strcpy(L_param, "BYE [last_Request_URI] SIP/2.0\n"); sprintf(L_param, "%s%s", L_param, "Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]\n"); sprintf(L_param, "%s%s", L_param, "[last_From]\n"); sprintf(L_param, "%s%s", L_param, "[last_To]\n"); sprintf(L_param, "%s%s", L_param, "Call-ID: [call_id]\n"); cseq = compute_cseq(src_recv); if (cseq != NULL) { sprintf(L_param, "%s%s BYE\n", L_param, compute_cseq(src_recv)); } sprintf(L_param, "%s%s", L_param, "Max-Forwards: 70\n"); sprintf(L_param, "%s%s", L_param, "Contact: \n"); sprintf(L_param, "%s%s", L_param, "Content-Length: 0\n\n"); sendBuffer(createSendingMessage((char*)(L_param),-1)); } else { /* Send a CANCEL */ strcpy(L_param, "CANCEL [last_Request_URI] SIP/2.0\n"); sprintf(L_param, "%s%s", L_param, "[last_Via]\n"); sprintf(L_param, "%s%s", L_param, "[last_From]\n"); sprintf(L_param, "%s%s", L_param, "[last_To]\n"); sprintf(L_param, "%s%s", L_param, "Call-ID: [call_id]\n"); sprintf(L_param, "%sCSeq: 1 CANCEL\n", L_param); sprintf(L_param, "%s%s", L_param, "Max-Forwards: 70\n"); sprintf(L_param, "%s%s", L_param, "Contact: \n"); sprintf(L_param, "%s%s", L_param, "Content-Length: 0\n\n"); sendBuffer(createSendingMessage((char*)(L_param),-2)); } } else { /* Call is not established and the reply is not a 4XX, 5XX */ /* and we didn't received any message. This is the case when */ /* we are aborting after having send an INVITE and not received */ /* any answer. */ /* Do nothing ! */ } } else if (last_recv_msg) { /* The call may not be established, if we haven't yet received a message, * because the earlier check depends on the first message being an INVITE * (although it could be something like a message message, therefore we * check that we received a message. */ char * src_recv = last_recv_msg ; char L_msg_buffer[SIPP_MAX_MSG_SIZE]; L_msg_buffer[0] = '\0'; char * L_param = L_msg_buffer; strcpy(L_param, "BYE [last_Request_URI] SIP/2.0\n"); sprintf(L_param, "%s%s", L_param, "Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]\n"); sprintf(L_param, "%s%s", L_param, "[last_From:]\n"); sprintf(L_param, "%s%s", L_param, "[last_To:]\n"); sprintf(L_param, "%s%s", L_param, "Call-ID: [call_id]\n"); char * cseq; cseq = compute_cseq(src_recv); if (cseq != NULL) { sprintf(L_param, "%s%s BYE\n", L_param, compute_cseq(src_recv)); } sprintf(L_param, "%s%s", L_param, "Max-Forwards: 70\n"); sprintf(L_param, "%s%s", L_param, "Contact: \n"); sprintf(L_param, "%s%s", L_param, "Content-Length: 0\n\n"); sendBuffer(createSendingMessage((char*)(L_param),-1)); } } delete_call(id); return false; } bool call::rejectCall() { CStat::instance()->computeStat(CStat::E_CALL_FAILED); CStat::instance()->computeStat(CStat::E_FAILED_CALL_REJECTED); delete_call(id); return false; } int call::sendCmdMessage(int index) { char * dest; char delimitor[2]; delimitor[0]=27; delimitor[1]=0; /* 3pcc extended mode */ char * peer_dest; struct sipp_socket **peer_socket; if(scenario[index] -> M_sendCmdData) { // WARNING_P1("---PREPARING_TWIN_CMD---%s---", scenario[index] -> M_sendCmdData); dest = createSendingMessage(scenario[index] -> M_sendCmdData, -1); strcat(dest, delimitor); //WARNING_P1("---SEND_TWIN_CMD---%s---", dest); int rc; /* 3pcc extended mode */ peer_dest = scenario[index]->peer_dest; if(peer_dest){ peer_socket = get_peer_socket(peer_dest); rc = write_socket(*peer_socket, dest, strlen(dest), WS_BUFFER); }else { rc = write_socket(twinSippSocket, dest, strlen(dest), WS_BUFFER); } if(rc < 0) { CStat::instance()->computeStat(CStat::E_CALL_FAILED); CStat::instance()->computeStat(CStat::E_FAILED_CMD_NOT_SENT); delete_call(id); return(-1); } return(0); } else return(-1); } int call::sendCmdBuffer(char* cmd) { char * dest; char delimitor[2]; int rc; delimitor[0]=27; delimitor[1]=0; dest = cmd ; strcat(dest, delimitor); rc = write_socket(twinSippSocket, dest, strlen(dest), WS_BUFFER); if(rc < 0) { CStat::instance()->computeStat(CStat::E_CALL_FAILED); CStat::instance()->computeStat(CStat::E_FAILED_CMD_NOT_SENT); delete_call(id); return(-1); } return(0); } char* call::createSendingMessage(SendingMessage *src, int P_index) { char * length_marker = NULL; char * auth_marker = NULL; MessageComponent *auth_comp = NULL; bool auth_comp_allocated = false; int len_offset = 0; static char msg_buffer[SIPP_MAX_MSG_SIZE+2]; char *dest = msg_buffer; bool supresscrlf = false; *dest = '\0'; for (int i = 0; i < src->numComponents(); i++) { MessageComponent *comp = src->getComponent(i); int left = sizeof(msg_buffer) - (dest - msg_buffer); switch(comp->type) { case E_Message_Literal: if (supresscrlf) { char *ptr = comp->literal; while (isspace(*ptr++)); dest += snprintf(dest, left, "%s", ptr); supresscrlf = false; } else { dest += snprintf(dest, left, "%s", comp->literal); } break; case E_Message_Remote_IP: dest += snprintf(dest, left, "%s", remote_ip_escaped); break; case E_Message_Remote_Port: dest += snprintf(dest, left, "%d", remote_port + comp->offset); break; case E_Message_Local_IP: dest += snprintf(dest, left, "%s", local_ip_escaped); break; case E_Message_Local_Port: int port; if((transport == T_UDP) && (multisocket) && (toolMode != MODE_SERVER)) { port = call_port; } else { port = local_port; } dest += snprintf(dest, left, "%d", port + comp->offset); break; case E_Message_Transport: dest += snprintf(dest, left, "%s", TRANSPORT_TO_STRING(transport)); break; case E_Message_Local_IP_Type: dest += snprintf(dest, left, "%s", (local_ip_is_ipv6 ? "6" : "4")); break; case E_Message_Server_IP: { /* We should do this conversion once per socket creation, rather than * repeating it every single time. */ struct sockaddr_storage server_sockaddr; sipp_socklen_t len = SOCK_ADDR_SIZE(&server_sockaddr); getsockname(call_socket->ss_fd, (sockaddr *)(void *)&server_sockaddr, &len); if (server_sockaddr.ss_family == AF_INET6) { char * temp_dest; temp_dest = (char *) malloc(INET6_ADDRSTRLEN); memset(temp_dest,0,INET6_ADDRSTRLEN); inet_ntop(AF_INET6, &((_RCAST(struct sockaddr_in6 *,&server_sockaddr))->sin6_addr), temp_dest, INET6_ADDRSTRLEN); dest += snprintf(dest, left, "%s",temp_dest); } else { dest += snprintf(dest, left, "%s", inet_ntoa((_RCAST(struct sockaddr_in *,&server_sockaddr))->sin_addr)); } } break; case E_Message_Media_IP: dest += snprintf(dest, left, "%s", media_ip_escaped); break; case E_Message_Media_Port: case E_Message_Auto_Media_Port: { int port = media_port + comp->offset; if (comp->type == E_Message_Auto_Media_Port) { port = media_port + (4 * (number - 1)) % 10000 + comp->offset; } #ifdef PCAPPLAY char *begin = dest; while (begin > msg_buffer) { if (*begin == '\n') { break; } begin--; } if (begin == msg_buffer) { ERROR("Can not find beginning of a line for the media port!\n"); } if (strstr(begin, "audio")) { if (media_ip_is_ipv6) { (_RCAST(struct sockaddr_in6 *, &(play_args_a.from)))->sin6_port = port; } else { (_RCAST(struct sockaddr_in *, &(play_args_a.from)))->sin_port = port; } } else if (strstr(begin, "video")) { if (media_ip_is_ipv6) { (_RCAST(struct sockaddr_in6 *, &(play_args_v.from)))->sin6_port = port; } else { (_RCAST(struct sockaddr_in *, &(play_args_v.from)))->sin_port = port; } } else { ERROR_P1("media_port keyword with no audio or video on the current line (%s)", begin); } #endif dest += sprintf(dest, "%u", port); break; } case E_Message_Media_IP_Type: dest += snprintf(dest, left, "%s", (media_ip_is_ipv6 ? "6" : "4")); break; case E_Message_Call_Number: dest += snprintf(dest, left, "%u", number); break; case E_Message_Call_ID: dest += snprintf(dest, left, "%s", id); break; case E_Message_CSEQ: dest += snprintf(dest, left, "%u", cseq + comp->offset); break; case E_Message_PID: dest += snprintf(dest, left, "%d", pid); break; case E_Message_Service: dest += snprintf(dest, left, "%s", service); break; case E_Message_Branch: /* Branch is magic cookie + call number + message index in scenario */ if(P_index == -2){ dest += snprintf(dest, left, "z9hG4bK-%u-%u-%d", pid, number, msg_index-1 + comp->offset); } else { dest += snprintf(dest, left, "z9hG4bK-%u-%u-%d", pid, number, P_index + comp->offset); } break; case E_Message_Index: dest += snprintf(dest, left, "%d", P_index); break; case E_Message_Next_Url: if (next_req_url) { dest += sprintf(dest, "%s", next_req_url); } break; case E_Message_Len: length_marker = dest; dest += snprintf(dest, left, " "); len_offset = comp->offset; break; case E_Message_Authentication: if (auth_marker) { ERROR("Only one [authentication] keyword is currently supported!\n"); } auth_marker = dest; dest += snprintf(dest, left, "[authentication place holder]"); auth_comp = comp; break; case E_Message_Peer_Tag_Param: if(peer_tag) { dest += snprintf(dest, left, ";tag=%s", peer_tag); } break; case E_Message_Routes: if (dialog_route_set) { dest += sprintf(dest, "Route: %s", dialog_route_set); } else if (*(dest - 1) == '\n') { supresscrlf = true; } break; case E_Message_ClockTick: dest += snprintf(dest, left, "%lu", clock_tick); break; case E_Message_Variable: { int varId = comp->varId; if(varId <= maxVariableUsed) { if(M_callVariableTable[varId] != NULL) { if(M_callVariableTable[varId]->isSet()) { if (M_callVariableTable[varId]->isRegExp()) { dest += sprintf(dest, "%s", M_callVariableTable[varId]->getMatchingValue()); } else if (M_callVariableTable[varId]->isDouble()) { dest += sprintf(dest, "%lf", M_callVariableTable[varId]->getDouble()); } else if (M_callVariableTable[varId]->isString()) { dest += sprintf(dest, "%s", M_callVariableTable[varId]->getString()); } else if (M_callVariableTable[varId]->isBool()) { dest += sprintf(dest, "true"); } } else if (M_callVariableTable[varId]->isBool()) { dest += sprintf(dest, "false"); } } } break; } case E_Message_Fill: { int varId = comp->varId; int length = 0; if(varId <= maxVariableUsed && M_callVariableTable[varId]) { length = (int) M_callVariableTable[varId]->getDouble(); if (length < 0) { length = 0; } } char *filltext = comp->literal; int filllen = strlen(filltext); if (filllen == 0) { ERROR("Internal error: [fill] keyword has zero-length text."); } for (int i = 0, j = 0; i < length; i++, j++) { *dest++ = filltext[j % filllen]; } *dest = '\0'; break; } case E_Message_Injection: { char *orig_dest = dest; getFieldFromInputFile(comp->comp_param.field_param.filename, comp->comp_param.field_param.field, dest); /* We are injecting an authentication line. */ if (char *tmp = strstr(orig_dest, "[authentication")) { if (auth_marker) { ERROR("Only one [authentication] keyword is currently supported!\n"); } auth_marker = tmp; auth_comp = (struct MessageComponent *)calloc(1, sizeof(struct MessageComponent)); if (!auth_comp) { ERROR("Out of memory!"); } auth_comp_allocated = true; tmp = strchr(auth_marker, ']'); char c = *tmp; *tmp = '\0'; SendingMessage::parseAuthenticationKeyword(auth_comp, auth_marker); *tmp = c; } if (*(dest - 1) == '\n') { supresscrlf = true; } break; } case E_Message_Last_Header: { char * last_header = get_last_header(comp->literal); if(last_header) { dest += sprintf(dest, "%s", last_header); } if (*(dest - 1) == '\n') { supresscrlf = true; } break; } case E_Message_Last_Request_URI: { char * last_request_uri = get_last_request_uri(); dest += sprintf(dest, "%s", last_request_uri); free(last_request_uri); break; } case E_Message_TDM_Map: if (!use_tdmmap) ERROR("[tdmmap] keyword without -tdmmap parameter on command line"); dest += snprintf(dest, left, "%d.%d.%d/%d", tdm_map_x+(int((tdm_map_number)/((tdm_map_b+1)*(tdm_map_c+1))))%(tdm_map_a+1), tdm_map_h, tdm_map_y+(int((tdm_map_number)/(tdm_map_c+1)))%(tdm_map_b+1), tdm_map_z+(tdm_map_number)%(tdm_map_c+1) ); break; } } /* Need the body for length and auth-int calculation */ char *body; if (length_marker || auth_marker) { body = strstr(msg_buffer, "\r\n\r\n"); } /* Fix up the length. */ if (length_marker) { if (auth_marker > body) { ERROR("The authentication keyword should appear in the message header, not the body!"); } if (body && dest - body > 4 && dest - body < 100004) { char tmp = length_marker[5]; sprintf(length_marker, "%5zu", dest - body - 4 + len_offset); length_marker[5] = tmp; } else { // Other cases: Content-Length is 0 sprintf(length_marker, " 0\r\n\r\n"); } } /* * The authentication substitution must be done outside the above * loop because auth-int will use the body (which must have already * been keyword substituted) to build the md5 hash */ if (auth_marker) { #ifndef _USE_OPENSSL ERROR("Authentication requires OpenSSL!"); #else if (!dialog_authentication) { ERROR("Authentication keyword without dialog_authentication!"); } int auth_marker_len; char * tmp; int authlen; auth_marker_len = (strchr(auth_marker, ']') + 1) - auth_marker; /* Need the Method name from the CSeq of the Challenge */ char method[MAX_HEADER_LEN]; tmp = get_last_header("CSeq:") + 5; if(!tmp) { ERROR("Could not extract method from cseq of challenge"); } while(isspace(*tmp) || isdigit(*tmp)) tmp++; sscanf(tmp,"%s", method); if (!body) { body = ""; } /* Determine the type of credentials. */ char result[MAX_HEADER_LEN]; if (dialog_challenge_type == 401) { /* Registrars use Authorization */ authlen = sprintf(result, "Authorization: "); } else { /* Proxies use Proxy-Authorization */ authlen = sprintf(result, "Proxy-Authorization: "); } /* Build the auth credenticals */ char uri[MAX_HEADER_LEN]; sprintf (uri, "%s:%d", remote_ip, remote_port); if (createAuthHeader(auth_comp->comp_param.auth_param.auth_user, auth_comp->comp_param.auth_param.auth_pass, method, uri, body, dialog_authentication, auth_comp->comp_param.auth_param.aka_OP, auth_comp->comp_param.auth_param.aka_AMF, auth_comp->comp_param.auth_param.aka_K, result + authlen) == 0) { ERROR_P1("%s", result + authlen); } authlen = strlen(result); /* Shift the end of the message to its rightful place. */ memmove(auth_marker + authlen, auth_marker + auth_marker_len, strlen(auth_marker + auth_marker_len) + 1); /* Copy our result into the hole. */ memcpy(auth_marker, result, authlen); #endif } if (auth_comp_allocated) { SendingMessage::freeMessageComponent(auth_comp); } return msg_buffer; } char* call::createSendingMessage(char *src, int P_index, bool skip_sanity) { if (src == NULL) { ERROR("Unsupported 'send' message in scenario"); } SendingMessage *msgsrc = new SendingMessage(src, skip_sanity); char *msg = createSendingMessage(msgsrc, P_index); delete msgsrc; return msg; } #ifdef __3PCC__ bool call::process_twinSippCom(char * msg) { int search_index; bool found = false; T_ActionResult actionResult; if (!running) { paused_calls.remove_paused_call(this); add_running_call(this); } if (checkInternalCmd(msg) == false) { for(search_index = msg_index; search_index < scenario_len; search_index++) { if(scenario[search_index] -> M_type != MSG_TYPE_RECVCMD) { if(scenario[search_index] -> optional) { continue; } /* The received message is different from the expected one */ TRACE_MSG((s, "Unexpected control message received (I was expecting a different type of message):\n%s\n", msg)); return rejectCall(); } else { if(extendedTwinSippMode){ // 3pcc extended mode if(check_peer_src(msg, search_index)){ found = true; break; } else{ WARNING_P1("Unexpected sender for the received peer message \n%s\n", msg); return rejectCall(); } } else { found = true; break; } } } if (found) { scenario[search_index]->M_nbCmdRecv ++; // variable treatment // Remove \r, \n at the end of a received command // (necessary for transport, to be removed for usage) while ( (msg[strlen(msg)-1] == '\n') && (msg[strlen(msg)-2] == '\r') ) { msg[strlen(msg)-2] = 0; } actionResult = executeAction(msg, search_index); if(actionResult != call::E_AR_NO_ERROR) { // Store last action result if it is an error // and go on with the scenario call::last_action_result = actionResult; if (actionResult == E_AR_STOP_CALL) { return rejectCall(); } } } else { TRACE_MSG((s, "Unexpected control message received (no such message found):\n%s\n", msg)); return rejectCall(); } msg_index = search_index; //update the state machine return(next()); } else { return (false); } } bool call::checkInternalCmd(char * cmd) { char * L_ptr1, * L_ptr2, L_backup; L_ptr1 = strstr(cmd, "internal-cmd:"); if (!L_ptr1) {return (false);} L_ptr1 += 13 ; while((*L_ptr1 == ' ') || (*L_ptr1 == '\t')) { L_ptr1++; } if (!(*L_ptr1)) {return (false);} L_ptr2 = L_ptr1; while((*L_ptr2) && (*L_ptr2 != ' ') && (*L_ptr2 != '\t') && (*L_ptr2 != '\r') && (*L_ptr2 != '\n')) { L_ptr2 ++; } if(!*L_ptr2) { return (false); } L_backup = *L_ptr2; *L_ptr2 = 0; if (strcmp(L_ptr1, "abort_call") == 0) { *L_ptr2 = L_backup; abortCall(); CStat::instance()->computeStat(CStat::E_CALL_FAILED); return (true); } *L_ptr2 = L_backup; return (false); } bool call::check_peer_src(char * msg, int search_index) { char * L_ptr1, * L_ptr2, L_backup ; L_ptr1 = strstr(msg, "From:"); if (!L_ptr1) {return (false);} L_ptr1 += 5 ; while((*L_ptr1 == ' ') || (*L_ptr1 == '\t')) { L_ptr1++; } if (!(*L_ptr1)) {return (false);} L_ptr2 = L_ptr1; while((*L_ptr2) && (*L_ptr2 != ' ') && (*L_ptr2 != '\t') && (*L_ptr2 != '\r') && (*L_ptr2 != '\n')) { L_ptr2 ++; } if(!*L_ptr2) { return (false); } L_backup = *L_ptr2; *L_ptr2 = 0; if (strcmp(L_ptr1, scenario[search_index] -> peer_src) == 0) { *L_ptr2 = L_backup; return(true); } *L_ptr2 = L_backup; return (false); } #endif void call::extract_cseq_method (char* method, char* msg) { char* cseq ; if (cseq = strstr (msg, "CSeq")) { char * value ; if ( value = strchr (cseq, ':')) { value++; while ( isspace(*value)) value++; // ignore any white spaces after the : while ( !isspace(*value)) value++; // ignore the CSEQ numnber value++; char *end = value; int nbytes = 0; /* A '\r' terminates the line, so we want to catch that too. */ while ((*end != '\r') && (*end != '\n')) { end++; nbytes++; } if (nbytes > 0) strncpy (method, value, nbytes); method[nbytes] = '\0'; } } } void call::formatNextReqUrl (char* next_req_url) { /* clean up the next_req_url -- Record routes may have extra gunk that needs to be removed */ char* actual_req_url = strchr(next_req_url, '<'); if (actual_req_url) { /* using a temporary buffer */ char tempBuffer[MAX_HEADER_LEN]; strcpy(tempBuffer, actual_req_url + 1); actual_req_url = strrchr(tempBuffer, '>'); *actual_req_url = '\0'; strcpy(next_req_url, tempBuffer); } } void call::computeRouteSetAndRemoteTargetUri (char* rr, char* contact, bool bRequestIncoming) { if (0 >=strlen (rr)) { // // there are no RR headers. Simply set up the contact as our target uri // if (0 < strlen(contact)) { strcpy (next_req_url, contact); } formatNextReqUrl(next_req_url); return; } char actual_rr[MAX_HEADER_LEN]; char targetURI[MAX_HEADER_LEN]; memset(actual_rr, 0, sizeof(actual_rr)); bool isFirst = true; bool bCopyContactToRR = false; while (1) { char* pointer = NULL; if (bRequestIncoming) { pointer = strchr (rr, ','); } else { pointer = strrchr(rr, ','); } if (pointer) { if (!isFirst) { if (strlen(actual_rr) ) { strcat(actual_rr, pointer + 1); } else { strcpy(actual_rr, pointer + 1); } strcat(actual_rr, ","); } else { isFirst = false; if (NULL == strstr (pointer, ";lr")) { /* bottom most RR is the next_req_url */ strcpy (targetURI, pointer + 1); bCopyContactToRR = true; } else { /* the hop is a loose router. Thus, the target URI should be the * contact */ strcpy (targetURI, contact); strcpy(actual_rr, pointer + 1); strcat(actual_rr, ","); } } } else { if (!isFirst) { strcat(actual_rr, rr); } // // this is the *only* RR header that was found // else { if (NULL == strstr (rr, ";lr")) { /* bottom most RR is the next_req_url */ strcpy (targetURI, rr); bCopyContactToRR = true; } else { /* the hop is a loose router. Thus, the target URI should be the * contact */ strcpy (actual_rr, rr); strcpy (targetURI, contact); } } break; } *pointer = '\0'; } if (bCopyContactToRR) { if (0 < strlen (actual_rr)) { strcat(actual_rr, ","); strcat(actual_rr, contact); } else { strcpy(actual_rr, contact); } } if (strlen(actual_rr)) { dialog_route_set = (char *) calloc(1, strlen(actual_rr) + 2); sprintf(dialog_route_set, "%s", actual_rr); } if (strlen (targetURI)) { strcpy (next_req_url, targetURI); formatNextReqUrl (next_req_url); } } bool call::matches_scenario(unsigned int index, int reply_code, char * request, char * responsecseqmethod) { int result; if ((reply_code) && ((scenario[index] -> recv_response) == reply_code) && \ (index == 0 || ((scenario[index]->recv_response_for_cseq_method_list) && \ (strstr(scenario[index]->recv_response_for_cseq_method_list, responsecseqmethod))))) { return true; } if ((scenario[index] -> recv_request) && \ (!strcmp(scenario[index] -> recv_request, request))) { return true; } if ((scenario[index] -> recv_request) && (scenario[index] -> regexp_match)) { if (scenario[index] -> regexp_compile == NULL) { regex_t *re = new regex_t; if (regcomp(re, scenario[index] -> recv_request, REG_EXTENDED|REG_NOSUB)) { // regexp is not well formed scenario[index] -> regexp_match = 0; free(re); return false; } scenario[index] -> regexp_compile = re; } result = regexec(scenario[index] -> regexp_compile, request, (size_t)0, NULL, 0); if (!result) return true; } return false; } bool call::process_incoming(char * msg) { int reply_code; static char request[65]; char responsecseqmethod[65]; unsigned long cookie; char * ptr; int search_index; bool found = false; T_ActionResult actionResult; int L_case = 0 ; if (!running) { paused_calls.remove_paused_call(this); add_running_call(this); } /* Ignore the messages received during a pause if -pause_msg_ign is set */ if(scenario[msg_index] -> M_type == MSG_TYPE_PAUSE && pause_msg_ign) return(true); /* Authorize nop as a first command, even in server mode */ if((msg_index == 0) && (scenario[msg_index] -> M_type == MSG_TYPE_NOP)) { actionResult = executeAction(NULL, msg_index); return next(); } responsecseqmethod[0] = '\0'; if((transport == T_UDP) && (retrans_enabled)) { /* Detects retransmissions from peer and retransmit the * message which was sent just after this one was received */ cookie = hash(msg); if(recv_retrans_hash == cookie) { int status; if(lost(recv_retrans_recv_index)) { TRACE_MSG((s, "%s message (retrans) lost (recv).", TRANSPORT_TO_STRING(transport))); if(comp_state) { comp_free(&comp_state); } scenario[recv_retrans_recv_index] -> nb_lost++; return true; } scenario[recv_retrans_recv_index] -> nb_recv_retrans++; send_scene(recv_retrans_send_index, &status); if(status == 0) { scenario[recv_retrans_send_index] -> nb_sent_retrans++; CStat::instance()->computeStat(CStat::E_RETRANSMISSION); } else if(status < 0) { return false; } return true; } if(last_recv_hash == cookie) { /* This one has already been received, but not processed * yet => (has not triggered something yet) so we can discard. * * This case appears when the UAS has send a 200 but not received * a ACK yet. Thus, the UAS retransmit the 200 (invite transaction) * until it receives a ACK. In this case, it nevers sends the 200 * from the BYE, until it has reveiced the previous 200. Thus, * the UAC retransmit the BYE, and this BYE is considered as an * unexpected. * * This case can also appear in case of message duplication by * the network. This should not be considered as an unexpected. */ scenario[last_recv_index]->nb_recv_retrans++; return true; } } /* Is it a response ? */ if((msg[0] == 'S') && (msg[1] == 'I') && (msg[2] == 'P') && (msg[3] == '/') && (msg[4] == '2') && (msg[5] == '.') && (msg[6] == '0') ) { reply_code = get_reply_code(msg); if(!reply_code) { if (!process_unexpected(msg)) { return false; // Call aborted by unexpected message handling } #ifdef PCAPPLAY } else if ((hasMedia == 1) && *(strstr(msg, "\r\n\r\n")+4) != '\0') { /* Get media info if we find something like an SDP */ get_remote_media_addr(msg); #endif } /* It is a response: update peer_tag */ ptr = get_peer_tag(msg); if (ptr) { if(strlen(ptr) > (MAX_HEADER_LEN - 1)) { ERROR("Peer tag too long. Change MAX_HEADER_LEN and recompile sipp"); } if(peer_tag) { free(peer_tag); } peer_tag = strdup(ptr); if (!peer_tag) { ERROR("Out of memory allocating peer tag."); } } request[0]=0; // extract the cseq method from the response extract_cseq_method (responsecseqmethod, msg); } else if(ptr = strchr(msg, ' ')) { if((ptr - msg) < 64) { memcpy(request, msg, ptr - msg); request[ptr - msg] = 0; // Check if we received an ACK => call established if (strcmp(request,"ACK")==0) { call_established=true; } #ifdef PCAPPLAY /* In case of INVITE or re-INVITE, ACK or PRACK get the media info if needed (= we got a pcap play action) */ if ((strncmp(request, "INVITE", 6) == 0) || (strncmp(request, "ACK", 3) == 0) || (strncmp(request, "PRACK", 5) == 0) && (hasMedia == 1)) get_remote_media_addr(msg); #endif reply_code = 0; } else { ERROR_P1("SIP method too long in received message '%s'", msg); } } else { ERROR_P1("Invalid sip message received '%s'", msg); } /* Try to find it in the expected non mandatory responses * until the first mandatory response in the scenario */ for(search_index = msg_index; search_index < scenario_len; search_index++) { if(!matches_scenario(search_index, reply_code, request, responsecseqmethod)) { if(scenario[search_index] -> optional) { continue; } /* The received message is different for the expected one */ break; } found = true; /* TODO : this is a little buggy: If a 100 trying from an INVITE * is delayed by the network until the BYE is sent, it may * stop BYE transmission erroneously, if the BYE also expects * a 100 trying. */ break; } /* Try to find it in the old non-mandatory receptions */ if(!found) { bool contig = true; for(search_index = msg_index - 1; search_index >= 0; search_index--) { if (scenario[search_index]->optional == OPTIONAL_FALSE) contig = false; if(matches_scenario(search_index, reply_code, request, responsecseqmethod)) { if (contig || scenario[search_index]->optional == OPTIONAL_GLOBAL) { found = true; break; } else { /* * we received a non mandatory msg for an old transaction (this could be due to a retransmit. * If this response is for an INVITE transaction, retransmit the ACK to quench retransmits. */ if ( (reply_code) && (0 == strncmp (responsecseqmethod, "INVITE", strlen(responsecseqmethod)) ) && (scenario[search_index+1]->M_type == MSG_TYPE_SEND) && (scenario[search_index+1]->send_scheme->isAck()) ) { sendBuffer(createSendingMessage(scenario[search_index+1] -> send_scheme, (search_index+1))); return true; } } } } } /* If it is still not found, process an unexpected message */ if(!found) { if ((L_case = checkAutomaticResponseMode(request)) == 0) { if (!process_unexpected(msg)) { return false; // Call aborted by unexpected message handling } } else { // call aborted by automatic response mode if needed return automaticResponseMode(L_case, msg); } } int test = (!found) ? -1 : scenario[search_index]->test; /* test==0: No branching" * test==-1 branching without testing" * test>0 branching with testing */ /* Simulate loss of messages */ if(lost(search_index)) { TRACE_MSG((s, "%s message lost (recv).", TRANSPORT_TO_STRING(transport))); if(comp_state) { comp_free(&comp_state); } scenario[search_index] -> nb_lost++; return true; } /* Handle counters and RTDs for this message. */ do_bookkeeping(search_index); /* Increment the recv counter */ scenario[search_index] -> nb_recv++; // Action treatment if (found) { //WARNING_P1("---EXECUTE_ACTION_ON_MSG---%s---", msg); actionResult = executeAction(msg, search_index); if(actionResult != call::E_AR_NO_ERROR) { // Store last action result if it is an error // and go on with the scenario call::last_action_result = actionResult; if (actionResult == E_AR_STOP_CALL) { return rejectCall(); } } } if (request) { // update [cseq] with received CSeq unsigned long int rcseq = get_cseq_value(msg); if (rcseq > cseq) cseq = rcseq; } /* This is an ACK/PRACK or a response, and its index is greater than the * current active retransmission message, so we stop the retrans timer. * True also for CANCEL and BYE that we also want to answer to */ if(((reply_code) || ((!strcmp(request, "ACK")) || (!strcmp(request, "CANCEL")) || (!strcmp(request, "BYE")) || (!strcmp(request, "PRACK")))) && (search_index > last_send_index)) { /* * We should stop any retransmission timers on receipt of a provisional response only for INVITE * transactions. Non INVITE transactions continue to retransmit at T2 until a final response is * received */ if ( (0 == reply_code) || // means this is a request. (200 <= reply_code) || // final response ((0 != reply_code) && (0 == strncmp (responsecseqmethod, "INVITE", strlen(responsecseqmethod)))) ) // prov for INVITE { next_retrans = 0; } else { /* * We are here due to a provisional response for non INVITE. Update our next retransmit. */ next_retrans = clock_tick + DEFAULT_T2_TIMER_VALUE; nb_last_delay = DEFAULT_T2_TIMER_VALUE; } } /* This is a response with 200 so set the flag indicating that an * ACK is pending (used to prevent from release a call with CANCEL * when an ACK+BYE should be sent instead) */ if (reply_code == 200) { ack_is_pending = true; } /* store the route set only once. TODO: does not support target refreshes!! */ if (scenario[search_index] -> bShouldRecordRoutes && NULL == dialog_route_set ) { next_req_url = (char*) calloc(1, MAX_HEADER_LEN); char rr[MAX_HEADER_LEN]; memset(rr, 0, sizeof(rr)); strcpy(rr, get_header_content(msg, (char*)"Record-Route:")); // WARNING_P1("rr [%s]", rr); char ch[MAX_HEADER_LEN]; strcpy(ch, get_header_content(msg, (char*)"Contact:")); /* decorate the contact with '<' and '>' if it does not have it */ char* contDecorator = strchr(ch, '<'); if (NULL == contDecorator) { char tempBuffer[MAX_HEADER_LEN]; sprintf(tempBuffer, "<%s>", ch); strcpy(ch, tempBuffer); } /* should cache the route set */ if (reply_code) { computeRouteSetAndRemoteTargetUri (rr, ch, false); } else { computeRouteSetAndRemoteTargetUri (rr, ch, true); } // WARNING_P1("next_req_url is [%s]", next_req_url); } #ifdef _USE_OPENSSL /* store the authentication info */ if ((scenario[search_index] -> bShouldAuthenticate) && (reply_code == 401 || reply_code == 407)) { /* is a challenge */ char auth[MAX_HEADER_LEN]; memset(auth, 0, sizeof(auth)); strcpy(auth, get_header_content(msg, (char*)"Proxy-Authenticate:")); if (auth[0] == 0) { strcpy(auth, get_header_content(msg, (char*)"WWW-Authenticate:")); } if (auth[0] == 0) { ERROR("Couldn't find 'Proxy-Authenticate' or 'WWW-Authenticate' in 401 or 407!"); } dialog_authentication = (char *) realloc(dialog_authentication, strlen(auth) + 2); sprintf(dialog_authentication, "%s", auth); /* Store the code of the challenge for building the proper header */ dialog_challenge_type = reply_code; } #endif /* Store last received message information for all messages so that we can * correctly identify retransmissions, and use its body for inclusion * in our messages. */ last_recv_index = search_index; last_recv_hash = cookie; last_recv_msg = (char *) realloc(last_recv_msg, strlen(msg) + 1); strcpy(last_recv_msg, msg); /* If this was a mandatory message, or if there is an explicit next label set * we must update our state machine. */ if (!(scenario[search_index] -> optional) || scenario[search_index]->next && ((test == -1) || (test <= maxVariableUsed && M_callVariableTable[test] != NULL && M_callVariableTable[test]->isSet())) ) { /* If we are paused, then we need to wake up so that we properly go through the state machine. */ paused_until = 0; msg_index = search_index; return next(); } else { unsigned int timeout = call_wake(this); unsigned int candidate; if (scenario[search_index]->next && test <= maxVariableUsed && M_callVariableTable[test] != NULL && M_callVariableTable[test]->isSet()) { WARNING_P1("Last message generates an error and will not be used for next sends (for last_ variables):\r\n%s",msg); } /* We are just waiting for a message to be received, if any of the * potential messages have a timeout we set it as our timeout. We * start from the next message and go until any non-receives. */ for(search_index++; search_index < scenario_len; search_index++) { if(scenario[search_index] -> M_type != MSG_TYPE_RECV) { break; } candidate = scenario[search_index] -> timeout; if (candidate == 0) { if (defl_recv_timeout == 0) { continue; } candidate = defl_recv_timeout; } if (!timeout || (clock_tick + candidate < timeout)) { timeout = clock_tick + candidate; } } if (!remove_running_call(this)) { ERROR("Tried to remove a running call that wasn't running!\n"); } paused_calls.add_paused_call(this, true); } return true; } double call::get_rhs(CAction *currentAction) { if (currentAction->getVarInId()) { return M_callVariableTable[currentAction->getVarInId()]->getDouble(); } else { return currentAction->getDoubleValue(); } } call::T_ActionResult call::executeAction(char * msg, int scenarioIndex) { CActions* actions; CAction* currentAction; CVariable* scenVariable; char msgPart[MAX_SUB_MESSAGE_LENGTH]; int currentId; actions = scenario[scenarioIndex]->M_actions; // looking for action to do on this message if(actions != NULL) { for(int i=0; igetActionSize(); i++) { currentAction = actions->getAction(i); if(currentAction != NULL) { if(currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_REGEXP) { currentId = currentAction->getVarId(); scenVariable = scenVariableTable[currentId][scenarioIndex]; if(scenVariable != NULL) { if(currentAction->getLookingPlace() == CAction::E_LP_HDR) { extractSubMessage (msg, currentAction->getLookingChar(), msgPart, currentAction->getCaseIndep(), currentAction->getOccurence(), currentAction->getHeadersOnly()); if(strlen(msgPart) > 0) { scenVariable->executeRegExp(msgPart, M_callVariableTable, currentId, currentAction->getNbSubVarId(), currentAction->getSubVarId()); if( (!(M_callVariableTable[currentId]->isSet())) && (currentAction->getCheckIt() == true) ) { // the message doesn't match and the checkit // action say it MUST match // Allow easier regexp debugging WARNING_P2("Failed regexp match: looking " "in '%s', with regexp '%s'", msgPart, scenVariable-> getRegularExpression()); // --> Call will be marked as failed return(call::E_AR_REGEXP_DOESNT_MATCH); } } else {// sub part of message not found if( currentAction->getCheckIt() == true ) { // the sub message is not found and the // checking action say it MUST match // --> Call will be marked as failed but // will go on WARNING_P2("Failed regexp match: header %s not found in message %s\n", currentAction->getLookingChar(), msg); return(call::E_AR_HDR_NOT_FOUND); } } } else {// we must look in the entire message // WARNING_P1("LOOKING IN MSG -%s-", msg); scenVariable->executeRegExp(msg, M_callVariableTable, currentId, currentAction->getNbSubVarId(), currentAction->getSubVarId()); if((!(M_callVariableTable[currentId]->isSet())) && (currentAction->getCheckIt() == true) ) { // the message doesn't match and the checkit // action say it MUST match // Allow easier regexp debugging WARNING_P2("Failed regexp match: looking in '%s'" ", with regexp '%s'", msg, scenVariable->getRegularExpression()); // --> rejecting the call return(call::E_AR_REGEXP_DOESNT_MATCH); } } } // end if scen variable != null } else /* end action == E_AT_ASSIGN_FROM_REGEXP */ if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_VALUE) { M_callVariableTable[currentAction->getVarId()]->setDouble(currentAction->getDoubleValue()); } else if (currentAction->getActionType() == CAction::E_AT_VAR_ADD) { double value = M_callVariableTable[currentAction->getVarId()]->getDouble(); double operand = get_rhs(currentAction); M_callVariableTable[currentAction->getVarId()]->setDouble(value + operand); } else if (currentAction->getActionType() == CAction::E_AT_VAR_SUBTRACT) { double value = M_callVariableTable[currentAction->getVarId()]->getDouble(); double operand = get_rhs(currentAction); M_callVariableTable[currentAction->getVarId()]->setDouble(value - operand); } else if (currentAction->getActionType() == CAction::E_AT_VAR_MULTIPLY) { double value = M_callVariableTable[currentAction->getVarId()]->getDouble(); double operand = get_rhs(currentAction); M_callVariableTable[currentAction->getVarId()]->setDouble(value * operand); } else if (currentAction->getActionType() == CAction::E_AT_VAR_DIVIDE) { double value = M_callVariableTable[currentAction->getVarId()]->getDouble(); double operand = get_rhs(currentAction); if (operand == 0) { WARNING_P2("Action failure: Can not divide by zero ($%d/$%d)!\n", currentAction->getVarId(), currentAction->getVarInId()); } else { M_callVariableTable[currentAction->getVarId()]->setDouble(value / operand); } } else if (currentAction->getActionType() == CAction::E_AT_VAR_TEST) { double value = currentAction->compare(M_callVariableTable); M_callVariableTable[currentAction->getVarId()]->setBool(value); } else if (currentAction->getActionType() == CAction::E_AT_VAR_STRCMP) { char *rhs = M_callVariableTable[currentAction->getVarInId()]->getString(); char *lhs = currentAction->getStringValue(); int value = strcmp(rhs, lhs); M_callVariableTable[currentAction->getVarId()]->setDouble((double)value); } else if (currentAction->getActionType() == CAction::E_AT_VAR_TO_DOUBLE) { double value; if (M_callVariableTable[currentAction->getVarInId()]->toDouble(&value)) { M_callVariableTable[currentAction->getVarId()]->setDouble(value); } else { WARNING_P2("Invalid double conversion from $%d to $%d", currentAction->getVarInId(), currentAction->getVarId()); } } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_SAMPLE) { double value = currentAction->getDistribution()->sample(); M_callVariableTable[currentAction->getVarId()]->setDouble(value); } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_STRING) { char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/, true /* skip sanity check */); char *str = strdup(x); if (!str) { ERROR("Out of memory duplicating string for assignment!"); } M_callVariableTable[currentAction->getVarId()]->setString(str); } else if (currentAction->getActionType() == CAction::E_AT_LOG_TO_FILE) { char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/, true /* skip sanity check */); LOG_MSG((s, "%s\n", x)); } else if (currentAction->getActionType() == CAction::E_AT_EXECUTE_CMD) { if (currentAction->getCmdLine()) { char* x = createSendingMessage(currentAction->getCmdLine(), -2 /* do not add crlf*/, true /* skip sanity check. */); // TRACE_MSG((s, "Trying to execute [%s]", x)); pid_t l_pid; switch(l_pid = fork()) { case -1: // error when forking ! ERROR_NO("Forking error main"); break; case 0: // first child process - execute the command if((l_pid = fork()) < 0) { ERROR_NO("Forking error child"); } else { if( l_pid == 0){ int ret; ret = system(x); // second child runs if(ret == -1) { WARNING_P1("system call error for %s",x); } } exit(EXIT_OTHER); } break; default: // parent process continue // reap first child immediately pid_t ret; while ((ret=waitpid(l_pid, NULL, 0)) != l_pid) { if (ret != -1) { ERROR_P2("waitpid returns %1d for child %1d",ret,l_pid); } } break; } } } else /* end action == E_AT_EXECUTE_CMD */ if (currentAction->getActionType() == CAction::E_AT_EXEC_INTCMD) { switch (currentAction->getIntCmd()) { case CAction::E_INTCMD_STOP_ALL: quitting = 1; break; case CAction::E_INTCMD_STOP_NOW: screen_exit(EXIT_TEST_RES_INTERNAL); break; case CAction::E_INTCMD_STOPCALL: default: return(call::E_AR_STOP_CALL); break; } #ifdef PCAPPLAY } else if ((currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) || (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO)) { play_args_t *play_args; if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) { play_args = &(this->play_args_a); } else if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO) { play_args = &(this->play_args_v); } play_args->pcap = currentAction->getPcapPkts(); /* port number is set in [auto_]media_port interpolation */ if (media_ip_is_ipv6) { struct sockaddr_in6 *from = (struct sockaddr_in6 *)(void *) &(play_args->from); from->sin6_family = AF_INET6; inet_pton(AF_INET6, media_ip, &(from->sin6_addr)); } else { struct sockaddr_in *from = (struct sockaddr_in *)(void *) &(play_args->from); from->sin_family = AF_INET; from->sin_addr.s_addr = inet_addr(media_ip); } /* Create a thread to send RTP packets */ pthread_attr_t attr; pthread_attr_init(&attr); #ifndef PTHREAD_STACK_MIN #define PTHREAD_STACK_MIN 16384 #endif //pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); int ret = pthread_create(&media_thread, &attr, send_wrapper, (void *) play_args); if(ret) ERROR("Can create thread to send RTP packets"); pthread_attr_destroy(&attr); #endif } else { ERROR("call::executeAction unknown action"); } } // end if current action != null } // end for } return(call::E_AR_NO_ERROR); } void call::extractSubMessage(char * msg, char * matchingString, char* result, bool case_indep, int occurrence, bool headers) { char *ptr, *ptr1; int sizeOf; int i = 0; int len = strlen(matchingString); char mat1 = tolower(*matchingString); char mat2 = toupper(*matchingString); ptr = msg; while (*ptr) { if (!case_indep) { ptr = strstr(ptr, matchingString); if (ptr == NULL) break; if (headers == true && ptr != msg && *(ptr-1) != '\n') { ++ptr; continue; } } else { if (headers) { if (ptr != msg) { ptr = strchr(ptr, '\n'); if (ptr == NULL) break; ++ptr; if (*ptr == 0) break; } } else { ptr1 = strchr(ptr, mat1); ptr = strchr(ptr, mat2); if (ptr == NULL) { if (ptr1 == NULL) break; ptr = ptr1; } else { if (ptr1 != NULL && ptr1 < ptr) ptr = ptr1; } } if (strncasecmp(ptr, matchingString, len) != 0) { ++ptr; continue; } } // here with ptr pointing to a matching string if (occurrence <= 1) break; --occurrence; ++ptr; } if(ptr != NULL && *ptr != 0) { strncpy(result, ptr+len, MAX_SUB_MESSAGE_LENGTH); sizeOf = strlen(result); if(sizeOf >= MAX_SUB_MESSAGE_LENGTH) sizeOf = MAX_SUB_MESSAGE_LENGTH-1; while((igetField(line, field, dest, SIPP_MAX_MSG_SIZE); } int call::checkAutomaticResponseMode(char * P_recv) { int L_res = 0 ; if (strcmp(P_recv, "BYE")==0) { L_res = 1 ; } else if (strcmp(P_recv, "CANCEL") == 0) { L_res = 2 ; } else if (strcmp(P_recv, "PING") == 0) { L_res = 3 ; } else if (((strcmp(P_recv, "INFO") == 0) || (strcmp(P_recv, "NOTIFY") == 0) || (strcmp(P_recv, "UPDATE") == 0)) && (auto_answer == true)){ L_res = 4 ; } return (L_res) ; } bool call::automaticResponseMode(int P_case, char * P_recv) { int res ; char * old_last_recv_msg = NULL; bool last_recv_msg_saved = false; switch (P_case) { case 1: // response for an unexpected BYE // usage of last_ keywords last_recv_msg = (char *) realloc(last_recv_msg, strlen(P_recv) + 1); strcpy(last_recv_msg, P_recv); // The BYE is unexpected, count it scenario[msg_index] -> nb_unexp++; if (default_behavior) { WARNING_P1("Aborting call on an unexpected BYE for call: %s", (id==NULL)?"none":id); sendBuffer(createSendingMessage( (char*)"SIP/2.0 200 OK\n" "[last_Via:]\n" "[last_From:]\n" "[last_To:]\n" "[last_Call-ID:]\n" "[last_CSeq:]\n" "Contact: \n" "Content-Length: 0\n\n" , -1)) ; #ifdef __3PCC__ // if twin socket call => reset the other part here if (twinSippSocket && (msg_index > 0)) { res = sendCmdBuffer (createSendingMessage((char*)"call-id: [call_id]\ninternal-cmd: abort_call\n\n", -1)); } #endif /* __3PCC__ */ CStat::instance()->computeStat(CStat::E_CALL_FAILED); CStat::instance()->computeStat(CStat::E_FAILED_UNEXPECTED_MSG); delete_call(id); } else { WARNING_P1("Continuing call on an unexpected BYE for call: %s", (id==NULL)?"none":id); } break ; case 2: // response for an unexpected cancel // usage of last_ keywords last_recv_msg = (char *) realloc(last_recv_msg, strlen(P_recv) + 1); strcpy(last_recv_msg, P_recv); // The CANCEL is unexpected, count it scenario[msg_index] -> nb_unexp++; if (default_behavior) { WARNING_P1("Aborting call on an unexpected CANCEL for call: %s", (id==NULL)?"none":id); sendBuffer(createSendingMessage( (char*)"SIP/2.0 200 OK\n" "[last_Via:]\n" "[last_From:]\n" "[last_To:]\n" "[last_Call-ID:]\n" "[last_CSeq:]\n" "Contact: sip:sipp@[local_ip]:[local_port]\n" "Content-Length: 0\n\n" , -1)) ; #ifdef __3PCC__ // if twin socket call => reset the other part here if (twinSippSocket && (msg_index > 0)) { res = sendCmdBuffer (createSendingMessage((char*)"call-id: [call_id]\ninternal-cmd: abort_call\n\n", -1)); } #endif /* __3PCC__ */ CStat::instance()->computeStat(CStat::E_CALL_FAILED); CStat::instance()->computeStat(CStat::E_FAILED_UNEXPECTED_MSG); delete_call(id); } else { WARNING_P1("Continuing call on unexpected CANCEL for call: %s", (id==NULL)?"none":id); } break ; case 3: // response for a random ping // usage of last_ keywords last_recv_msg = (char *) realloc(last_recv_msg, strlen(P_recv) + 1); strcpy(last_recv_msg, P_recv); if (default_behavior) { WARNING_P1("Automatic response mode for an unexpected PING for call: %s", (id==NULL)?"none":id); count_in_stats = false; // Call must not be counted in statistics sendBuffer(createSendingMessage( (char*)"SIP/2.0 200 OK\n" "[last_Via:]\n" "[last_Call-ID:]\n" "[last_To:]\n" "[last_From:]\n" "[last_CSeq:]\n" "Contact: sip:sipp@[local_ip]:[local_port]\n" "Content-Length: 0\n\n" , -1)) ; // Note: the call ends here but it is not marked as bad. PING is a // normal message. #ifdef __3PCC__ // if twin socket call => reset the other part here if (twinSippSocket && (msg_index > 0)) { res = sendCmdBuffer (createSendingMessage((char*)"call-id: [call_id]\ninternal-cmd: abort_call\n\n",-1)); } #endif /* __3PCC__ */ CStat::instance()->computeStat(CStat::E_AUTO_ANSWERED); delete_call(id); } else { WARNING_P1("Do not answer on an unexpected PING for call: %s", (id==NULL)?"none":id); } break ; case 4: // response for a random INFO, UPDATE or NOTIFY // store previous last msg if msg is INFO, UPDATE or NOTIFY // restore last_recv_msg to previous one // after sending ok old_last_recv_msg = NULL; if (last_recv_msg != NULL) { last_recv_msg_saved = true; old_last_recv_msg = (char *) malloc(strlen(last_recv_msg)+1); strcpy(old_last_recv_msg,last_recv_msg); } // usage of last_ keywords last_recv_msg = (char *) realloc(last_recv_msg, strlen(P_recv) + 1); strcpy(last_recv_msg, P_recv); WARNING_P1("Automatic response mode for an unexpected INFO, UPDATE or NOTIFY for call: %s", (id==NULL)?"none":id); sendBuffer(createSendingMessage( (char*)"SIP/2.0 200 OK\n" "[last_Via:]\n" "[last_Call-ID:]\n" "[last_To:]\n" "[last_From:]\n" "[last_CSeq:]\n" "Contact: sip:sipp@[local_ip]:[local_port]\n" "Content-Length: 0\n\n" , -1)) ; // restore previous last msg if (last_recv_msg_saved == true) { last_recv_msg = (char *) realloc(last_recv_msg, strlen(old_last_recv_msg) + 1); strcpy(last_recv_msg, old_last_recv_msg); if (old_last_recv_msg != NULL) { free(old_last_recv_msg); old_last_recv_msg = NULL; } } CStat::instance()->computeStat(CStat::E_AUTO_ANSWERED); return true; break; case 5: // response for an out of call message old_last_recv_msg = NULL; if (last_recv_msg != NULL) { last_recv_msg_saved = true; old_last_recv_msg = (char *) malloc(strlen(last_recv_msg)+1); strcpy(old_last_recv_msg,last_recv_msg); } // usage of last_ keywords last_recv_msg = (char *) realloc(last_recv_msg, strlen(P_recv) + 1); strcpy(last_recv_msg, P_recv); WARNING("Automatic response mode for an out of call message"); sendBuffer(createSendingMessage( (char*)"SIP/2.0 200 OK\n" "[last_Via:]\n" "[last_Call-ID:]\n" "[last_To:]\n" "[last_From:]\n" "[last_CSeq:]\n" "Contact: sip:sipp@[local_ip]:[local_port]\n" "Content-Length: 0\n\n" , -1)) ; // restore previous last msg if (last_recv_msg_saved == true) { last_recv_msg = (char *) realloc(last_recv_msg, strlen(old_last_recv_msg) + 1); strcpy(last_recv_msg, old_last_recv_msg); if (old_last_recv_msg != NULL) { free(old_last_recv_msg); old_last_recv_msg = NULL; } } CStat::instance()->computeStat(CStat::E_AUTO_ANSWERED); return true; default: ERROR_P1("Internal error for automaticResponseMode - mode %d is not implemented!", P_case); break ; } return false; } #ifdef PCAPPLAY void *send_wrapper(void *arg) { play_args_t *s = (play_args_t *) arg; //struct sched_param param; //int ret; //param.sched_priority = 10; //ret = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); //if(ret) // ERROR("Can't set RTP play thread realtime parameters"); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); send_packets(s); pthread_exit(NULL); return NULL; } #endif