/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2012, Anthony Minessale II * * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * * The Initial Developer of the Original Code is * Anthony Minessale II * Portions created by the Initial Developer are Copyright (C) * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Anthony Minessale II * Andrew Thompson * Rob Charlton * Karl Anderson * * Original from mod_erlang_event. * ei_helpers.c -- helper functions for ei * */ #include "mod_kazoo.h" /* Stolen from code added to ei in R12B-5. * Since not everyone has this version yet; * provide our own version. * */ #define put8(s,n) do { \ (s)[0] = (char)((n) & 0xff); \ (s) += 1; \ } while (0) #define put32be(s,n) do { \ (s)[0] = ((n) >> 24) & 0xff; \ (s)[1] = ((n) >> 16) & 0xff; \ (s)[2] = ((n) >> 8) & 0xff; \ (s)[3] = (n) & 0xff; \ (s) += 4; \ } while (0) #ifdef EI_DEBUG static void ei_x_print_reg_msg(ei_x_buff *buf, char *dest, int send) { char *mbuf = NULL; int i = 1; ei_s_print_term(&mbuf, buf->buff, &i); if (send) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Encoded term %s to '%s'\n", mbuf, dest); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Decoded term %s for '%s'\n", mbuf, dest); } free(mbuf); } static void ei_x_print_msg(ei_x_buff *buf, erlang_pid *pid, int send) { char *pbuf = NULL; int i = 0; ei_x_buff pidbuf; ei_x_new(&pidbuf); ei_x_encode_pid(&pidbuf, pid); ei_s_print_term(&pbuf, pidbuf.buff, &i); ei_x_print_reg_msg(buf, pbuf, send); free(pbuf); } #endif void ei_encode_switch_event_headers(ei_x_buff *ebuf, switch_event_t *event) { ei_encode_switch_event_headers_2(ebuf, event, 1); } void ei_encode_switch_event_headers_2(ei_x_buff *ebuf, switch_event_t *event, int encode) { switch_event_header_t *hp; char *uuid = switch_event_get_header(event, "unique-id"); int i; for (i = 0, hp = event->headers; hp; hp = hp->next, i++); if (event->body) i++; ei_x_encode_list_header(ebuf, i + 1); if (uuid) { char *unique_id = switch_event_get_header(event, "unique-id"); ei_x_encode_binary(ebuf, unique_id, strlen(unique_id)); } else { ei_x_encode_atom(ebuf, "undefined"); } for (hp = event->headers; hp; hp = hp->next) { ei_x_encode_tuple_header(ebuf, 2); ei_x_encode_binary(ebuf, hp->name, strlen(hp->name)); if(encode) { switch_url_decode(hp->value); } ei_x_encode_binary(ebuf, hp->value, strlen(hp->value)); } if (event->body) { ei_x_encode_tuple_header(ebuf, 2); ei_x_encode_binary(ebuf, "body", strlen("body")); ei_x_encode_binary(ebuf, event->body, strlen(event->body)); } ei_x_encode_empty_list(ebuf); } int ei_json_child_count(cJSON *JObj) { int mask = cJSON_False | cJSON_True | cJSON_NULL | cJSON_Number | cJSON_String | cJSON_Array | cJSON_Object | cJSON_Raw; cJSON *item = JObj->child; int i = 0; while(item) { if(item->type & mask) i++; item = item->next; } return i; } void ei_encode_json_array(ei_x_buff *ebuf, cJSON *JObj) { cJSON *item; int count = ei_json_child_count(JObj); ei_x_encode_list_header(ebuf, count); if(count == 0) return; item = JObj->child; while(item) { switch(item->type) { case cJSON_String: ei_x_encode_binary(ebuf, item->valuestring, strlen(item->valuestring)); break; case cJSON_Number: ei_x_encode_double(ebuf, item->valuedouble); break; case cJSON_True: ei_x_encode_boolean(ebuf, 1); break; case cJSON_False: ei_x_encode_boolean(ebuf, 0); break; case cJSON_Object: ei_encode_json(ebuf, item); break; case cJSON_Array: ei_encode_json_array(ebuf, item); break; case cJSON_Raw: { cJSON *Decoded = cJSON_Parse(item->valuestring); if(!Decoded) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR DECODING RAW JSON %s\n", item->valuestring); ei_x_encode_tuple_header(ebuf, 0); } else { ei_encode_json(ebuf, Decoded); cJSON_Delete(Decoded); } break; } case cJSON_NULL: ei_x_encode_atom(ebuf, "null"); break; default: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NOT ENCODED %i\n", item->type); break; } item = item->next; } ei_x_encode_empty_list(ebuf); } void ei_encode_json(ei_x_buff *ebuf, cJSON *JObj) { cJSON *item; int count = ei_json_child_count(JObj); if(kazoo_globals.json_encoding == ERLANG_TUPLE) { ei_x_encode_tuple_header(ebuf, 1); ei_x_encode_list_header(ebuf, count); } else { ei_x_encode_map_header(ebuf, count); } if(count == 0) return; item = JObj->child; while(item) { if(kazoo_globals.json_encoding == ERLANG_TUPLE) { ei_x_encode_tuple_header(ebuf, 2); } ei_x_encode_binary(ebuf, item->string, strlen(item->string)); switch(item->type) { case cJSON_String: ei_x_encode_binary(ebuf, item->valuestring, strlen(item->valuestring)); break; case cJSON_Number: if ((fabs(((double)item->valueint) - item->valuedouble) <= DBL_EPSILON) && (item->valuedouble <= INT_MAX) && (item->valuedouble >= INT_MIN)) { ei_x_encode_longlong(ebuf, item->valueint); } else { ei_x_encode_double(ebuf, item->valuedouble); } break; case cJSON_True: ei_x_encode_boolean(ebuf, 1); break; case cJSON_False: ei_x_encode_boolean(ebuf, 0); break; case cJSON_Object: ei_encode_json(ebuf, item); break; case cJSON_Array: ei_encode_json_array(ebuf, item); break; case cJSON_Raw: { cJSON *Decoded = cJSON_Parse(item->valuestring); if(!Decoded) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR DECODING RAW JSON %s\n", item->valuestring); ei_x_encode_tuple_header(ebuf, 0); } else { ei_encode_json(ebuf, Decoded); cJSON_Delete(Decoded); } break; } case cJSON_NULL: ei_x_encode_atom(ebuf, "null"); break; default: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NOT ENCODED %i\n", item->type); break; } item = item->next; } if(kazoo_globals.json_encoding == ERLANG_TUPLE) { ei_x_encode_empty_list(ebuf); } } void close_socket(switch_socket_t ** sock) { if (*sock) { switch_socket_shutdown(*sock, SWITCH_SHUTDOWN_READWRITE); switch_socket_close(*sock); *sock = NULL; } } void close_socketfd(int *sockfd) { if (*sockfd) { shutdown(*sockfd, SHUT_RDWR); close(*sockfd); } } switch_socket_t *create_socket_with_port(switch_memory_pool_t *pool, switch_port_t port) { switch_sockaddr_t *sa; switch_socket_t *socket; if(switch_sockaddr_info_get(&sa, kazoo_globals.ip, SWITCH_UNSPEC, port, 0, pool)) { return NULL; } if (switch_socket_create(&socket, switch_sockaddr_get_family(sa), SOCK_STREAM, SWITCH_PROTO_TCP, pool)) { return NULL; } if (switch_socket_opt_set(socket, SWITCH_SO_REUSEADDR, 1)) { return NULL; } if (switch_socket_bind(socket, sa)) { return NULL; } if (switch_socket_listen(socket, 5)){ return NULL; } if (kazoo_globals.nat_map && switch_nat_get_type()) { switch_nat_add_mapping(port, SWITCH_NAT_TCP, NULL, SWITCH_FALSE); } return socket; } switch_socket_t *create_socket(switch_memory_pool_t *pool) { return create_socket_with_port(pool, 0); } switch_status_t create_ei_cnode(const char *ip_addr, const char *name, struct ei_cnode_s *ei_cnode) { char hostname[EI_MAXHOSTNAMELEN + 1]; char nodename[MAXNODELEN + 1]; char cnodename[EI_MAXALIVELEN + 1]; char *atsign; /* copy the erlang interface nodename into something we can modify */ strncpy(cnodename, name, EI_MAXALIVELEN); if ((atsign = strchr(cnodename, '@'))) { /* we got a qualified node name, don't guess the host/domain */ snprintf(nodename, MAXNODELEN + 1, "%s", name); /* truncate the alivename at the @ */ *atsign++ = '\0'; strncpy(hostname, atsign, EI_MAXHOSTNAMELEN); } else { strncpy(hostname, kazoo_globals.hostname, EI_MAXHOSTNAMELEN); snprintf(nodename, MAXNODELEN + 1, "%s@%s", name, hostname); } if (kazoo_globals.ei_shortname) { char *off; if ((off = strchr(nodename, '.'))) { *off = '\0'; } } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "creating nodename: %s\n", nodename); /* init the ec stuff */ if (ei_connect_xinit(ei_cnode, hostname, cnodename, nodename, (Erl_IpAddr) ip_addr, kazoo_globals.ei_cookie, 0) < 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to initialize the erlang interface connection structure\n"); return SWITCH_STATUS_FALSE; } return SWITCH_STATUS_SUCCESS; } switch_status_t ei_compare_pids(const erlang_pid *pid1, const erlang_pid *pid2) { if ((!strcmp(pid1->node, pid2->node)) && pid1->creation == pid2->creation && pid1->num == pid2->num && pid1->serial == pid2->serial) { return SWITCH_STATUS_SUCCESS; } else { return SWITCH_STATUS_FALSE; } } void ei_link(ei_node_t *ei_node, erlang_pid * from, erlang_pid * to) { char msgbuf[2048]; char *s; int index = 0; index = 5; /* max sizes: */ ei_encode_version(msgbuf, &index); /* 1 */ ei_encode_tuple_header(msgbuf, &index, 3); ei_encode_long(msgbuf, &index, ERL_LINK); ei_encode_pid(msgbuf, &index, from); /* 268 */ ei_encode_pid(msgbuf, &index, to); /* 268 */ /* 5 byte header missing */ s = msgbuf; put32be(s, index - 4); /* 4 */ put8(s, ERL_PASS_THROUGH); /* 1 */ /* sum: 542 */ if (write(ei_node->nodefd, msgbuf, index) == -1) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to link to process on %s\n", ei_node->peer_nodename); } } void ei_encode_switch_event(ei_x_buff *ebuf, switch_event_t *event) { ei_x_encode_tuple_header(ebuf, 2); ei_x_encode_atom(ebuf, "event"); ei_encode_switch_event_headers(ebuf, event); } int ei_helper_send(ei_node_t *ei_node, erlang_pid *to, ei_x_buff *buf) { int ret = 0; if (ei_node->nodefd) { #ifdef EI_DEBUG ei_x_print_msg(buf, to, 1); #endif ret = ei_send(ei_node->nodefd, to, buf->buff, buf->index); } return ret; } int ei_decode_atom_safe(char *buf, int *index, char *dst) { int type, size; ei_get_type(buf, index, &type, &size); if (type != ERL_ATOM_EXT) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed atom\n", type, size); return -1; } else if (size > MAXATOMLEN) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Requested decoding of atom with size %d into a buffer of size %d\n", size, MAXATOMLEN); return -1; } else { return ei_decode_atom(buf, index, dst); } } int ei_decode_string_or_binary(char *buf, int *index, char **dst) { int type, size, res; long len; ei_get_type(buf, index, &type, &size); if (type != ERL_STRING_EXT && type != ERL_BINARY_EXT && type != ERL_LIST_EXT && type != ERL_NIL_EXT) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed binary or string\n", type, size); return -1; } *dst = malloc(size + 1); if (type == ERL_NIL_EXT) { res = 0; **dst = '\0'; } else if (type == ERL_BINARY_EXT) { res = ei_decode_binary(buf, index, *dst, &len); (*dst)[len] = '\0'; } else { res = ei_decode_string(buf, index, *dst); } return res; } int ei_decode_string_or_binary_limited(char *buf, int *index, int maxsize, char *dst) { int type, size, res; long len; ei_get_type(buf, index, &type, &size); if (type != ERL_STRING_EXT && type != ERL_BINARY_EXT && type != ERL_LIST_EXT && type != ERL_NIL_EXT) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed binary or string\n", type, size); return -1; } if (size > maxsize) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Requested decoding of %s with size %d into a buffer of size %d\n", type == ERL_BINARY_EXT ? "binary" : "string", size, maxsize); return -1; } if (type == ERL_NIL_EXT) { res = 0; *dst = '\0'; } else if (type == ERL_BINARY_EXT) { res = ei_decode_binary(buf, index, dst, &len); dst[len] = '\0'; /* binaries aren't null terminated */ } else { res = ei_decode_string(buf, index, dst); } return res; } switch_status_t create_acceptor() { switch_sockaddr_t *sa; uint16_t port; char ipbuf[48]; const char *ip_addr; #if (ERLANG_MAJOR == 10 && ERLANG_MINOR >= 3) || ERLANG_MAJOR >= 11 ei_init(); #endif /* if the config has specified an erlang release compatibility then pass that along to the erlang interface */ if (kazoo_globals.ei_compat_rel) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Compatability with OTP R%d requested\n", kazoo_globals.ei_compat_rel); ei_set_compat_rel(kazoo_globals.ei_compat_rel); } if (!(kazoo_globals.acceptor = create_socket_with_port(kazoo_globals.pool, kazoo_globals.port))) { return SWITCH_STATUS_SOCKERR; } switch_socket_addr_get(&sa, SWITCH_FALSE, kazoo_globals.acceptor); port = switch_sockaddr_get_port(sa); ip_addr = switch_get_addr(ipbuf, sizeof (ipbuf), sa); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Erlang connection acceptor listening on %s:%u\n", ip_addr, port); /* try to initialize the erlang interface */ if (create_ei_cnode(ip_addr, kazoo_globals.ei_nodename, &kazoo_globals.ei_cnode) != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_SOCKERR; } /* tell the erlang port manager where we can be reached. this returns a file descriptor pointing to epmd or -1 */ if ((kazoo_globals.epmdfd = ei_publish(&kazoo_globals.ei_cnode, port)) == -1) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to publish port to epmd, trying to start epmd via system()\n"); if (system("fs_epmd -daemon")) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to start epmd manually! Is epmd in $PATH? If not, start it yourself or run an erl shell with -sname or -name\n"); return SWITCH_STATUS_SOCKERR; } switch_yield(100000); if ((kazoo_globals.epmdfd = ei_publish(&kazoo_globals.ei_cnode, port)) == -1) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to publish port to epmd AGAIN\n"); return SWITCH_STATUS_SOCKERR; } } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connected to epmd and published erlang cnode name %s at port %d\n", kazoo_globals.ei_cnode.thisnodename, port); return SWITCH_STATUS_SUCCESS; } switch_hash_t *create_default_filter() { switch_hash_t *filter; switch_core_hash_init(&filter); switch_core_hash_insert(filter, "Acquired-UUID", "1"); switch_core_hash_insert(filter, "action", "1"); switch_core_hash_insert(filter, "Action", "1"); switch_core_hash_insert(filter, "alt_event_type", "1"); switch_core_hash_insert(filter, "Answer-State", "1"); switch_core_hash_insert(filter, "Application", "1"); switch_core_hash_insert(filter, "Application-Data", "1"); switch_core_hash_insert(filter, "Application-Name", "1"); switch_core_hash_insert(filter, "Application-Response", "1"); switch_core_hash_insert(filter, "att_xfer_replaced_by", "1"); switch_core_hash_insert(filter, "Auth-Method", "1"); switch_core_hash_insert(filter, "Auth-Realm", "1"); switch_core_hash_insert(filter, "Auth-User", "1"); switch_core_hash_insert(filter, "Bridge-A-Unique-ID", "1"); switch_core_hash_insert(filter, "Bridge-B-Unique-ID", "1"); switch_core_hash_insert(filter, "Call-Direction", "1"); switch_core_hash_insert(filter, "Caller-Callee-ID-Name", "1"); switch_core_hash_insert(filter, "Caller-Callee-ID-Number", "1"); switch_core_hash_insert(filter, "Caller-Caller-ID-Name", "1"); switch_core_hash_insert(filter, "Caller-Caller-ID-Number", "1"); switch_core_hash_insert(filter, "Caller-Screen-Bit", "1"); switch_core_hash_insert(filter, "Caller-Privacy-Hide-Name", "1"); switch_core_hash_insert(filter, "Caller-Privacy-Hide-Number", "1"); switch_core_hash_insert(filter, "Caller-Context", "1"); switch_core_hash_insert(filter, "Caller-Controls", "1"); switch_core_hash_insert(filter, "Caller-Destination-Number", "1"); switch_core_hash_insert(filter, "Caller-Dialplan", "1"); switch_core_hash_insert(filter, "Caller-Network-Addr", "1"); switch_core_hash_insert(filter, "Caller-Unique-ID", "1"); switch_core_hash_insert(filter, "Call-ID", "1"); switch_core_hash_insert(filter, "Channel-Call-State", "1"); switch_core_hash_insert(filter, "Channel-Call-UUID", "1"); switch_core_hash_insert(filter, "Channel-Presence-ID", "1"); switch_core_hash_insert(filter, "Channel-State", "1"); switch_core_hash_insert(filter, "Chat-Permissions", "1"); switch_core_hash_insert(filter, "Conference-Name", "1"); switch_core_hash_insert(filter, "Conference-Profile-Name", "1"); switch_core_hash_insert(filter, "Conference-Unique-ID", "1"); switch_core_hash_insert(filter, "contact", "1"); switch_core_hash_insert(filter, "Detected-Tone", "1"); switch_core_hash_insert(filter, "dialog_state", "1"); switch_core_hash_insert(filter, "direction", "1"); switch_core_hash_insert(filter, "Distributed-From", "1"); switch_core_hash_insert(filter, "DTMF-Digit", "1"); switch_core_hash_insert(filter, "DTMF-Duration", "1"); switch_core_hash_insert(filter, "Event-Date-Timestamp", "1"); switch_core_hash_insert(filter, "Event-Name", "1"); switch_core_hash_insert(filter, "Event-Subclass", "1"); switch_core_hash_insert(filter, "expires", "1"); switch_core_hash_insert(filter, "Expires", "1"); switch_core_hash_insert(filter, "Ext-SIP-IP", "1"); switch_core_hash_insert(filter, "File", "1"); switch_core_hash_insert(filter, "FreeSWITCH-Hostname", "1"); switch_core_hash_insert(filter, "from", "1"); switch_core_hash_insert(filter, "Hunt-Destination-Number", "1"); switch_core_hash_insert(filter, "ip", "1"); switch_core_hash_insert(filter, "Message-Account", "1"); switch_core_hash_insert(filter, "metadata", "1"); switch_core_hash_insert(filter, "old_node_channel_uuid", "1"); switch_core_hash_insert(filter, "Other-Leg-Callee-ID-Name", "1"); switch_core_hash_insert(filter, "Other-Leg-Callee-ID-Number", "1"); switch_core_hash_insert(filter, "Other-Leg-Caller-ID-Name", "1"); switch_core_hash_insert(filter, "Other-Leg-Caller-ID-Number", "1"); switch_core_hash_insert(filter, "Other-Leg-Destination-Number", "1"); switch_core_hash_insert(filter, "Other-Leg-Direction", "1"); switch_core_hash_insert(filter, "Other-Leg-Unique-ID", "1"); switch_core_hash_insert(filter, "Other-Leg-Channel-Name", "1"); switch_core_hash_insert(filter, "Participant-Type", "1"); switch_core_hash_insert(filter, "Path", "1"); switch_core_hash_insert(filter, "profile_name", "1"); switch_core_hash_insert(filter, "Profiles", "1"); switch_core_hash_insert(filter, "proto-specific-event-name", "1"); switch_core_hash_insert(filter, "Raw-Application-Data", "1"); switch_core_hash_insert(filter, "realm", "1"); switch_core_hash_insert(filter, "Resigning-UUID", "1"); switch_core_hash_insert(filter, "set", "1"); switch_core_hash_insert(filter, "sip_auto_answer", "1"); switch_core_hash_insert(filter, "sip_auth_method", "1"); switch_core_hash_insert(filter, "sip_from_host", "1"); switch_core_hash_insert(filter, "sip_from_user", "1"); switch_core_hash_insert(filter, "sip_to_host", "1"); switch_core_hash_insert(filter, "sip_to_user", "1"); switch_core_hash_insert(filter, "sub-call-id", "1"); switch_core_hash_insert(filter, "technology", "1"); switch_core_hash_insert(filter, "to", "1"); switch_core_hash_insert(filter, "Unique-ID", "1"); switch_core_hash_insert(filter, "URL", "1"); switch_core_hash_insert(filter, "username", "1"); switch_core_hash_insert(filter, "variable_channel_is_moving", "1"); switch_core_hash_insert(filter, "variable_collected_digits", "1"); switch_core_hash_insert(filter, "variable_current_application", "1"); switch_core_hash_insert(filter, "variable_current_application_data", "1"); switch_core_hash_insert(filter, "variable_domain_name", "1"); switch_core_hash_insert(filter, "variable_effective_caller_id_name", "1"); switch_core_hash_insert(filter, "variable_effective_caller_id_number", "1"); switch_core_hash_insert(filter, "variable_holding_uuid", "1"); switch_core_hash_insert(filter, "variable_hold_music", "1"); switch_core_hash_insert(filter, "variable_media_group_id", "1"); switch_core_hash_insert(filter, "variable_originate_disposition", "1"); switch_core_hash_insert(filter, "variable_origination_uuid", "1"); switch_core_hash_insert(filter, "variable_playback_terminator_used", "1"); switch_core_hash_insert(filter, "variable_presence_id", "1"); switch_core_hash_insert(filter, "variable_record_ms", "1"); switch_core_hash_insert(filter, "variable_recovered", "1"); switch_core_hash_insert(filter, "variable_silence_hits_exhausted", "1"); switch_core_hash_insert(filter, "variable_sip_auth_realm", "1"); switch_core_hash_insert(filter, "variable_sip_from_host", "1"); switch_core_hash_insert(filter, "variable_sip_from_user", "1"); switch_core_hash_insert(filter, "variable_sip_from_tag", "1"); switch_core_hash_insert(filter, "variable_sip_h_X-AUTH-IP", "1"); switch_core_hash_insert(filter, "variable_sip_received_ip", "1"); switch_core_hash_insert(filter, "variable_sip_to_host", "1"); switch_core_hash_insert(filter, "variable_sip_to_user", "1"); switch_core_hash_insert(filter, "variable_sip_to_tag", "1"); switch_core_hash_insert(filter, "variable_sofia_profile_name", "1"); switch_core_hash_insert(filter, "variable_transfer_history", "1"); switch_core_hash_insert(filter, "variable_user_name", "1"); switch_core_hash_insert(filter, "variable_endpoint_disposition", "1"); switch_core_hash_insert(filter, "variable_originate_disposition", "1"); switch_core_hash_insert(filter, "variable_bridge_hangup_cause", "1"); switch_core_hash_insert(filter, "variable_hangup_cause", "1"); switch_core_hash_insert(filter, "variable_last_bridge_proto_specific_hangup_cause", "1"); switch_core_hash_insert(filter, "variable_proto_specific_hangup_cause", "1"); switch_core_hash_insert(filter, "VM-Call-ID", "1"); switch_core_hash_insert(filter, "VM-sub-call-id", "1"); switch_core_hash_insert(filter, "whistle_application_name", "1"); switch_core_hash_insert(filter, "whistle_application_response", "1"); switch_core_hash_insert(filter, "whistle_event_name", "1"); switch_core_hash_insert(filter, "kazoo_application_name", "1"); switch_core_hash_insert(filter, "kazoo_application_response", "1"); switch_core_hash_insert(filter, "kazoo_event_name", "1"); switch_core_hash_insert(filter, "sip_auto_answer_notify", "1"); switch_core_hash_insert(filter, "eavesdrop_group", "1"); switch_core_hash_insert(filter, "origination_caller_id_name", "1"); switch_core_hash_insert(filter, "origination_caller_id_number", "1"); switch_core_hash_insert(filter, "origination_callee_id_name", "1"); switch_core_hash_insert(filter, "origination_callee_id_number", "1"); switch_core_hash_insert(filter, "sip_auth_username", "1"); switch_core_hash_insert(filter, "sip_auth_password", "1"); switch_core_hash_insert(filter, "effective_caller_id_name", "1"); switch_core_hash_insert(filter, "effective_caller_id_number", "1"); switch_core_hash_insert(filter, "effective_callee_id_name", "1"); switch_core_hash_insert(filter, "effective_callee_id_number", "1"); switch_core_hash_insert(filter, "variable_destination_number", "1"); switch_core_hash_insert(filter, "variable_effective_callee_id_name", "1"); switch_core_hash_insert(filter, "variable_effective_callee_id_number", "1"); switch_core_hash_insert(filter, "variable_record_silence_hits", "1"); switch_core_hash_insert(filter, "variable_refer_uuid", "1"); switch_core_hash_insert(filter, "variable_sip_call_id", "1"); switch_core_hash_insert(filter, "variable_sip_h_Referred-By", "1"); switch_core_hash_insert(filter, "variable_sip_h_X-AUTH-PORT", "1"); switch_core_hash_insert(filter, "variable_sip_loopback_req_uri", "1"); switch_core_hash_insert(filter, "variable_sip_received_port", "1"); switch_core_hash_insert(filter, "variable_sip_refer_to", "1"); switch_core_hash_insert(filter, "variable_sip_req_host", "1"); switch_core_hash_insert(filter, "variable_sip_req_uri", "1"); switch_core_hash_insert(filter, "variable_transfer_source", "1"); switch_core_hash_insert(filter, "variable_uuid", "1"); /* Registration headers */ switch_core_hash_insert(filter, "call-id", "1"); switch_core_hash_insert(filter, "profile-name", "1"); switch_core_hash_insert(filter, "from-user", "1"); switch_core_hash_insert(filter, "from-host", "1"); switch_core_hash_insert(filter, "presence-hosts", "1"); switch_core_hash_insert(filter, "contact", "1"); switch_core_hash_insert(filter, "rpid", "1"); switch_core_hash_insert(filter, "status", "1"); switch_core_hash_insert(filter, "expires", "1"); switch_core_hash_insert(filter, "to-user", "1"); switch_core_hash_insert(filter, "to-host", "1"); switch_core_hash_insert(filter, "network-ip", "1"); switch_core_hash_insert(filter, "network-port", "1"); switch_core_hash_insert(filter, "username", "1"); switch_core_hash_insert(filter, "realm", "1"); switch_core_hash_insert(filter, "user-agent", "1"); switch_core_hash_insert(filter, "Hangup-Cause", "1"); switch_core_hash_insert(filter, "Unique-ID", "1"); switch_core_hash_insert(filter, "variable_switch_r_sdp", "1"); switch_core_hash_insert(filter, "variable_rtp_local_sdp_str", "1"); switch_core_hash_insert(filter, "variable_sip_to_uri", "1"); switch_core_hash_insert(filter, "variable_sip_from_uri", "1"); switch_core_hash_insert(filter, "variable_sip_user_agent", "1"); switch_core_hash_insert(filter, "variable_duration", "1"); switch_core_hash_insert(filter, "variable_billsec", "1"); switch_core_hash_insert(filter, "variable_billmsec", "1"); switch_core_hash_insert(filter, "variable_progresssec", "1"); switch_core_hash_insert(filter, "variable_progress_uepoch", "1"); switch_core_hash_insert(filter, "variable_progress_media_uepoch", "1"); switch_core_hash_insert(filter, "variable_start_uepoch", "1"); switch_core_hash_insert(filter, "variable_digits_dialed", "1"); switch_core_hash_insert(filter, "Member-ID", "1"); switch_core_hash_insert(filter, "Floor", "1"); switch_core_hash_insert(filter, "Video", "1"); switch_core_hash_insert(filter, "Hear", "1"); switch_core_hash_insert(filter, "Speak", "1"); switch_core_hash_insert(filter, "Talking", "1"); switch_core_hash_insert(filter, "Current-Energy", "1"); switch_core_hash_insert(filter, "Energy-Level", "1"); switch_core_hash_insert(filter, "Mute-Detect", "1"); /* RTMP headers */ switch_core_hash_insert(filter, "RTMP-Session-ID", "1"); switch_core_hash_insert(filter, "RTMP-Profile", "1"); switch_core_hash_insert(filter, "RTMP-Flash-Version", "1"); switch_core_hash_insert(filter, "RTMP-SWF-URL", "1"); switch_core_hash_insert(filter, "RTMP-TC-URL", "1"); switch_core_hash_insert(filter, "RTMP-Page-URL", "1"); switch_core_hash_insert(filter, "User", "1"); switch_core_hash_insert(filter, "Domain", "1"); /* Fax headers */ switch_core_hash_insert(filter, "variable_fax_bad_rows", "1"); switch_core_hash_insert(filter, "variable_fax_document_total_pages", "1"); switch_core_hash_insert(filter, "variable_fax_document_transferred_pages", "1"); switch_core_hash_insert(filter, "variable_fax_ecm_used", "1"); switch_core_hash_insert(filter, "variable_fax_result_code", "1"); switch_core_hash_insert(filter, "variable_fax_result_text", "1"); switch_core_hash_insert(filter, "variable_fax_success", "1"); switch_core_hash_insert(filter, "variable_fax_transfer_rate", "1"); switch_core_hash_insert(filter, "variable_fax_local_station_id", "1"); switch_core_hash_insert(filter, "variable_fax_remote_station_id", "1"); switch_core_hash_insert(filter, "variable_fax_remote_country", "1"); switch_core_hash_insert(filter, "variable_fax_remote_vendor", "1"); switch_core_hash_insert(filter, "variable_fax_remote_model", "1"); switch_core_hash_insert(filter, "variable_fax_image_resolution", "1"); switch_core_hash_insert(filter, "variable_fax_file_image_resolution", "1"); switch_core_hash_insert(filter, "variable_fax_image_size", "1"); switch_core_hash_insert(filter, "variable_fax_image_pixel_size", "1"); switch_core_hash_insert(filter, "variable_fax_file_image_pixel_size", "1"); switch_core_hash_insert(filter, "variable_fax_longest_bad_row_run", "1"); switch_core_hash_insert(filter, "variable_fax_encoding", "1"); switch_core_hash_insert(filter, "variable_fax_encoding_name", "1"); switch_core_hash_insert(filter, "variable_fax_header", "1"); switch_core_hash_insert(filter, "variable_fax_ident", "1"); switch_core_hash_insert(filter, "variable_fax_timezone", "1"); switch_core_hash_insert(filter, "variable_fax_doc_id", "1"); switch_core_hash_insert(filter, "variable_fax_doc_database", "1"); switch_core_hash_insert(filter, "variable_has_t38", "1"); /* Secure headers */ switch_core_hash_insert(filter, "variable_sdp_secure_savp_only", "1"); switch_core_hash_insert(filter, "variable_rtp_has_crypto", "1"); switch_core_hash_insert(filter, "variable_rtp_secure_media", "1"); switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed", "1"); switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed_audio", "1"); switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed_video", "1"); switch_core_hash_insert(filter, "variable_zrtp_secure_media", "1"); switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed", "1"); switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed_audio", "1"); switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed_video", "1"); switch_core_hash_insert(filter, "sdp_secure_savp_only", "1"); switch_core_hash_insert(filter, "rtp_has_crypto", "1"); switch_core_hash_insert(filter, "rtp_secure_media", "1"); switch_core_hash_insert(filter, "rtp_secure_media_confirmed", "1"); switch_core_hash_insert(filter, "rtp_secure_media_confirmed_audio", "1"); switch_core_hash_insert(filter, "rtp_secure_media_confirmed_video", "1"); switch_core_hash_insert(filter, "zrtp_secure_media", "1"); switch_core_hash_insert(filter, "zrtp_secure_media_confirmed", "1"); switch_core_hash_insert(filter, "zrtp_secure_media_confirmed_audio", "1"); switch_core_hash_insert(filter, "zrtp_secure_media_confirmed_video", "1"); /* Device Redirect headers */ switch_core_hash_insert(filter, "variable_last_bridge_hangup_cause", "1"); switch_core_hash_insert(filter, "variable_sip_redirected_by", "1"); switch_core_hash_insert(filter, "intercepted_by", "1"); switch_core_hash_insert(filter, "variable_bridge_uuid", "1"); switch_core_hash_insert(filter, "Record-File-Path", "1"); /* Loopback headers */ switch_core_hash_insert(filter, "variable_loopback_bowout_on_execute", "1"); switch_core_hash_insert(filter, "variable_loopback_bowout", "1"); switch_core_hash_insert(filter, "variable_other_loopback_leg_uuid", "1"); switch_core_hash_insert(filter, "variable_loopback_leg", "1"); switch_core_hash_insert(filter, "variable_is_loopback", "1"); // SMS switch_core_hash_insert(filter, "Message-ID", "1"); switch_core_hash_insert(filter, "Delivery-Failure", "1"); switch_core_hash_insert(filter, "Delivery-Result-Code", "1"); return filter; } static void fetch_config_filters(switch_memory_pool_t *pool) { char *cf = "kazoo.conf"; switch_xml_t cfg, xml, child, param; switch_event_t *params; switch_event_create(¶ms, SWITCH_EVENT_REQUEST_PARAMS); switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "Action", "request-filter"); if (!(xml = switch_xml_open_cfg(cf, &cfg, params))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open configuration file %s\n", cf); } else { if ((child = switch_xml_child(cfg, "event-filter"))) { switch_hash_t *filter; switch_hash_t *old_filter; switch_core_hash_init(&filter); for (param = switch_xml_child(child, "header"); param; param = param->next) { char *var = (char *) switch_xml_attr_soft(param, "name"); switch_core_hash_insert(filter, var, "1"); } old_filter = kazoo_globals.event_filter; kazoo_globals.event_filter = filter; if (old_filter) { switch_core_hash_destroy(&old_filter); } } kazoo_globals.config_fetched = 1; switch_xml_free(xml); } } static void fetch_config_handlers(switch_memory_pool_t *pool) { char *cf = "kazoo.conf"; switch_xml_t cfg, xml; switch_event_t *params; switch_event_create(¶ms, SWITCH_EVENT_REQUEST_PARAMS); switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "Action", "request-handlers"); if (!(xml = switch_xml_open_cfg(cf, &cfg, params))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open configuration file %s\n", cf); } else { kazoo_config_handlers(cfg); kazoo_globals.config_fetched = 1; switch_xml_free(xml); } } static void *SWITCH_THREAD_FUNC fetch_config_exec(switch_thread_t *thread, void *obj) { switch_memory_pool_t *pool = (switch_memory_pool_t *)obj; fetch_config_filters(pool); fetch_config_handlers(pool); kazoo_globals.config_fetched = 1; return NULL; } void fetch_config() { switch_memory_pool_t *pool; switch_thread_t *thread; switch_threadattr_t *thd_attr = NULL; switch_uuid_t uuid; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "fetching kazoo config\n"); switch_core_new_memory_pool(&pool); switch_threadattr_create(&thd_attr, pool); switch_threadattr_detach_set(thd_attr, 1); switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); switch_uuid_get(&uuid); switch_thread_create(&thread, thd_attr, fetch_config_exec, pool, pool); } SWITCH_MODULE_RUNTIME_FUNCTION(mod_kazoo_runtime) { switch_os_socket_t os_socket; if(create_acceptor() != SWITCH_STATUS_SUCCESS) { // TODO: what would we need to clean up here switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to create erlang connection acceptor!\n"); close_socket(&kazoo_globals.acceptor); return SWITCH_STATUS_TERM; } switch_atomic_inc(&kazoo_globals.threads); switch_os_sock_get(&os_socket, kazoo_globals.acceptor); while (switch_test_flag(&kazoo_globals, LFLAG_RUNNING)) { int nodefd; ErlConnect conn; /* zero out errno because ei_accept doesn't differentiate between a */ /* failed authentication or a socket failure, or a client version */ /* mismatch or a godzilla attack (and a godzilla attack is highly likely) */ errno = 0; /* wait here for an erlang node to connect, timming out to check if our module is still running every now-and-again */ if ((nodefd = ei_accept_tmo(&kazoo_globals.ei_cnode, (int) os_socket, &conn, kazoo_globals.connection_timeout)) == ERL_ERROR) { if (erl_errno == ETIMEDOUT) { continue; } else if (errno) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Erlang connection acceptor socket error %d %d\n", erl_errno, errno); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Erlang node connection failed - ensure your cookie matches '%s' and you are using a good nodename\n", kazoo_globals.ei_cookie); } continue; } if (!switch_test_flag(&kazoo_globals, LFLAG_RUNNING)) { break; } /* NEW ERLANG NODE CONNECTION! Hello friend! */ new_kazoo_node(nodefd, &conn); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Erlang connection acceptor shut down\n"); switch_atomic_dec(&kazoo_globals.threads); return SWITCH_STATUS_TERM; } /* For Emacs: * Local Variables: * mode:c * indent-tabs-mode:t * tab-width:4 * c-basic-offset:4 * End: * For VIM: * vim:set softtabstop=4 shiftwidth=4 tabstop=4: */