From 7dd872e9b87cc5114e7845f6cd7d45ae53b9aac6 Mon Sep 17 00:00:00 2001 From: Seven Du Date: Tue, 19 Jul 2016 00:48:43 +0800 Subject: [PATCH] FS-9575 #resolve [Add MRCP] --- Makefile.am | 2 + conf/vanilla/autoload_configs/msrp.conf.xml | 10 + src/include/switch_core_media.h | 5 +- src/include/switch_msrp.h | 146 ++ src/include/switch_types.h | 2 + .../applications/mod_commands/mod_commands.c | 2 + src/mod/endpoints/mod_sofia/mod_sofia.c | 17 + src/mod/endpoints/mod_sofia/sofia_glue.c | 1 + src/switch_core.c | 4 + src/switch_core_media.c | 246 ++- src/switch_msrp.c | 1569 +++++++++++++++++ 11 files changed, 1988 insertions(+), 16 deletions(-) create mode 100644 conf/vanilla/autoload_configs/msrp.conf.xml create mode 100644 src/include/switch_msrp.h create mode 100644 src/switch_msrp.c diff --git a/Makefile.am b/Makefile.am index ef5747e124..286cfe0536 100644 --- a/Makefile.am +++ b/Makefile.am @@ -296,6 +296,7 @@ library_include_HEADERS = \ src/include/switch_curl.h \ src/include/switch_json.h \ src/include/switch_utf8.h \ + src/include/switch_msrp.h \ src/include/switch_vpx.h \ libs/libteletone/src/libteletone_detect.h \ libs/libteletone/src/libteletone_generate.h \ @@ -376,6 +377,7 @@ libfreeswitch_la_SOURCES = \ src/switch_curl.c \ src/switch_hashtable.c\ src/switch_utf8.c \ + src/switch_msrp.c \ src/switch_vpx.c \ libs/libtpl-1.5/src/tpl.c \ libs/libteletone/src/libteletone_detect.c \ diff --git a/conf/vanilla/autoload_configs/msrp.conf.xml b/conf/vanilla/autoload_configs/msrp.conf.xml new file mode 100644 index 0000000000..ad5dbace8c --- /dev/null +++ b/conf/vanilla/autoload_configs/msrp.conf.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/include/switch_core_media.h b/src/include/switch_core_media.h index 5bca68d6c0..c85388509d 100644 --- a/src/include/switch_core_media.h +++ b/src/include/switch_core_media.h @@ -35,6 +35,7 @@ #include +#include SWITCH_BEGIN_EXTERN_C @@ -371,7 +372,9 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_text_factory_destroy(switch_rtp_text_ SWITCH_DECLARE(switch_status_t) switch_core_session_print(switch_core_session_t *session, const char *data); SWITCH_DECLARE(switch_status_t) switch_core_session_printf(switch_core_session_t *session, const char *fmt, ...); - + +SWITCH_DECLARE(switch_msrp_session_t *) switch_core_media_get_msrp_session(switch_core_session_t *session); + SWITCH_END_EXTERN_C #endif /* For Emacs: diff --git a/src/include/switch_msrp.h b/src/include/switch_msrp.h new file mode 100644 index 0000000000..8a6cf62dfb --- /dev/null +++ b/src/include/switch_msrp.h @@ -0,0 +1,146 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2011-2016, Seven Du + * + * 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 + * Seven Du + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * + * + * msrp.h -- MSRP lib + * + */ + +#ifndef _MSRP_H +#define _MSRP_H + +#include +#include + +#define MSRP_LISTEN_PORT 2855 +#define MSRP_SSL_LISTEN_PORT 2856 + +enum { + MSRP_ST_WAIT_HEADER, + MSRP_ST_PARSE_HEADER, + MSRP_ST_WAIT_BODY, + MSRP_ST_DONE, + MSRP_ST_ERROR, + + MSRP_METHOD_REPLY, + MSRP_METHOD_SEND, + MSRP_METHOD_AUTH, + MSRP_METHOD_REPORT, + +}; + +enum { + MSRP_H_FROM_PATH, + MSRP_H_TO_PATH, + MSRP_H_MESSAGE_ID, + MSRP_H_CONTENT_TYPE, + MSRP_H_SUCCESS_REPORT, + MSRP_H_FAILURE_REPORT, + MSRP_H_STATUS, + MSRP_H_KEEPALIVE, + MSRP_H_UNKNOWN +}; + +typedef struct msrp_msg_s { + int state; + int method; + char *headers[12]; + int last_header; + char *transaction_id; + char *delimiter; + int code_number; + char *code_description; + switch_size_t byte_start; + switch_size_t byte_end; + switch_size_t bytes; + switch_size_t payload_bytes; + int range_star; /* range-end is '*' */ + char *last_p; + char *payload; + struct msrp_msg_s *next; +} msrp_msg_t; + +typedef struct msrp_msg_s switch_msrp_msg_t; + +typedef struct msrp_socket_s { + switch_port_t port; + switch_socket_t *sock; + switch_thread_t *thread; + int secure; +} msrp_socket_t; + +typedef struct msrp_client_socket_s { + switch_socket_t *sock; + int secure; +} msrp_client_socket_t; + +typedef struct { + switch_memory_pool_t *pool; + int secure; + char *remote_path; + char *remote_accept_types; + char *remote_accept_wrapped_types; + char *remote_setup; + char *remote_file_selector; + char *local_path; + char *local_accept_types; + char *local_accept_wrapped_types; + char *local_setup; + char *local_file_selector; + int local_port; + char *call_id; + msrp_msg_t *msrp_msg; + msrp_msg_t *last_msg; + switch_mutex_t *mutex; + switch_size_t msrp_msg_buffer_size; + switch_size_t msrp_msg_count; + msrp_socket_t *msock; + msrp_client_socket_t *csock; + switch_frame_t frame; + uint8_t frame_data[SWITCH_RTP_MAX_BUF_LEN]; +} switch_msrp_session_t; + +SWITCH_DECLARE(switch_status_t) switch_msrp_init(); +SWITCH_DECLARE(switch_status_t) switch_msrp_destroy(); +SWITCH_DECLARE(switch_msrp_session_t *)switch_msrp_session_new(switch_memory_pool_t *pool, switch_bool_t secure); +SWITCH_DECLARE(switch_status_t) switch_msrp_session_destroy(switch_msrp_session_t **ms); +// switch_status_t switch_msrp_session_push_msg(switch_msrp_session_t *ms, msrp_msg_t *msg); +SWITCH_DECLARE(switch_msrp_msg_t *)switch_msrp_session_pop_msg(switch_msrp_session_t *ms); +SWITCH_DECLARE(switch_status_t) switch_msrp_send(switch_msrp_session_t *ms, msrp_msg_t *msg); + +SWITCH_DECLARE(void) switch_msrp_load_apis_and_applications(switch_loadable_module_interface_t **moudle_interface); +#endif + +/* 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 noet: + */ diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 7b81a07e8f..c00a76a5cc 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -1376,6 +1376,7 @@ typedef enum { CC_QUEUEABLE_DTMF_DELAY, CC_IO_OVERRIDE, CC_RTP_RTT, + CC_MSRP, /* WARNING: DO NOT ADD ANY FLAGS BELOW THIS LINE */ CC_FLAG_MAX } switch_channel_cap_t; @@ -1528,6 +1529,7 @@ typedef enum { CF_TEXT_ECHO, CF_TEXT_ACTIVE, CF_TEXT_IDLE, + CF_MSRP, /* WARNING: DO NOT ADD ANY FLAGS BELOW THIS LINE */ /* IF YOU ADD NEW ONES CHECK IF THEY SHOULD PERSIST OR ZERO THEM IN switch_core_session.c switch_core_session_request_xml() */ CF_FLAG_MAX diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index f44b4ab55b..e1cb3f0063 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -7498,6 +7498,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load) switch_console_set_complete("add file_exists"); switch_console_set_complete("add getcputime"); + switch_msrp_load_apis_and_applications(module_interface); + /* indicate that the module should continue to be loaded */ return SWITCH_STATUS_NOUNLOAD; } diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index 017150d6ed..8b5f67c811 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -927,6 +927,23 @@ static switch_status_t sofia_read_text_frame(switch_core_session_t *session, swi static switch_status_t sofia_write_text_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id) { + if (switch_channel_test_flag(switch_core_session_get_channel(session), CF_MSRP)) { + switch_msrp_session_t *msrp_session = switch_core_media_get_msrp_session(session); + + if (frame && msrp_session) { + switch_msrp_msg_t msrp_msg = { 0 }; + + msrp_msg.headers[MSRP_H_CONTENT_TYPE] = "message/cpim"; + // msrp_msg.headers[MSRP_H_CONTENT_TYPE] = "text/plain"; + msrp_msg.payload = frame->data; + msrp_msg.payload_bytes = frame->datalen; + + return switch_msrp_send(msrp_session, &msrp_msg); + } + + return SWITCH_STATUS_FALSE; + } + return switch_core_media_write_frame(session, frame, flags, stream_id, SWITCH_MEDIA_TYPE_TEXT); } diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index 9c493bf9a9..ac5b812388 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -160,6 +160,7 @@ void sofia_glue_attach_private(switch_core_session_t *session, sofia_profile_t * switch_channel_set_cap(tech_pvt->channel, CC_JITTERBUFFER); switch_channel_set_cap(tech_pvt->channel, CC_FS_RTP); switch_channel_set_cap(tech_pvt->channel, CC_RTP_RTT); + switch_channel_set_cap(tech_pvt->channel, CC_MSRP); switch_channel_set_cap(tech_pvt->channel, CC_QUEUEABLE_DTMF_DELAY); diff --git a/src/switch_core.c b/src/switch_core.c index e06b6b1f39..d19bc75ed2 100644 --- a/src/switch_core.c +++ b/src/switch_core.c @@ -42,6 +42,7 @@ #include #include "private/switch_core_pvt.h" #include +#include #ifndef WIN32 #include #ifdef HAVE_SETRLIMIT @@ -2391,6 +2392,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_init_and_modload(switch_core_flag_t switch_core_set_signal_handlers(); switch_load_network_lists(SWITCH_FALSE); + switch_msrp_init(); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Bringing up environment.\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Loading Modules.\n"); if (switch_loadable_module_init(SWITCH_TRUE) != SWITCH_STATUS_SUCCESS) { @@ -2905,6 +2908,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_destroy(void) switch_scheduler_task_thread_stop(); switch_rtp_shutdown(); + switch_msrp_destroy(); if (switch_test_flag((&runtime), SCF_USE_AUTO_NAT)) { switch_nat_shutdown(); diff --git a/src/switch_core_media.c b/src/switch_core_media.c index 06b4d312f8..69923c6fa4 100644 --- a/src/switch_core_media.c +++ b/src/switch_core_media.c @@ -24,6 +24,7 @@ * Contributor(s): * * Anthony Minessale II + * Seven Du * * switch_core_media.c -- Core Media * @@ -56,7 +57,6 @@ static void gen_ice(switch_core_session_t *session, switch_media_type_t type, co #define MAX_RED_FRAMES 25 #define RED_PACKET_SIZE 100 - typedef enum { SMF_INIT = (1 << 0), SMF_READY = (1 << 1), @@ -217,6 +217,7 @@ struct switch_media_handle_s { switch_core_media_flag_t media_flags[SCMF_MAX]; smh_flag_t flags; switch_rtp_engine_t engines[SWITCH_MEDIA_TYPE_TOTAL]; + switch_msrp_session_t *msrp_session; switch_mutex_t *read_mutex[SWITCH_MEDIA_TYPE_TOTAL]; switch_mutex_t *write_mutex[SWITCH_MEDIA_TYPE_TOTAL]; char *codec_order[SWITCH_MAX_CODECS]; @@ -1702,8 +1703,7 @@ SWITCH_DECLARE(void) switch_media_handle_destroy(switch_core_session_t *session) switch_core_session_unset_write_codec(session); switch_core_media_deactivate_rtp(session); - - + if (smh->msrp_session) switch_msrp_session_destroy(&smh->msrp_session); } @@ -2417,6 +2417,42 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_read_frame(switch_core_session return SWITCH_STATUS_FALSE; } + if (type == SWITCH_MEDIA_TYPE_TEXT && smh->msrp_session) { + switch_msrp_session_t *msrp_session = smh->msrp_session; + switch_frame_t *rframe = &msrp_session->frame; + + msrp_msg_t *msrp_msg = switch_msrp_session_pop_msg(msrp_session); + + if (0 && msrp_msg && msrp_msg->method == MSRP_METHOD_SEND) { /*echo back*/ + char *p; + p = msrp_msg->headers[MSRP_H_TO_PATH]; + msrp_msg->headers[MSRP_H_TO_PATH] = msrp_msg->headers[MSRP_H_FROM_PATH]; + msrp_msg->headers[MSRP_H_FROM_PATH] = p; + switch_msrp_send(msrp_session, msrp_msg); + } + + if (msrp_msg && msrp_msg->method == MSRP_METHOD_SEND) { + rframe->data = msrp_session->frame_data; + rframe->datalen = msrp_msg->payload_bytes; + rframe->packetlen = msrp_msg->payload_bytes; + memcpy(rframe->data, msrp_msg->payload, msrp_msg->payload_bytes); + rframe->m = 1; + + *frame = rframe; + + switch_safe_free(msrp_msg); + msrp_msg = NULL; + status = SWITCH_STATUS_SUCCESS; + + return status; + } + + *frame = NULL; + status = SWITCH_STATUS_FALSE; + + return status; + } + engine = &smh->engines[type]; if (type != SWITCH_MEDIA_TYPE_TEXT && (!engine->read_codec.implementation || !switch_core_codec_ready(&engine->read_codec))) { @@ -4051,7 +4087,7 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s switch_channel_t *channel = switch_core_session_get_channel(session); const char *val; const char *crypto = NULL; - int got_crypto = 0, got_video_crypto = 0, got_audio = 0, saw_audio = 0, got_avp = 0, got_video_avp = 0, got_video_savp = 0, got_savp = 0, got_udptl = 0, got_webrtc = 0, got_text = 0, got_text_crypto = 0; + int got_crypto = 0, got_video_crypto = 0, got_audio = 0, saw_audio = 0, got_avp = 0, got_video_avp = 0, got_video_savp = 0, got_savp = 0, got_udptl = 0, got_webrtc = 0, got_text = 0, got_text_crypto = 0, got_msrp = 0; int scrooge = 0; sdp_parser_t *parser = NULL; sdp_session_t *sdp; @@ -4231,6 +4267,97 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s } } else if (m->m_proto == sdp_proto_udptl) { got_udptl++; + } else if (m->m_proto == sdp_proto_msrp || m->m_proto == sdp_proto_msrps){ + got_msrp++; + } + + if(got_msrp && m->m_type == sdp_media_message) { + if (!smh->msrp_session) { + smh->msrp_session = switch_msrp_session_new(switch_core_session_get_pool(session), m->m_proto == sdp_proto_msrps); + } + + if (!smh->msrp_session) { + goto endmsrp; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "MSRP session created\n"); + + for (attr = m->m_attributes; attr; attr = attr->a_next) { + // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "[%s]=[%s]\n", attr->a_name, attr->a_value); + if (!strcasecmp(attr->a_name, "path") && attr->a_value) { + smh->msrp_session->remote_path = switch_core_session_strdup(session, attr->a_value); + switch_channel_set_variable(session->channel, "sip_msrp_remote_path", attr->a_value); + } else if (!strcasecmp(attr->a_name, "accept-types") && attr->a_value) { + smh->msrp_session->remote_accept_types = switch_core_session_strdup(session, attr->a_value); + switch_channel_set_variable(session->channel, "sip_msrp_remote_accept_types", attr->a_value); + } else if (!strcasecmp(attr->a_name, "accept-wrapped-types") && attr->a_value) { + smh->msrp_session->remote_accept_wrapped_types = switch_core_session_strdup(session, attr->a_value); + switch_channel_set_variable(session->channel, "sip_msrp_remote_accept_wrapped_types", attr->a_value); + } else if (!strcasecmp(attr->a_name, "setup") && attr->a_value) { + smh->msrp_session->remote_setup = switch_core_session_strdup(session, attr->a_value); + switch_channel_set_variable(session->channel, "sip_msrp_remote_setup", attr->a_value); + } else if (!strcasecmp(attr->a_name, "file-selector") && attr->a_value) { + char *tmp = switch_mprintf("%s", attr->a_value); + char *argv[4] = { 0 }; + int argc; + int i; + + smh->msrp_session->remote_file_selector = switch_core_session_strdup(session, attr->a_value); + switch_channel_set_variable(session->channel, "sip_msrp_remote_file_selector", attr->a_value); + + argc = switch_separate_string(tmp, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + + for(i = 0; ichannel, "sip_msrp_file_name", p); + } else if (!strncasecmp(argv[i], "type:", 5)) { + switch_channel_set_variable(session->channel, "sip_msrp_file_type", argv[i] + 5); + } + if (!strncasecmp(argv[i], "size:", 5)) { + switch_channel_set_variable(session->channel, "sip_msrp_file_size", argv[i] + 5); + } + if (!strncasecmp(argv[i], "hash:", 5)) { + switch_channel_set_variable(session->channel, "sip_msrp_file_hash", argv[i] + 5); + } + } + switch_safe_free(tmp); + } else if (!strcasecmp(attr->a_name, "file-transfer-id") && attr->a_value) { + switch_channel_set_variable(session->channel, "sip_msrp_file_transfer_id", attr->a_value); + } else if (!strcasecmp(attr->a_name, "file-disposition") && attr->a_value) { + switch_channel_set_variable(session->channel, "sip_msrp_file_disposition", attr->a_value); + } else if (!strcasecmp(attr->a_name, "file-date") && attr->a_value) { + switch_channel_set_variable(session->channel, "sip_msrp_file_date", attr->a_value); + } else if (!strcasecmp(attr->a_name, "file-icon") && attr->a_value) { + switch_channel_set_variable(session->channel, "sip_msrp_file_icon", attr->a_value); + } else if (!strcasecmp(attr->a_name, "file-range") && attr->a_value) { + switch_channel_set_variable(session->channel, "sip_msrp_file_range", attr->a_value); + } + } + + smh->msrp_session->call_id = switch_core_session_get_uuid(session); + smh->msrp_session->local_accept_types = smh->msrp_session->remote_accept_types; + smh->msrp_session->local_accept_wrapped_types = smh->msrp_session->remote_accept_types; + smh->msrp_session->local_setup = smh->msrp_session->remote_setup; + + switch_channel_set_flag(session->channel, CF_TEXT); + switch_channel_set_flag(session->channel, CF_TEXT_POSSIBLE); + switch_channel_set_flag(session->channel, CF_MSRP); + + switch_core_session_start_text_thread(session); + + endmsrp:; } if (got_udptl && m->m_type == sdp_media_image) { @@ -6748,8 +6875,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_choose_port(switch_core_sessio if (!zstr(smh->mparams->extrtpip)) { /* and we've got an ext-rtp-ip, eg, from verto config */ use_ip = smh->mparams->extrtpip; /* let's use it for composing local sdp to send to client */ /* - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, - "%s will use %s instead of %s in SDP, because we're originating and we have an ext-rtp-ip setting\n", + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, + "%s will use %s instead of %s in SDP, because we're originating and we have an ext-rtp-ip setting\n", switch_channel_get_name(smh->session->channel), smh->mparams->extrtpip, smh->mparams->rtpip); */ } @@ -7508,7 +7635,9 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi text: - + if (switch_channel_test_flag(session->channel, CF_MSRP)) { // skip RTP RTT + goto video; + } if (switch_channel_test_flag(session->channel, CF_TEXT_POSSIBLE) && t_engine->cur_payload_map->rm_encoding && t_engine->cur_payload_map->remote_sdp_port) { /******************************************************************************************/ @@ -9729,7 +9858,85 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess } - ///TEXT +msrp: + + if (smh->msrp_session) { + switch_msrp_session_t *msrp_session = smh->msrp_session; + + if (!zstr(msrp_session->remote_path)) { + if (zstr(msrp_session->local_path)) { + msrp_session->local_path = switch_core_session_sprintf(session, + "msrp%s://%s:%d/%s;tcp", + msrp_session->secure ? "s" : "", + ip, msrp_session->local_port, msrp_session->call_id); + } + + switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), + "m=message %d TCP/%sMSRP *\n" + "a=path:%s\n" + "a=accept-types:%s\n" + "a=accept-wrapped-types:%s\n" + "a=setup:passive\n", + msrp_session->local_port, + msrp_session->secure ? "TLS/" : "", + msrp_session->local_path, + msrp_session->local_accept_types, + msrp_session->local_accept_wrapped_types); + } else { + char *uuid = switch_core_session_get_uuid(session); + const char *file_selector = switch_channel_get_variable(session->channel, "sip_msrp_local_file_selector"); + + if (zstr(msrp_session->local_path)) { + msrp_session->local_path = switch_core_session_sprintf(session, + "msrp%s://%s:%d/%s;tcp", + msrp_session->secure ? "s" : "", + ip, msrp_session->local_port, uuid); + } + + switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), + "m=message %d TCP/%sMSRP *\n" + "a=path:%s\n" + "a=accept-types:message/cpim text/* application/im-iscomposing+xml\n" + "a=accept-wrapped-types:*\n" + "a=setup:passive\n", + msrp_session->local_port, + msrp_session->secure ? "TLS/" : "", + msrp_session->local_path); + + if (!zstr(file_selector)) { + switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + "a=sendonly\na=file-selector:%s\n", file_selector); + } + } + + goto no_rtt; + } else if (switch_channel_test_cap(session->channel, CC_RTP_RTT) && ( + // switch_channel_test_flag(session->channel, CF_TEXT_POSSIBLE) || + switch_channel_var_true(session->channel, "sip_enable_msrp") || + switch_channel_var_true(session->channel, "sip_enable_msrps"))) { + + smh->msrp_session = switch_msrp_session_new(switch_core_session_get_pool(session), switch_channel_var_true(session->channel, "sip_enable_msrps")); + + if (!smh->msrp_session) { + goto endmsrp; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "MSRP session created\n"); + + smh->msrp_session->call_id = switch_core_session_get_uuid(session); + + switch_channel_set_flag(session->channel, CF_TEXT); + switch_channel_set_flag(session->channel, CF_TEXT_POSSIBLE); + switch_channel_set_flag(session->channel, CF_MSRP); + + switch_core_session_start_text_thread(session); + + goto msrp; + + endmsrp: ; + } + + // RTP TEXT if (sdp_type == SDP_TYPE_RESPONSE && !switch_channel_test_flag(session->channel, CF_TEXT_POSSIBLE)) { if (switch_channel_test_flag(session->channel, CF_TEXT_SDP_RECVD)) { @@ -9993,9 +10200,8 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess } } - - + no_rtt: if (map) { switch_event_destroy(&map); @@ -10767,7 +10973,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_reset_jb(switch_core_session_t SWITCH_DECLARE(switch_status_t) switch_core_media_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg) { switch_media_handle_t *smh; - switch_rtp_engine_t *a_engine, *v_engine;//, *t_engine; + switch_rtp_engine_t *a_engine, *v_engine, *t_engine; switch_status_t status = SWITCH_STATUS_SUCCESS; switch_assert(session); @@ -10782,7 +10988,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_receive_message(switch_core_se a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO]; v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO]; - //t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT]; + t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT]; switch (msg->message_id) { @@ -10925,6 +11131,9 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_receive_message(switch_core_se if (direction && *direction == 'v') { direction++; rtp = v_engine->rtp_session; + } else if (direction && *direction == 't' && t_engine) { + direction++; + rtp = t_engine->rtp_session; } if (switch_rtp_ready(rtp) && !zstr(direction) && !zstr(msg->string_array_arg[1])) { @@ -13427,6 +13636,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_text_frame(switch_core switch_io_event_hook_text_write_frame_t *ptr; switch_rtp_engine_t *t_engine; switch_io_write_text_frame_t write_text_frame = NULL; + int is_msrp = switch_channel_test_flag(session->channel, CF_MSRP); switch_assert(session); @@ -13456,7 +13666,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_text_frame(switch_core t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT]; - if (switch_channel_test_cap(session->channel, CC_RTP_RTT)) { + if (!is_msrp && switch_channel_test_cap(session->channel, CC_RTP_RTT)) { if (frame) { char *str = (char *) frame->data; @@ -13522,7 +13732,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_text_frame(switch_core } - if (switch_channel_test_cap(session->channel, CC_RTP_RTT)) { + if (!is_msrp && switch_channel_test_cap(session->channel, CC_RTP_RTT)) { if (t_engine->red_pt) { t_engine->tf->red_pos++; if (t_engine->tf->red_pos == t_engine->tf->red_max) { @@ -13538,7 +13748,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_text_frame(switch_core } return status; - } +} SWITCH_DECLARE(switch_status_t) switch_core_session_printf(switch_core_session_t *session, const char *fmt, ...) { @@ -13615,6 +13825,12 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_print(switch_core_session_t return SWITCH_STATUS_SUCCESS; } +SWITCH_DECLARE(switch_msrp_session_t *) switch_core_media_get_msrp_session(switch_core_session_t *session) +{ + if (!session->media_handle) return NULL; + + return session->media_handle->msrp_session; +} /* For Emacs: diff --git a/src/switch_msrp.c b/src/switch_msrp.c new file mode 100644 index 0000000000..8e2a968116 --- /dev/null +++ b/src/switch_msrp.c @@ -0,0 +1,1569 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2011-2016, Seven Du + * + * 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 + * Seven Du + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * + * + * msrp.c -- MSRP lib + * + */ + +#include +#include + +#define MSRP_BUFF_SIZE SWITCH_RTP_MAX_BUF_LEN +#define DEBUG_MSRP 0 + +static struct { + int running; + int debug; + // switch_mutex_t *mutex; + char *ip; + int message_buffer_size; + + const SSL_METHOD *ssl_method; + SSL_CTX *ssl_ctx; + SSL *ssl; + int ssl_ready; + + msrp_socket_t msock; + msrp_socket_t msock_ssl; +} globals; + +typedef struct worker_helper{ + int debug; + switch_memory_pool_t *pool; + msrp_client_socket_t csock; +} worker_helper_t; + +static void msrp_deinit_ssl() +{ + if (globals.ssl_ctx) { + SSL_CTX_free(globals.ssl_ctx); + globals.ssl_ctx = NULL; + } +} + +static int msrp_init_ssl() +{ + const char *err = ""; + char *cert = "/usr/local/freeswitch/certs/wss.pem"; + char *key = cert; + + + SSL_library_init(); + + globals.ssl_method = SSLv23_server_method(); /* create server instance */ + globals.ssl_ctx = SSL_CTX_new(globals.ssl_method); /* create context */ + assert(globals.ssl_ctx); + globals.ssl_ready = 1; + + /* Disable SSLv2 */ + SSL_CTX_set_options(globals.ssl_ctx, SSL_OP_NO_SSLv2); + /* Disable SSLv3 */ + SSL_CTX_set_options(globals.ssl_ctx, SSL_OP_NO_SSLv3); + /* Disable TLSv1 */ + SSL_CTX_set_options(globals.ssl_ctx, SSL_OP_NO_TLSv1); + /* Disable Compression CRIME (Compression Ratio Info-leak Made Easy) */ + SSL_CTX_set_options(globals.ssl_ctx, SSL_OP_NO_COMPRESSION); + + // /* set the local certificate from CertFile */ + // if (!zstr(profile->chain)) { + // if (switch_file_exists(profile->chain, NULL) != SWITCH_STATUS_SUCCESS) { + // err = "SUPPLIED CHAIN FILE NOT FOUND\n"; + // goto fail; + // } + + // if (!SSL_CTX_use_certificate_chain_file(profile->ssl_ctx, profile->chain)) { + // err = "CERT CHAIN FILE ERROR"; + // goto fail; + // } + // } + + if (switch_file_exists(cert, NULL) != SWITCH_STATUS_SUCCESS) { + err = "SUPPLIED CERT FILE NOT FOUND\n"; + goto fail; + } + + if (!SSL_CTX_use_certificate_file(globals.ssl_ctx, cert, SSL_FILETYPE_PEM)) { + err = "CERT FILE ERROR"; + goto fail; + } + + /* set the private key from KeyFile */ + + if (switch_file_exists(key, NULL) != SWITCH_STATUS_SUCCESS) { + err = "SUPPLIED KEY FILE NOT FOUND\n"; + goto fail; + } + + if (!SSL_CTX_use_PrivateKey_file(globals.ssl_ctx, key, SSL_FILETYPE_PEM)) { + err = "PRIVATE KEY FILE ERROR"; + goto fail; + } + + /* verify private key */ + if ( !SSL_CTX_check_private_key(globals.ssl_ctx) ) { + err = "PRIVATE KEY FILE ERROR"; + goto fail; + } + + SSL_CTX_set_cipher_list(globals.ssl_ctx, "HIGH:!DSS:!aNULL@STRENGTH"); + + return 1; + + fail: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SSL ERR: %s\n", err); + + globals.ssl_ready = 0; + msrp_deinit_ssl(); + + return 0; +} + +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_ip, globals.ip); + +static switch_status_t load_config() +{ + char *cf = "msrp.conf"; + switch_xml_t cfg, xml = NULL, settings, param; + switch_status_t status = SWITCH_STATUS_SUCCESS; + + if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Open of %s failed\n", cf); + status = SWITCH_STATUS_FALSE; + return status; + } + + if ((settings = switch_xml_child(cfg, "settings"))) { + for (param = switch_xml_child(settings, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + if (!strcasecmp(var, "listen-ip")) { + set_global_ip(val); + } else if (!strcasecmp(var, "listen-port")) { + globals.msock.port = atoi(val); + } else if (!strcasecmp(var, "listen-ssl-port")) { + globals.msock_ssl.port = atoi(val); + } else if (!strcasecmp(var, "debug")) { + globals.debug = switch_true(val); + } else if (!strcasecmp(var, "message-buffer-size") && val) { + globals.message_buffer_size = atoi(val); + if (globals.message_buffer_size == 0) globals.message_buffer_size = 50; + } + } + } + + switch_xml_free(xml); + + return status; +} + +static void *SWITCH_THREAD_FUNC msrp_listener(switch_thread_t *thread, void *obj); + +static void close_socket(switch_socket_t ** sock) +{ + // switch_mutex_lock(globals.sock_mutex); + if (*sock) { + switch_socket_shutdown(*sock, SWITCH_SHUTDOWN_READWRITE); + switch_socket_close(*sock); + *sock = NULL; + } + // switch_mutex_unlock(globals.sock_mutex); +} + +static switch_status_t msock_init(char *ip, switch_port_t port, switch_socket_t **sock, switch_memory_pool_t *pool) +{ + switch_sockaddr_t *sa; + switch_status_t rv; + + rv = switch_sockaddr_info_get(&sa, ip, SWITCH_INET, port, 0, pool); + if (rv) goto sock_fail; + + rv = switch_socket_create(sock, switch_sockaddr_get_family(sa), SOCK_STREAM, SWITCH_PROTO_TCP, pool); + if (rv) goto sock_fail; + + rv = switch_socket_opt_set(*sock, SWITCH_SO_REUSEADDR, 1); + if (rv) goto sock_fail; + + rv = switch_socket_bind(*sock, sa); + if (rv) goto sock_fail; + + rv = switch_socket_listen(*sock, 5); + if (rv) goto sock_fail; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Socket up listening on %s:%u\n", ip, port); + + return SWITCH_STATUS_SUCCESS; + +sock_fail: + return rv; +} + +SWITCH_DECLARE(switch_status_t) switch_msrp_init() +{ + switch_memory_pool_t *pool; + switch_thread_t *thread; + switch_threadattr_t *thd_attr = NULL; + switch_status_t status; + + if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "OH OH no pool\n"); + return SWITCH_STATUS_FALSE; + } + + memset(&globals, 0, sizeof(globals)); + set_global_ip("0.0.0.0"); + globals.msock.port = (switch_port_t)MSRP_LISTEN_PORT; + globals.msock_ssl.port = (switch_port_t)MSRP_SSL_LISTEN_PORT; + globals.msock_ssl.secure = 1; + globals.message_buffer_size = 50; + globals.debug = DEBUG_MSRP; + + load_config(); + + globals.running = 1; + + status = msock_init(globals.ip, globals.msock.port, &globals.msock.sock, pool); + + if (status == SWITCH_STATUS_SUCCESS) { + switch_threadattr_create(&thd_attr, pool); + // switch_threadattr_detach_set(thd_attr, 1); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&thread, thd_attr, msrp_listener, &globals.msock, pool); + globals.msock.thread = thread; + } + + msrp_init_ssl(); + status = msock_init(globals.ip, globals.msock_ssl.port, &globals.msock_ssl.sock, pool); + + if (status == SWITCH_STATUS_SUCCESS) { + switch_threadattr_create(&thd_attr, pool); + // switch_threadattr_detach_set(thd_attr, 1); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&thread, thd_attr, msrp_listener, &globals.msock_ssl, pool); + globals.msock_ssl.thread = thread; + } + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(switch_status_t) switch_msrp_destroy() +{ + switch_status_t st = SWITCH_STATUS_SUCCESS; + switch_socket_t *sock; + globals.running = 0; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "destroying thread\n"); + + sock = globals.msock.sock; + close_socket(&sock); + + sock = globals.msock_ssl.sock; + close_socket(&sock); + + if (globals.msock.thread) switch_thread_join(&st, globals.msock.thread); + if (globals.msock_ssl.thread) switch_thread_join(&st, globals.msock_ssl.thread); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "destroy thread done\n"); + + globals.msock.thread = NULL; + globals.msock_ssl.thread = NULL; + + msrp_deinit_ssl(); + + return st; +} + +SWITCH_DECLARE(switch_msrp_session_t *)switch_msrp_session_new(switch_memory_pool_t *pool, switch_bool_t secure) +{ + switch_msrp_session_t *ms; + ms = switch_core_alloc(pool, sizeof(switch_msrp_session_t)); + switch_assert(ms); + ms->pool = pool; + ms->secure = secure; + ms->local_port = secure ? globals.msock_ssl.port : globals.msock.port; + ms->msrp_msg_buffer_size = globals.message_buffer_size; + switch_mutex_init(&ms->mutex, SWITCH_MUTEX_NESTED, pool); + return ms; +} + +SWITCH_DECLARE(switch_status_t) switch_msrp_session_destroy(switch_msrp_session_t **ms) +{ + switch_mutex_destroy((*ms)->mutex); + ms = NULL; + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t switch_msrp_session_push_msg(switch_msrp_session_t *ms, msrp_msg_t *msg) +{ + switch_mutex_lock(ms->mutex); + if (ms->last_msg == NULL) { + ms->last_msg = msg; + ms->msrp_msg = msg; + } else { + ms->last_msg->next = msg; + ms->last_msg = msg; + } + ms->msrp_msg_count++; + switch_mutex_unlock(ms->mutex); + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(msrp_msg_t *)switch_msrp_session_pop_msg(switch_msrp_session_t *ms) +{ + msrp_msg_t *m = ms->msrp_msg; + if (m == NULL) { + switch_yield(20000); + return NULL; + } + + switch_mutex_lock(ms->mutex); + ms->msrp_msg = ms->msrp_msg->next; + ms->msrp_msg_count--; + if (ms->msrp_msg == NULL) ms->last_msg = NULL; + switch_mutex_unlock(ms->mutex); + return m; +} + +switch_status_t msrp_msg_serialize(msrp_msg_t *msrp_msg, char *buf) +{ + char *code_number_str = switch_mprintf("%d", msrp_msg->code_number); + char method[10]; + + switch(msrp_msg->method) { + case MSRP_METHOD_SEND: sprintf(method, "SEND"); break; + case MSRP_METHOD_AUTH: sprintf(method, "REPORT"); break; + case MSRP_METHOD_REPORT: sprintf(method, "REPORT"); break; + default: sprintf(method, "%d", msrp_msg->method); break; + } + sprintf(buf, "=================================\n" + "MSRP %s %s%s\nFrom: %s\nTo: %s\nMessage-ID: %s\n" + "Content-Type: %s\n" + "Byte-Range: %" SWITCH_SIZE_T_FMT "-%" SWITCH_SIZE_T_FMT"/%" SWITCH_SIZE_T_FMT "\n" + "Payload:\n%s\n%s\n" + "=================================\n", + msrp_msg->transaction_id ? switch_str_nil(msrp_msg->transaction_id) : code_number_str, + msrp_msg->transaction_id ? "" : " ", + msrp_msg->transaction_id ? method : switch_str_nil(msrp_msg->code_description), + switch_str_nil(msrp_msg->headers[MSRP_H_FROM_PATH]), + switch_str_nil(msrp_msg->headers[MSRP_H_TO_PATH]), + switch_str_nil(msrp_msg->headers[MSRP_H_MESSAGE_ID]), + switch_str_nil(msrp_msg->headers[MSRP_H_CONTENT_TYPE]), + msrp_msg->byte_start, + msrp_msg->byte_end, + msrp_msg->bytes, + msrp_msg->payload, + msrp_msg->delimiter); + switch_safe_free(code_number_str) + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t msrp_socket_recv(msrp_client_socket_t *csock, char *buf, switch_size_t *len) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + + if (csock->secure) { + *len = SSL_read(globals.ssl, buf, *len); + if (*len) status = SWITCH_STATUS_SUCCESS; + } else { + status = switch_socket_recv(csock->sock, buf, len); + } + + return status; +} + +static switch_status_t msrp_socket_send(msrp_client_socket_t *csock, char *buf, switch_size_t *len) +{ + if (csock->secure) { + *len = SSL_write(globals.ssl, buf, *len); + return *len ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE; + } else { + return switch_socket_send(csock->sock, buf, len); + } +} + +void dump_buffer(char *buf, switch_size_t len, int line) +{ + int i, j, k = 0; + char buff[MSRP_BUFF_SIZE * 2]; + // return; + for(i=0,j=0; i= MSRP_BUFF_SIZE * 2) break; + } + + buff[j] = '\0'; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%d:%ldDUMP:%s:DUMP\n", line, len, buff); +} + +char *find_delim(char *buf, int len, char *delim) +{ + char *p, *q, *s = NULL; + char *end; + + p = buf; + q = delim; + + if (p == NULL) return NULL; + if (q == NULL) return NULL; + + end = buf + len - strlen(delim); + + while(p < end && *q) { + if (*p == *q) { + if (s == NULL) s = p; + p++; + q++; + } else { + s = NULL; + p++; + q = delim; + } + if (*q == '\0') return s; + } + return NULL; +} + +/* +MSRP d4c667b2351e958f SEND +To-Path: msrp://192.168.0.56:2856/73671a97c9dec690d303;tcp +From-Path: msrp://192.168.0.56:2855/2fb5dfec96f3609f7b48;tcp +Message-ID: 7b7c9965ffa8533c +Byte-Range: 1-0/0 +-------d4c667b2351e958f$ +*/ + +char *msrp_parse_header(char *start, int skip, const char *end, msrp_msg_t *msrp_msg, int index, switch_memory_pool_t *pool) +{ + char *p = start + skip; + char *q; + if (*p && *p == ' ') p++; + q = p; + while(*q != '\n' && q < end) q++; + if (q > p) { + if (*(q-1) == '\r') *(q-1) = '\0'; + *q = '\0'; + msrp_msg->headers[index] = switch_core_strdup(pool, p); + msrp_msg->last_header = msrp_msg->last_header > index ? msrp_msg->last_header : index; + return q + 1; + } + return start; +} + +msrp_msg_t *msrp_parse_headers(const char *start, int len, msrp_msg_t *msrp_msg, switch_memory_pool_t *pool) +{ + char *p = (char *)start; + char *q = p; + const char *end = start + len; + int headers = 0; + char line[1024]; + + + while(p < end) { + if (!strncasecmp(p, "MSRP ", 5)) { + p += 5; + q = p; + while(*q && q < end && *q != ' ') q++; + if (q > p) { + *q = '\0'; + msrp_msg->transaction_id = switch_core_strdup(pool, p); + switch_snprintf(line, 128, "-------%s", msrp_msg->transaction_id); + msrp_msg->delimiter = switch_core_strdup(pool, line); + msrp_msg->state = MSRP_ST_PARSE_HEADER; + } + p = q; + if (++p >= end) goto done; + + if (!strncasecmp(p, "SEND", 4)) { + msrp_msg->method = MSRP_METHOD_SEND; + p +=6; /*skip \r\n*/ + } else if (!strncasecmp(p, "REPORT", 6)) { + msrp_msg->method = MSRP_METHOD_REPORT; + p += 8; + } else if (!strncasecmp(p, "AUTH", 4)) { + msrp_msg->method = MSRP_METHOD_AUTH; + p += 6; + } else {/* MSRP transaction_id coden_number codede_scription */ + msrp_msg->method = MSRP_METHOD_REPLY; + q = p; + while(*q && q < end && *q != ' ') q++; + if (q > p) { + *q = '\0'; + msrp_msg->code_number = atoi(p); + p = ++q; + while(*q && q < end && *q != '\n') q++; + if (q > p) { + if (*(q-1) == '\r') *(q-1) = '\0'; + *q = '\0'; + msrp_msg->code_description = switch_core_strdup(pool, p); + p = ++q; + } + } + } + headers++; + } else if (!strncasecmp(p, "From-Path:", 10)) { + q = msrp_parse_header(p, 10, end, msrp_msg, MSRP_H_FROM_PATH, pool); + if (q == p) break; /* incomplete header*/ + p = q; + } else if (!strncasecmp(p, "To-Path:", 8)) { + q = msrp_parse_header(p, 8, end, msrp_msg, MSRP_H_TO_PATH, pool); + if (q == p) break; /* incomplete header*/ + p = q; + } else if (!strncasecmp(p, "Status:", 7)) { + q = msrp_parse_header(p, 7, end, msrp_msg, MSRP_H_STATUS, pool); + if (q == p) break; /* incomplete header*/ + p = q; + } else if (!strncasecmp(p, "Keep-Alive:", 11)) { + q = msrp_parse_header(p, 11, end, msrp_msg, MSRP_H_KEEPALIVE, pool); + if (q == p) break; /* incomplete header*/ + p = q; + } else if (!strncasecmp(p, "Message-ID:", 11)) { + q = msrp_parse_header(p, 11, end, msrp_msg, MSRP_H_MESSAGE_ID, pool); + if (q == p) break; /* incomplete header*/ + p = q; + } else if (!strncasecmp(p, "Content-Type:", 13)) { + q = msrp_parse_header(p, 13, end, msrp_msg, MSRP_H_CONTENT_TYPE, pool); + if (q == p) break; /* incomplete header*/ + p = q; + } else if (!strncasecmp(p, "Success-Report:", 15)) { + q = msrp_parse_header(p, 15, end, msrp_msg, MSRP_H_SUCCESS_REPORT, pool); + if (q == p) break; /* incomplete header*/ + p = q; + } else if (!strncasecmp(p, "Failure-Report:", 15)) { + q = msrp_parse_header(p, 15, end, msrp_msg, MSRP_H_FAILURE_REPORT, pool); + if (q == p) break; /* incomplete header*/ + p = q; + } else if (!strncasecmp(p, "Byte-Range:", 11)) { + p += 11; + if (*p && *p == ' ') p++; + q = p; + while(*q && q < end && *q != '-') q++; + if (q > p) { + *q = '\0'; + msrp_msg->byte_start = atoi(p); + switch_assert(msrp_msg->byte_start > 0); + p = ++q; + if (*p && *p == '*') { + msrp_msg->range_star = 1; + } + while(*q && q < end && *q != '/') q++; + if (q > p) { + *q = '\0'; + msrp_msg->byte_end = msrp_msg->range_star ? 0 : atoi(p); + p = ++q; + while(*q && q < end && *q != '\n') q++; + if (q > p) { + if (*(q-1) == '\r') *(q-1) = '\0'; + *q = '\0'; + msrp_msg->bytes = atoi(p); + + if (!msrp_msg->range_star) { + msrp_msg->payload_bytes = msrp_msg->byte_end + 1 - msrp_msg->byte_start; + } + + if (globals.debug) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%" SWITCH_SIZE_T_FMT " payload bytes\n", msrp_msg->payload_bytes); + + /*Fixme sanity check to avoid large byte-range attack*/ + if (!msrp_msg->range_star && msrp_msg->payload_bytes > msrp_msg->bytes) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "payload size does't match %" SWITCH_SIZE_T_FMT " != %" SWITCH_SIZE_T_FMT "\n", msrp_msg->payload_bytes, msrp_msg->bytes); + msrp_msg->state = MSRP_ST_ERROR; + p = ++q; + break; + } + p = ++q; + } + } + } + headers++; + } else if (*p == '\r' && *(p+1) == '\n') { + msrp_msg->state = MSRP_ST_WAIT_BODY; + p += 2; + break; + } else if (msrp_msg->delimiter && + !strncasecmp(p, msrp_msg->delimiter, strlen(msrp_msg->delimiter))) { + char *x = p + strlen(msrp_msg->delimiter); + if (x < end) { + if (*x == '$') { + p = x + 1; + msrp_msg->state = MSRP_ST_DONE; + if (*p == '\r') p++; + if (*p == '\n') p++; + break; + } else if(*x == '+') { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unsupported %c\n", *x); + if (*p == '\r') p++; + if (*p == '\n') p++; + break; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unsupported %c\n", *x); + msrp_msg->state = MSRP_ST_ERROR; //TODO support # etc. + break; + } + } + break; + } else {/* unsupported header*/ + q = p; + while(*q && q < end && *q != ':') q++; + if (q > p) { + char *last_p = p; + *q = '\0'; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unsupported header [%s]\n", p); + p = q + 1; + msrp_msg->last_header = msrp_msg->last_header == 0 ? MSRP_H_UNKNOWN : msrp_msg->last_header + 1; + q = msrp_parse_header(p, 0, end, msrp_msg, msrp_msg->last_header, pool); + if (q == p) { + p = last_p; + break; /* incomplete header*/ + } + p = q; + } + } + } + +done: + msrp_msg->last_p = p; + return msrp_msg; +} + +msrp_msg_t *msrp_parse_buffer(char *buf, int len, msrp_msg_t *msrp_msg, switch_memory_pool_t *pool) +{ + const char *start; + + if (!msrp_msg) { + switch_zmalloc(msrp_msg, sizeof(msrp_msg_t)); + switch_assert(msrp_msg); + msrp_msg->state = MSRP_ST_WAIT_HEADER; + } + + if (globals.debug) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "parse state: %d\n", msrp_msg->state); + dump_buffer(buf, len, __LINE__); + } + + if (msrp_msg->state == MSRP_ST_WAIT_HEADER) { + if ((start = switch_stristr("MSRP ", buf)) == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Not an MSRP packet, Skip!\n"); + return msrp_msg; + } + msrp_msg = msrp_parse_headers(start, len - (start - buf), msrp_msg, pool); + + if (msrp_msg->state == MSRP_ST_ERROR) return msrp_msg; + if (msrp_msg->state == MSRP_ST_DONE) return msrp_msg; + + if (msrp_msg->last_p && msrp_msg->last_p < buf + len) { + msrp_msg = msrp_parse_buffer(msrp_msg->last_p, len - (msrp_msg->last_p - buf), msrp_msg, pool); + } + } else if (msrp_msg->state == MSRP_ST_WAIT_BODY) { + if(!msrp_msg->range_star && msrp_msg->byte_end == 0) { + msrp_msg->state = MSRP_ST_DONE; + return msrp_msg; + } + if (msrp_msg->range_star) { /* the * case */ + /*hope we can find the delimiter at the end*/ + int dlen; + char *delim_pos = NULL; + switch_assert(msrp_msg->delimiter); + dlen = strlen(msrp_msg->delimiter); + if (!strncmp(buf + len - dlen - 3, msrp_msg->delimiter, dlen)) { /*bingo*/ + msrp_msg->payload_bytes = len - dlen - 5; + msrp_msg->payload = switch_core_alloc(pool, msrp_msg->payload_bytes + 1); + switch_assert(msrp_msg->payload); + memcpy(msrp_msg->payload, buf, msrp_msg->payload_bytes); + msrp_msg->byte_end = msrp_msg->byte_start + msrp_msg->payload_bytes - 1; + msrp_msg->state = MSRP_ST_DONE; + msrp_msg->last_p = buf + len; + return msrp_msg; + } else if ((delim_pos = find_delim(buf, len, msrp_msg->delimiter))){ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "=======================================delimiter: %s\n", delim_pos); + msrp_msg->payload_bytes = delim_pos - buf - 2; + // if (msrp_msg->payload_bytes < 0) msrp_msg->payload_bytes = 0; + msrp_msg->payload = switch_core_alloc(pool, msrp_msg->payload_bytes + 1); + switch_assert(msrp_msg->payload); + memcpy(msrp_msg->payload, buf, msrp_msg->payload_bytes); + msrp_msg->byte_end = msrp_msg->byte_start + msrp_msg->payload_bytes - 1; + msrp_msg->state = MSRP_ST_DONE; + msrp_msg->last_p = delim_pos + dlen + 3; + return msrp_msg; + } else {/* keep waiting*/ + /*TODO: fix potential overflow here*/ + msrp_msg->last_p = buf; + return msrp_msg; + } + } else if (msrp_msg->payload_bytes == 0) { + int dlen = strlen(msrp_msg->delimiter); + if(strncasecmp(buf, msrp_msg->delimiter, dlen)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error find delimiter\n"); + msrp_msg->state = MSRP_ST_ERROR; + return msrp_msg; + } + msrp_msg->payload = NULL; + msrp_msg->state = MSRP_ST_DONE; + msrp_msg->last_p = buf + dlen + 3; /*Fixme: assuming end with $\r\n*/ + return msrp_msg; + } else { + int dlen = strlen(msrp_msg->delimiter); + + if (msrp_msg->payload_bytes > len) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Waiting payload...%d < %d\n", (int)msrp_msg->payload_bytes, (int)len); + return msrp_msg; /*keep waiting ?*/ + } + + msrp_msg->payload = switch_core_alloc(pool, msrp_msg->payload_bytes); + switch_assert(msrp_msg->payload); + memcpy(msrp_msg->payload, buf, msrp_msg->payload_bytes); + msrp_msg->last_p = buf + msrp_msg->payload_bytes; + msrp_msg->state = MSRP_ST_DONE; + msrp_msg->last_p = buf + msrp_msg->payload_bytes; + if (msrp_msg->payload_bytes == len - dlen - 5) { + msrp_msg->last_p = buf + len; + + if (globals.debug) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "payload bytes:%d\n", (int)msrp_msg->payload_bytes); + + return msrp_msg; /*Fixme: assuming \r\ndelimiter$\r\n present*/ + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%ld %d %d\n", msrp_msg->payload_bytes, len, dlen); + + msrp_msg->state = MSRP_ST_ERROR; + return msrp_msg; + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error here! code:%d\n", msrp_msg->state); + } + return msrp_msg; +} + + +switch_status_t msrp_reply(msrp_client_socket_t *csock, msrp_msg_t *msrp_msg) +{ + char buf[2048]; + switch_size_t len; + sprintf(buf, "MSRP %s 200 OK\r\nTo-Path: %s\r\nFrom-Path: %s\r\n" + "%s$\r\n", + switch_str_nil(msrp_msg->transaction_id), + switch_str_nil(msrp_msg->headers[MSRP_H_FROM_PATH]), + switch_str_nil(msrp_msg->headers[MSRP_H_TO_PATH]), + msrp_msg->delimiter); + len = strlen(buf); + + return msrp_socket_send(csock, buf, &len); +} + +switch_status_t msrp_report(msrp_client_socket_t *csock, msrp_msg_t *msrp_msg, char *status_code) +{ + char buf[2048]; + switch_size_t len; + sprintf(buf, "MSRP %s REPORT\r\nTo-Path: %s\r\nFrom-Path: %s\r\nMessage-ID: %s\r\n" + "Status: 000 %s\r\nByte-Range: 1-%" SWITCH_SIZE_T_FMT "/%" SWITCH_SIZE_T_FMT + "\r\n%s$\r\n", + switch_str_nil(msrp_msg->transaction_id), + switch_str_nil(msrp_msg->headers[MSRP_H_FROM_PATH]), + switch_str_nil(msrp_msg->headers[MSRP_H_TO_PATH]), + switch_str_nil(msrp_msg->headers[MSRP_H_MESSAGE_ID]), + switch_str_nil(status_code), + msrp_msg->byte_end, + msrp_msg->bytes, + msrp_msg->delimiter); + len = strlen(buf); + if (globals.debug) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "report: %" SWITCH_SIZE_T_FMT " bytes\n%s", len, buf); + return msrp_socket_send(csock, buf, &len); +} + +static switch_bool_t msrp_find_uuid(char *uuid, char *to_path) +{ + int len = strlen(to_path); + int i; + int slash_count = 0; + switch_assert(to_path); + for(i=0; icsock; + switch_memory_pool_t *pool = helper->pool; + char buf[MSRP_BUFF_SIZE]; + char *p; + char *last_p; + switch_size_t len = MSRP_BUFF_SIZE; + switch_status_t status; + msrp_msg_t *msrp_msg = NULL; + char uuid[128] = { 0 }; + switch_core_session_t *session = NULL; + switch_msrp_session_t *msrp_session = NULL; + switch_channel_t *channel = NULL; + int sanity = 10; + SSL *ssl = NULL; + + switch_socket_opt_set(csock->sock, SWITCH_SO_TCP_NODELAY, TRUE); + // switch_socket_opt_set(csock->sock, SWITCH_SO_NONBLOCK, TRUE); + + if (csock->secure) { // tls? + int secure_established = 0; + int sanity = 10; + switch_os_socket_t sockdes = -1; + + switch_os_sock_get(&sockdes, csock->sock); + // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "socket: %d\n", sockdes); + switch_assert(sockdes > -1); + + ssl = SSL_new(globals.ssl_ctx); + assert(ssl); + globals.ssl = ssl; + + SSL_set_fd(ssl, sockdes); + + do { + int code = SSL_accept(ssl); + + if (code == 1) { + secure_established = 1; + goto done; + } + + if (code == 0) { + goto err; + } + + if (code < 0) { + if (code == -1 && SSL_get_error(ssl, code) != SSL_ERROR_WANT_READ) { + goto err; + } + } + } while(sanity--); + + err: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SSL ERR\n"); + goto end; + + done: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SSL established = %d\n", secure_established); + } + + len = MSRP_BUFF_SIZE; + status = msrp_socket_recv(csock, buf, &len); + + if (helper->debug) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "status:%d, len:%" SWITCH_SIZE_T_FMT "\n", status, len); + } + + if (status == SWITCH_STATUS_SUCCESS) { + msrp_msg = msrp_parse_buffer(buf, len, NULL, pool); + switch_assert(msrp_msg); + } else { + goto end; + } + + if (helper->debug) { + msrp_msg_serialize(msrp_msg, buf); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s\n", buf); + } + + if (msrp_msg->state == MSRP_ST_DONE && msrp_msg->method == MSRP_METHOD_SEND) { + msrp_reply(csock, msrp_msg); + if (msrp_msg->headers[MSRP_H_SUCCESS_REPORT] && + !strcmp(msrp_msg->headers[MSRP_H_SUCCESS_REPORT], "yes")) { + msrp_report(csock, msrp_msg, "200 OK"); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse initial message error!\n"); + goto end; + } + + if (msrp_find_uuid(uuid, msrp_msg->headers[MSRP_H_TO_PATH]) != SWITCH_TRUE) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid MSRP to-path!\n"); + goto end; + } + + while (sanity-- && !(session = switch_core_session_locate(uuid))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "waiting for session\n"); + switch_yield(1000000); + } + + if(!session) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No such session %s\n", uuid); + goto end; + } + + channel = switch_core_session_get_channel(session); + msrp_session = switch_core_media_get_msrp_session(session); + switch_assert(msrp_session); + msrp_session->csock = csock; + + len = MSRP_BUFF_SIZE; + p = buf; + last_p = buf; + switch_safe_free(msrp_msg); + msrp_msg = NULL; + + while(msrp_socket_recv(csock, p, &len) == SWITCH_STATUS_SUCCESS) { + // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "read bytes:%ld\n", len); + + if (helper->debug) dump_buffer(buf, (p - buf) + len, __LINE__); + + msrp_msg = msrp_parse_buffer(last_p, p - last_p + len, msrp_msg, pool); + + switch_assert(msrp_msg); + + if (msrp_msg->state == MSRP_ST_ERROR) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "msrp parse error!\n"); + goto end; + } + + if (helper->debug) { + // char msg_buf[MSRP_BUFF_SIZE * 2]; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "state:%d, len:%" SWITCH_SIZE_T_FMT " payload_bytes:%" SWITCH_SIZE_T_FMT "\n", msrp_msg->state, len, msrp_msg->payload_bytes); + // { + // char bbb[MSRP_BUFF_SIZE * 2]; + // msrp_msg_serialize(msrp_msg_tmp, bbb), + // + } + + if (msrp_msg->state == MSRP_ST_DONE && msrp_msg->method == MSRP_METHOD_SEND) { + msrp_reply(csock, msrp_msg); + if (msrp_msg->headers[MSRP_H_SUCCESS_REPORT] && + !strcmp(msrp_msg->headers[MSRP_H_SUCCESS_REPORT], "yes")) { + msrp_report(csock, msrp_msg, "200 OK"); + } + last_p = msrp_msg->last_p; + switch_msrp_session_push_msg(msrp_session, msrp_msg); + msrp_msg = NULL; + } else if (msrp_msg->state == MSRP_ST_DONE) { /* throw away */ + last_p = msrp_msg->last_p; + switch_safe_free(msrp_msg); + msrp_msg = NULL; + } else { + last_p = msrp_msg->last_p; + } + + while (msrp_session && msrp_session->msrp_msg_count > msrp_session->msrp_msg_buffer_size) { + if (!switch_channel_ready(channel)) break; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "%s reading too fast, relax...\n", uuid); + switch_yield(100000); + } + + if (p + len > last_p) { + p += len; + if (!msrp_msg) { + int rest_len = p - last_p; + + memmove(buf, last_p, rest_len); + p = buf + rest_len; + len = MSRP_BUFF_SIZE - rest_len; + last_p = buf; + continue; + } + + if (p >= buf + MSRP_BUFF_SIZE) { + + if (msrp_msg->state != MSRP_ST_WAIT_BODY || !msrp_msg->range_star) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "buffer overflow\n"); + /*todo, do a strstr in the whole buffer ?*/ + break; + } + + /* buffer full*/ + msrp_msg->payload_bytes = p - last_p; + msrp_msg->byte_end = msrp_msg->byte_start + msrp_msg->payload_bytes - 1; + msrp_msg->payload = switch_core_alloc(pool, msrp_msg->payload_bytes); + switch_assert(msrp_msg->payload); + memcpy(msrp_msg->payload, last_p, msrp_msg->payload_bytes); + + { + int i; + msrp_msg_t *msrp_msg_old = msrp_msg; + msrp_msg = NULL; + /*dup msrp_msg*/ + switch_zmalloc(msrp_msg, sizeof(msrp_msg_t)); + switch_assert(msrp_msg); + msrp_msg->state = msrp_msg_old->state; + msrp_msg->byte_start = msrp_msg_old->byte_end + 1; + msrp_msg->bytes = msrp_msg_old->bytes; + msrp_msg->range_star = msrp_msg_old->range_star; + msrp_msg->method = msrp_msg_old->method; + msrp_msg->transaction_id = switch_core_strdup(pool, msrp_msg_old->transaction_id); + msrp_msg->delimiter = switch_core_strdup(pool, msrp_msg_old->delimiter); + msrp_msg->last_header = msrp_msg_old->last_header; + for (i = 0; i < msrp_msg->last_header; i++) { + msrp_msg->headers[i] = switch_core_strdup(pool, msrp_msg_old->headers[i]); + } + + msrp_msg_old->state = MSRP_ST_DONE; + + if (msrp_msg_old->headers[MSRP_H_SUCCESS_REPORT] && + !strcmp(msrp_msg_old->headers[MSRP_H_SUCCESS_REPORT], "yes")) { + // msrp_report(csock, msrp_msg_old, "200 OK"); + } + + switch_msrp_session_push_msg(msrp_session, msrp_msg_old); + } + + p = buf; + len = MSRP_BUFF_SIZE; + last_p = buf; + msrp_msg->last_p = buf; + } + } else { /* all buffer parsed */ + p = buf; + len = MSRP_BUFF_SIZE; + last_p = buf; + } + if (!switch_channel_ready(channel)) break; + } + +end: + if (session) switch_core_session_rwunlock(session); + + switch_socket_close(csock->sock); + switch_core_destroy_memory_pool(&pool); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "msrp worker %s down\n", uuid); + + return NULL; +} + +static void *SWITCH_THREAD_FUNC msrp_listener(switch_thread_t *thread, void *obj) +{ + msrp_socket_t *msock = (msrp_socket_t *)obj; + switch_status_t rv; + switch_memory_pool_t *pool = NULL; + switch_threadattr_t *thd_attr = NULL; + switch_socket_t *sock = NULL; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "MSRP listener start%s\n", msock->secure ? " ssl" : ""); + + if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "OH OH no pool\n"); + return NULL; + } + + switch_socket_opt_set(msock->sock, SWITCH_SO_TCP_NODELAY, TRUE); + // switch_socket_opt_set(msock->sock, SWITCH_SO_NONBLOCK, TRUE); + + while (globals.running && (rv = switch_socket_accept(&sock, msock->sock, pool)) == SWITCH_STATUS_SUCCESS) { + switch_memory_pool_t *worker_pool; + worker_helper_t *helper; + + if (globals.debug > 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Connection Open%s\n", msock->secure ? " SSL" : ""); + } + + if (switch_core_new_memory_pool(&worker_pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "OH OH no pool\n"); + return NULL; + } + + switch_zmalloc(helper, sizeof(worker_helper_t)); + + switch_assert(helper != NULL); + helper->pool = worker_pool; + helper->debug = globals.debug; + helper->csock.sock = sock; + helper->csock.secure = msock->secure; + + switch_threadattr_create(&thd_attr, pool); + switch_threadattr_detach_set(thd_attr, 1); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&thread, thd_attr, msrp_worker, helper, worker_pool); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "msrp worker new thread spawned!\n"); + } + + if (pool) switch_core_destroy_memory_pool(&pool); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "MSRP listener down\n"); + + return NULL; +} + +void random_string(char *buf, switch_size_t size) +{ + long val[4]; + int x; + + for (x = 0; x < 4; x++) + val[x] = random(); + snprintf(buf, size, "%08lx%08lx%08lx%08lx", val[0], val[1], val[2], val[3]); + *(buf+size) = '\0'; +} + +SWITCH_DECLARE(switch_status_t) switch_msrp_send(switch_msrp_session_t *ms, msrp_msg_t *msrp_msg) +{ + char transaction_id[32]; + char buf[MSRP_BUFF_SIZE]; + switch_size_t len; + char *to_path = msrp_msg->headers[MSRP_H_TO_PATH] ? msrp_msg->headers[MSRP_H_TO_PATH] : ms->remote_path; + char *from_path = msrp_msg->headers[MSRP_H_FROM_PATH] ? msrp_msg->headers[MSRP_H_FROM_PATH] : ms->local_path; + + if (!from_path) return SWITCH_STATUS_SUCCESS; + + random_string(transaction_id, 16); + + sprintf(buf, "MSRP %s SEND\r\nTo-Path: %s\r\nFrom-Path: %s\r\n" + "Content-Type: %s\r\n" + "Byte-Range: 1-%" SWITCH_SIZE_T_FMT "/%" SWITCH_SIZE_T_FMT "%s", + transaction_id, + to_path, + from_path, + switch_str_nil(msrp_msg->headers[MSRP_H_CONTENT_TYPE]), + msrp_msg->payload ? msrp_msg->payload_bytes : 0, + msrp_msg->payload ? msrp_msg->payload_bytes : 0, + msrp_msg->payload ? "\r\n\r\n" : ""); + + len = strlen(buf); + + if (msrp_msg->payload) { + if (len + msrp_msg->payload_bytes >= MSRP_BUFF_SIZE) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "payload too large! %" SWITCH_SIZE_T_FMT "\n", len + msrp_msg->payload_bytes); + return SWITCH_STATUS_FALSE; + } + memcpy(buf + len, msrp_msg->payload, msrp_msg->payload_bytes); + len += msrp_msg->payload_bytes; + } + sprintf(buf + len, "\r\n-------%s$\r\n", transaction_id); + len += (12 + strlen(transaction_id)); + if (globals.debug) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "---------------------send: %" SWITCH_SIZE_T_FMT " bytes\n%s\n", len, buf); + + return ms->csock ? msrp_socket_send(ms->csock, buf, &len) : SWITCH_STATUS_FALSE; +} + +#if 0 +SWITCH_STANDARD_APP(msrp_echo_function) +{ + msrp_session_t *msrp_session = NULL; + msrp_msg_t *msrp_msg = NULL; + switch_channel_t *channel = switch_core_session_get_channel(session); + // private_object_t *tech_pvt = switch_core_session_get_private(session); + + if (!tech_pvt) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No tech_pvt!\n"); + return; + } + + if(!tech_pvt->msrp_session) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No msrp_session!\n"); + return; + } + + while (switch_channel_ready(channel) && (msrp_session = tech_pvt->msrp_session)) { + if ((msrp_msg = msrp_session_pop_msg(msrp_session)) == NULL) { + switch_yield(100000); + continue; + } + + if (msrp_msg->method == MSRP_METHOD_SEND) { /*echo back*/ + char *p; + p = msrp_msg->headers[MSRP_H_TO_PATH]; + msrp_msg->headers[MSRP_H_TO_PATH] = msrp_msg->headers[MSRP_H_FROM_PATH]; + msrp_msg->headers[MSRP_H_FROM_PATH] = p; + msrp_send(msrp_session->socket, msrp_msg); + } + + switch_safe_free(msrp_msg); + msrp_msg = NULL; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "eat one message, left:%d\n", (int)msrp_session->msrp_msg_count); + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Echo down!\n"); + +} + +SWITCH_STANDARD_APP(msrp_recv_function) +{ + msrp_session_t *msrp_session = NULL; + msrp_msg_t *msrp_msg = NULL; + switch_channel_t *channel = switch_core_session_get_channel(session); + private_object_t *tech_pvt = switch_core_session_get_private(session); + switch_memory_pool_t *pool = switch_core_session_get_pool(session); + switch_file_t *fd; + const char *filename = data; + + if (!tech_pvt) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No tech_pvt!\n"); + return; + } + + if(!tech_pvt->msrp_session) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No msrp_session!\n"); + return; + } + + if (zstr(data)) { + filename = switch_channel_get_variable(channel, "sip_msrp_file_name"); + if (zstr(filename)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No file specified.\n"); + return; + } + filename = switch_core_session_sprintf(session, "%s%s%s", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR, filename); + } + + if (!(msrp_session = tech_pvt->msrp_session)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Not a msrp session!\n"); + return; + } + + if (switch_file_open(&fd, filename, SWITCH_FOPEN_WRITE | SWITCH_FOPEN_TRUNCATE | SWITCH_FOPEN_CREATE, SWITCH_FPROT_OS_DEFAULT, pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Open File %s\n", filename); + return; + } + + while (1) { + if ((msrp_msg = msrp_session_pop_msg(msrp_session)) == NULL) { + if (!switch_channel_ready(channel)) break; + switch_yield(10000); + continue; + } + + if (msrp_msg->method == MSRP_METHOD_SEND) { + switch_size_t bytes = msrp_msg->payload_bytes; + char *msg = switch_str_nil(msrp_msg->headers[MSRP_H_MESSAGE_ID]); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s %" SWITCH_SIZE_T_FMT "bytes writing\n", msg, bytes); + switch_file_write(fd, msrp_msg->payload, &bytes); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%" SWITCH_SIZE_T_FMT "bytes written\n", bytes); + if (bytes != msrp_msg->payload_bytes) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "write fail, bytes lost!\n"); + } + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "eat one message, left:%d\n", (int)msrp_session->msrp_msg_count); + + switch_safe_free(msrp_msg); + } + + switch_file_close(fd); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "File closed!\n"); +} + +SWITCH_STANDARD_APP(msrp_send_function) +{ + msrp_session_t *msrp_session = NULL; + msrp_msg_t *msrp_msg = NULL; + switch_channel_t *channel = switch_core_session_get_channel(session); + private_object_t *tech_pvt = switch_core_session_get_private(session); + switch_memory_pool_t *pool = switch_core_session_get_pool(session); + switch_file_t *fd; + const char *filename = data; + switch_size_t len = 2048; + char buf[2048]; + int sanity = 10; + + if (!tech_pvt) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No tech_pvt!\n"); + return; + } + + if(!tech_pvt->msrp_session) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No msrp_session!\n"); + return; + } + + if (!(msrp_session = tech_pvt->msrp_session)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Not a msrp session!\n"); + return; + } + + if (switch_file_open(&fd, filename, SWITCH_FOPEN_READ, SWITCH_FPROT_OS_DEFAULT, pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Open File %s\n", filename); + return; + } + + switch_assert(pool); + + switch_zmalloc(msrp_msg, sizeof(msrp_msg_t)); + switch_assert(msrp_msg); + + msrp_msg->headers[MSRP_H_FROM_PATH] = switch_mprintf("msrp://%s:%d/%s;tcp", + tech_pvt->rtpip, tech_pvt->msrp_session->local_port, tech_pvt->msrp_session->call_id); + msrp_msg->headers[MSRP_H_TO_PATH] = tech_pvt->msrp_session->remote_path; + /*TODO: send file in octet or maybe guess mime?*/ + msrp_msg->headers[MSRP_H_CONTENT_TYPE] = "application/octet-stream"; + msrp_msg->headers[MSRP_H_CONTENT_TYPE] = "text/plain"; + + msrp_msg->bytes = switch_file_get_size(fd); + msrp_msg->byte_start = 1; + + while(sanity-- && tech_pvt->msrp_session->socket == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Waiting socket\n"); + switch_yield(1000000); + } + + if (tech_pvt->msrp_session->socket == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Waiting for socket timedout, exiting...\n"); + goto end; + } + + while (switch_file_read(fd, buf, &len) == SWITCH_STATUS_SUCCESS && + switch_channel_ready(channel)) { + msrp_msg->payload = buf; + msrp_msg->byte_end = msrp_msg->byte_start + len - 1; + msrp_msg->payload_bytes = len; + + /*TODO: send in chunk should ending in + but not $ after delimiter*/ + msrp_send(tech_pvt->msrp_session->socket, msrp_msg); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "%ld bytes sent\n", len); + + msrp_msg->byte_start += len; + } + + sanity = 10; + + while(sanity-- && switch_channel_ready(channel)) { + switch_yield(1000000); + } + +end: + switch_file_close(fd); + + switch_safe_free(msrp_msg->headers[MSRP_H_FROM_PATH]); + switch_safe_free(msrp_msg); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "File sent, closed!\n"); +} + +SWITCH_STANDARD_APP(msrp_bridge_function) +{ + switch_channel_t *caller_channel = switch_core_session_get_channel(session); + switch_core_session_t *peer_session = NULL; + switch_channel_t *peer_channel = NULL; + msrp_session_t *caller_msrp_session = NULL; + msrp_session_t *peer_msrp_session = NULL; + private_object_t *tech_pvt = NULL; + private_object_t *ptech_pvt = NULL; + msrp_msg_t *msrp_msg = NULL; + switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING; + switch_status_t status; + + if (zstr(data)) { + return; + } + + if ((status = + switch_ivr_originate(session, &peer_session, &cause, data, 0, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Originate Failed. Cause: %s\n", switch_channel_cause2str(cause)); + return; + } + + switch_ivr_signal_bridge(session, peer_session); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "msrp channel bridged\n"); + + peer_channel = switch_core_session_get_channel(session); + tech_pvt = switch_core_session_get_private(session); + ptech_pvt = switch_core_session_get_private(peer_session); + + caller_msrp_session = tech_pvt->msrp_session; + peer_msrp_session = ptech_pvt->msrp_session; + switch_assert(caller_msrp_session); + switch_assert(peer_msrp_session); + + if (switch_channel_test_flag(peer_channel, CF_ANSWERED) && !switch_channel_test_flag(caller_channel, CF_ANSWERED)) { + switch_channel_pass_callee_id(peer_channel, caller_channel); + switch_channel_answer(caller_channel); + } + + // TODO we need to run the following code in a new thread + // TODO we cannot test channel_ready as we don't have (audio) media + // while (switch_channel_ready(caller_channel) && switch_channel_ready(peer_channel)){ + while (switch_channel_get_state(caller_channel) == CS_HIBERNATE && + switch_channel_get_state(peer_channel) == CS_HIBERNATE){ + int found = 0; + if ((msrp_msg = msrp_session_pop_msg(caller_msrp_session))) { + if (msrp_msg->method == MSRP_METHOD_SEND) { /* write to peer */ + msrp_msg->headers[MSRP_H_FROM_PATH] = switch_mprintf("msrp://%s:%d/%s;tcp", + ptech_pvt->rtpip, peer_msrp_session->local_port, peer_msrp_session->call_id); + msrp_msg->headers[MSRP_H_TO_PATH] = peer_msrp_session->remote_path; + + if (peer_msrp_session->socket) { + msrp_send(peer_msrp_session->socket, msrp_msg); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "socket not ready, discarding one message!!\n"); + } + } + switch_safe_free(msrp_msg); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "eat one message, left:%d\n", (int)caller_msrp_session->msrp_msg_count); + found++; + } + + if ((msrp_msg = msrp_session_pop_msg(peer_msrp_session))) { + if (msrp_msg->method == MSRP_METHOD_SEND) { /* write to caller */ + msrp_msg->headers[MSRP_H_FROM_PATH] = switch_mprintf("msrp://%s:%d/%s;tcp", + tech_pvt->rtpip, caller_msrp_session->local_port, caller_msrp_session->call_id); + msrp_msg->headers[MSRP_H_TO_PATH] = caller_msrp_session->remote_path; + msrp_send(caller_msrp_session->socket, msrp_msg); + } + switch_safe_free(msrp_msg); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "eat one message, left:%d\n", (int)peer_msrp_session->msrp_msg_count); + found++; + } + + msrp_msg = NULL; + if (!found) switch_yield(100000); + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Bridge down!\n"); + + if (peer_session) { + switch_core_session_rwunlock(peer_session); + } +} + +SWITCH_STANDARD_API(uuid_msrp_send_function) +{ + char *mycmd = NULL, *argv[3] = { 0 }; + int argc; + switch_core_session_t *msession = NULL; + // msrp_session_t *msrp_session = NULL; + msrp_msg_t *msrp_msg = NULL; + private_object_t *tech_pvt = NULL; + switch_memory_pool_t *pool = NULL; + + if (zstr(cmd) || !(mycmd = strdup(cmd))) { + goto error; + } + + argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + + if (argc < 2 || !argv[0]) { + goto error; + } + + if (!(msession = switch_core_session_locate(argv[0]))) { + stream->write_function(stream, "-ERR Usage: cannot locate session.\n"); + return SWITCH_STATUS_SUCCESS; + } + + tech_pvt = switch_core_session_get_private(msession); + pool = switch_core_session_get_pool(msession); + switch_assert(pool); + + if (!tech_pvt) { + stream->write_function(stream, "-ERR No tech_pvt.\n"); + switch_core_session_rwunlock(msession); + return SWITCH_STATUS_SUCCESS; + } + + if(!tech_pvt->msrp_session) { + stream->write_function(stream, "-ERR No msrp_session.\n"); + switch_core_session_rwunlock(msession); + return SWITCH_STATUS_SUCCESS; + } + + switch_zmalloc(msrp_msg, sizeof(msrp_msg_t)); + switch_assert(msrp_msg); + + msrp_msg->headers[MSRP_H_FROM_PATH] = switch_mprintf("msrp://%s:%d/%s;tcp", + tech_pvt->rtpip, tech_pvt->msrp_session->local_port, tech_pvt->msrp_session->call_id); + msrp_msg->headers[MSRP_H_TO_PATH] = tech_pvt->msrp_session->remote_path; + msrp_msg->headers[MSRP_H_CONTENT_TYPE] = "text/plain"; + msrp_msg->payload = switch_core_strdup(pool, argv[1]); + + msrp_send(tech_pvt->msrp_session->socket, msrp_msg); + + switch_safe_free(msrp_msg->headers[MSRP_H_FROM_PATH]); + switch_safe_free(msrp_msg); + stream->write_function(stream, "+OK sent\n"); + switch_core_session_rwunlock(msession); + return SWITCH_STATUS_SUCCESS; +error: + stream->write_function(stream, "-ERR Usage: uuid_msrp_send msg\n"); + return SWITCH_STATUS_SUCCESS; +} + +#endif + + +#define MSRP_SYNTAX "debug |restart" +SWITCH_STANDARD_API(msrp_api_function) +{ + if (zstr(cmd)) { + stream->write_function(stream, "-ERR usage: " MSRP_SYNTAX "\n"); + return SWITCH_STATUS_SUCCESS; + } + + if (!strcmp(cmd, "debug on")) { + globals.debug = 1; + stream->write_function(stream, "+OK debug on\n"); + } else if(!strcmp(cmd, "debug off")) { + globals.debug = 0; + stream->write_function(stream, "+OK debug off\n"); + } else if(!strcmp(cmd, "restart")) { + switch_msrp_destroy(); + switch_msrp_init(); + } + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(void) switch_msrp_load_apis_and_applications(switch_loadable_module_interface_t **module_interface) +{ + // switch_application_interface_t *app_interface; + switch_api_interface_t *api_interface; + + SWITCH_ADD_API(api_interface, "msrp", "MSRP Functions", msrp_api_function, "JSON"); + +#if 0 + SWITCH_ADD_API(api_interface, "uuid_msrp_send", "send msrp text", uuid_msrp_send_function, " "); + SWITCH_ADD_APP(app_interface, "msrp_echo", "Echo msrp message", "Perform an echo test against the msrp channel", msrp_echo_function, "", SAF_NONE); + SWITCH_ADD_APP(app_interface, "msrp_recv", "Recv msrp message to file", "Recv msrp message", msrp_recv_function, "", SAF_NONE); + SWITCH_ADD_APP(app_interface, "msrp_send", "Send file via msrp", "Send file via msrp", msrp_send_function, "", SAF_NONE); + SWITCH_ADD_APP(app_interface, "msrp_bridge", "Bridge msrp channels", "Bridge msrp channels", msrp_bridge_function, "dialstr", SAF_NONE); +#endif + + switch_console_set_complete("add msrp debug on"); + switch_console_set_complete("add msrp debug off"); + switch_console_set_complete("restart"); +} + + +/* 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 noet: + */