From 775ce5c0a8a4acbacaa36d70c81e351fbb73c50d Mon Sep 17 00:00:00 2001 From: William King Date: Wed, 15 Oct 2014 18:26:29 -0400 Subject: [PATCH] Adding mod_smpp as an event_handler module. FS-7730 #resolve --- build/modules.conf.in | 1 + conf/vanilla/autoload_configs/smpp.conf.xml | 15 + configure.ac | 5 + src/mod/event_handlers/mod_smpp/Makefile.am | 17 + src/mod/event_handlers/mod_smpp/TODO | 9 + .../event_handlers/mod_smpp/chatplan.conf.xml | 32 + src/mod/event_handlers/mod_smpp/mod_smpp.c | 273 +++++++++ src/mod/event_handlers/mod_smpp/mod_smpp.h | 109 ++++ .../mod_smpp/mod_smpp_gateway.c | 549 ++++++++++++++++++ .../mod_smpp/mod_smpp_message.c | 192 ++++++ .../event_handlers/mod_smpp/mod_smpp_utils.c | 58 ++ 11 files changed, 1260 insertions(+) create mode 100644 conf/vanilla/autoload_configs/smpp.conf.xml create mode 100644 src/mod/event_handlers/mod_smpp/Makefile.am create mode 100644 src/mod/event_handlers/mod_smpp/TODO create mode 100644 src/mod/event_handlers/mod_smpp/chatplan.conf.xml create mode 100644 src/mod/event_handlers/mod_smpp/mod_smpp.c create mode 100644 src/mod/event_handlers/mod_smpp/mod_smpp.h create mode 100644 src/mod/event_handlers/mod_smpp/mod_smpp_gateway.c create mode 100644 src/mod/event_handlers/mod_smpp/mod_smpp_message.c create mode 100644 src/mod/event_handlers/mod_smpp/mod_smpp_utils.c diff --git a/build/modules.conf.in b/build/modules.conf.in index b15ae9dcd6..b95f20029e 100644 --- a/build/modules.conf.in +++ b/build/modules.conf.in @@ -106,6 +106,7 @@ event_handlers/mod_event_socket #event_handlers/mod_radius_cdr #event_handlers/mod_odbc_cdr #event_handlers/mod_rayo +#event_handlers/mod_smpp #event_handlers/mod_snmp #event_handlers/mod_event_zmq #formats/mod_imagick diff --git a/conf/vanilla/autoload_configs/smpp.conf.xml b/conf/vanilla/autoload_configs/smpp.conf.xml new file mode 100644 index 0000000000..d5b7c0d6fc --- /dev/null +++ b/conf/vanilla/autoload_configs/smpp.conf.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/configure.ac b/configure.ac index 3952bff909..3b58e14685 100644 --- a/configure.ac +++ b/configure.ac @@ -1383,6 +1383,10 @@ PKG_CHECK_MODULES([AMQP], [librabbitmq >= 0.5.2],[ AM_CONDITIONAL([HAVE_AMQP],[true])],[ AC_MSG_RESULT([no]); AM_CONDITIONAL([HAVE_AMQP],[false])]) +PKG_CHECK_MODULES([SMPP34], [libsmpp34 >= 1.10],[ + AM_CONDITIONAL([HAVE_SMPP34],[true])],[ + AC_MSG_RESULT([no]); AM_CONDITIONAL([HAVE_SMPP34],[false])]) + AC_ARG_ENABLE(core-libedit-support, [AS_HELP_STRING([--disable-core-libedit-support], [Compile without libedit Support])]) @@ -1771,6 +1775,7 @@ AC_CONFIG_FILES([Makefile src/mod/event_handlers/mod_radius_cdr/Makefile src/mod/event_handlers/mod_odbc_cdr/Makefile src/mod/event_handlers/mod_rayo/Makefile + src/mod/event_handlers/mod_smpp/Makefile src/mod/event_handlers/mod_snmp/Makefile src/mod/event_handlers/mod_event_zmq/Makefile src/mod/formats/mod_imagick/Makefile diff --git a/src/mod/event_handlers/mod_smpp/Makefile.am b/src/mod/event_handlers/mod_smpp/Makefile.am new file mode 100644 index 0000000000..c67643787c --- /dev/null +++ b/src/mod/event_handlers/mod_smpp/Makefile.am @@ -0,0 +1,17 @@ +include $(top_srcdir)/build/modmake.rulesam +MODNAME=mod_smpp + +if HAVE_SMPP34 + +mod_LTLIBRARIES = mod_smpp.la +mod_smpp_la_SOURCES = mod_smpp_utils.c mod_smpp_gateway.c mod_smpp_message.c mod_smpp.c +mod_smpp_la_CFLAGS = $(AM_CFLAGS) $(SMPP34_CFLAGS) +mod_smpp_la_LIBADD = $(switch_builddir)/libfreeswitch.la +mod_smpp_la_LDFLAGS = -avoid-version -module -no-undefined -shared $(SMPP34_LIBS) $(SWITCH_AM_LDFLAGS) + +else +install: error +all: error +error: + $(error You must install libsmpp34-dev to build this module) +endif diff --git a/src/mod/event_handlers/mod_smpp/TODO b/src/mod/event_handlers/mod_smpp/TODO new file mode 100644 index 0000000000..da902b9654 --- /dev/null +++ b/src/mod/event_handlers/mod_smpp/TODO @@ -0,0 +1,9 @@ +TODO: +1. add more encoding options +2. Improve partial read and write support +3. Add storage options, and registration delivery checks. +4. SSL connection support +5. SMPP 5.0 support +6. async message support +7. Add support for configuring chatplan profile for inbound messages from a gateway +8. ... ? diff --git a/src/mod/event_handlers/mod_smpp/chatplan.conf.xml b/src/mod/event_handlers/mod_smpp/chatplan.conf.xml new file mode 100644 index 0000000000..5768702d0b --- /dev/null +++ b/src/mod/event_handlers/mod_smpp/chatplan.conf.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/event_handlers/mod_smpp/mod_smpp.c b/src/mod/event_handlers/mod_smpp/mod_smpp.c new file mode 100644 index 0000000000..d6b76ca74f --- /dev/null +++ b/src/mod/event_handlers/mod_smpp/mod_smpp.c @@ -0,0 +1,273 @@ +/* +* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application +* Copyright (C) 2005-2015, 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): +* +* William King +* +* mod_smpp.c -- smpp client and server implementation using libsmpp +* +* using libsmpp from: http://cgit.osmocom.org/libsmpp/ +* +*/ + +#include + +SWITCH_MODULE_LOAD_FUNCTION(mod_smpp_load); +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_smpp_shutdown); +SWITCH_MODULE_DEFINITION(mod_smpp, mod_smpp_load, mod_smpp_shutdown, NULL); + +mod_smpp_globals_t mod_smpp_globals; + +switch_status_t mod_smpp_interface_chat_send(switch_event_t *event) +{ + mod_smpp_gateway_t *gateway = NULL; + char *gw_name = switch_event_get_header(event, "smpp_gateway"); + + if (zstr(gw_name)) { + gw_name = "default"; + } + + gateway = switch_core_hash_find(mod_smpp_globals.gateways, gw_name); + + if (!gateway) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "NO SUCH SMPP GATEWAY[%s].", gw_name); + return SWITCH_STATUS_GENERR; + } + + mod_smpp_gateway_send_message(gateway, event); + + return SWITCH_STATUS_SUCCESS; +} + +/* static switch_status_t name (switch_event_t *message, const char *data) */ +SWITCH_STANDARD_CHAT_APP(mod_smpp_chat_send_function) +{ + mod_smpp_gateway_t *gateway = NULL; + + gateway = switch_core_hash_find(mod_smpp_globals.gateways, data); + + if ( !gateway ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "NO SUCH SMPP GATEWAY[%s].", data); + return SWITCH_STATUS_GENERR; + } + + mod_smpp_gateway_send_message(gateway, message); + return SWITCH_STATUS_SUCCESS; +} + +/* static void name (switch_core_session_t *session, const char *data) */ +SWITCH_STANDARD_APP(mod_smpp_app_send_function) +{ + switch_event_header_t *chan_var = NULL; + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_event_t *message = NULL; + + if (switch_event_create(&message, SWITCH_EVENT_MESSAGE) != SWITCH_STATUS_SUCCESS) { + return; + } + + /* Copy over recognized channel vars. Then call the chat send function */ + /* Cycle through all of the channel headers, and ones with 'smpp_' prefix copy over without the prefix */ + for ( chan_var = switch_channel_variable_first(channel); chan_var; chan_var = chan_var->next) { + if ( !strncmp(chan_var->name, "smpp_", 5) ) { + switch_event_add_header_string(message, SWITCH_STACK_BOTTOM, chan_var->name + 5, chan_var->value); + } else { + switch_event_add_header_string(message, SWITCH_STACK_BOTTOM, chan_var->name, chan_var->value); + } + } + + /* Unlock the channel variables */ + switch_channel_variable_last(channel); + mod_smpp_chat_send_function(message, data); + return; +} + +/* static switch_status_t name (_In_opt_z_ const char *cmd, _In_opt_ switch_core_session_t *session, _In_ switch_stream_handle_t *stream) */ +SWITCH_STANDARD_API(mod_smpp_debug_api) +{ + mod_smpp_globals.debug = switch_true(cmd); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "debug is %s\n", (mod_smpp_globals.debug ? "on" : "off") ); + return SWITCH_STATUS_SUCCESS; +} + +/* static switch_status_t name (_In_opt_z_ const char *cmd, _In_opt_ switch_core_session_t *session, _In_ switch_stream_handle_t *stream) */ +SWITCH_STANDARD_API(mod_smpp_send_api) +{ + mod_smpp_gateway_t *gateway = NULL; + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_event_t *message = NULL; + char *argv[1024] = { 0 }; + int argc = 0; + char *cmd_dup = strdup(cmd); + + if (!(argc = switch_separate_string(cmd_dup, '|', argv, (sizeof(argv) / sizeof(argv[0]))))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Invalid format. Must be | separated like: gateway|destination|source|message\n"); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + gateway = switch_core_hash_find(mod_smpp_globals.gateways, argv[0]); + + if ( !gateway ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "NO SUCH SMPP GATEWAY[%s].", argv[0]); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + if (switch_event_create(&message, SWITCH_EVENT_MESSAGE) != SWITCH_STATUS_SUCCESS) { + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + switch_event_add_header_string(message, SWITCH_STACK_BOTTOM, "destination_addr", argv[1]); + switch_event_add_header_string(message, SWITCH_STACK_BOTTOM, "source_addr", argv[2]); + switch_event_set_body(message, argv[3]); + + if (mod_smpp_gateway_send_message(gateway, message) != SWITCH_STATUS_SUCCESS) { + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + done: + switch_safe_free(cmd_dup); + return status; + +} + +switch_status_t mod_smpp_do_config() +{ + char *conf = "smpp.conf"; + switch_xml_t xml, cfg, gateways, gateway, params, param; + + if (!(xml = switch_xml_open_cfg(conf, &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", conf); + goto err; + } + + if ( (gateways = switch_xml_child(cfg, "gateways")) != NULL) { + for (gateway = switch_xml_child(gateways, "gateway"); gateway; gateway = gateway->next) { + mod_smpp_gateway_t *new_gateway = NULL; + char *host = NULL, *system_id = NULL, *password = NULL, *profile = NULL, *system_type = NULL; + int port = 0, debug = 0; + + char *name = (char *)switch_xml_attr_soft(gateway, "name"); + + // Load params + if ( (params = switch_xml_child(gateway, "params")) != NULL) { + for (param = switch_xml_child(params, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + if ( ! strncmp(var, "host", 4) ) { + host = (char *) switch_xml_attr_soft(param, "value"); + } else if ( ! strncmp(var, "port", 4) ) { + port = atoi(switch_xml_attr_soft(param, "value")); + } else if ( ! strncmp(var, "debug", 5) ) { + debug = atoi(switch_xml_attr_soft(param, "value")); + } else if ( ! strncmp(var, "system_id", 9) ) { + system_id = (char *) switch_xml_attr_soft(param, "value"); + } else if ( ! strncmp(var, "password", 8) ) { + password = (char *) switch_xml_attr_soft(param, "value"); + } else if ( ! strncmp(var, "profile", 7) ) { + profile = (char *) switch_xml_attr_soft(param, "value"); + } else if ( ! strncmp(var, "system_type", 11) ) { + system_type = (char *) switch_xml_attr_soft(param, "value"); + } + } + } + + if ( mod_smpp_gateway_create(&new_gateway, name, host, port, debug, system_id, password, system_type, profile) == SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created gateway[%s]\n", name); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create gateway[%s]\n", name); + } + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Gateways config is missing\n"); + goto err; + } + + return SWITCH_STATUS_SUCCESS; + + err: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Configuration failed\n"); + return SWITCH_STATUS_GENERR; +} + +/* switch_status_t name (switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */ +SWITCH_MODULE_LOAD_FUNCTION(mod_smpp_load) +{ + switch_api_interface_t *mod_smpp_api_interface; + switch_chat_interface_t *mod_smpp_chat_interface; + switch_chat_application_interface_t *mod_smpp_chat_app_interface; + switch_application_interface_t *mod_smpp_app_interface; + + /* connect my internal structure to the blank pointer passed to me */ + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + + memset(&mod_smpp_globals, 0, sizeof(mod_smpp_globals_t)); + mod_smpp_globals.pool = pool; + mod_smpp_globals.debug = 0; + switch_core_hash_init(&(mod_smpp_globals.gateways)); + + if ( mod_smpp_do_config() != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to load due to bad configs\n"); + return SWITCH_STATUS_TERM; + } + + SWITCH_ADD_CHAT(mod_smpp_chat_interface, "smpp", mod_smpp_interface_chat_send); + SWITCH_ADD_API(mod_smpp_api_interface, "smpp_debug", "mod_smpp toggle debug", mod_smpp_debug_api, NULL); + SWITCH_ADD_API(mod_smpp_api_interface, "smpp_send", "mod_smpp send", mod_smpp_send_api, NULL); + SWITCH_ADD_CHAT_APP(mod_smpp_chat_app_interface, "smpp_send", "send message to gateway", "send message to gateway", + mod_smpp_chat_send_function, "", SCAF_NONE); + SWITCH_ADD_APP(mod_smpp_app_interface, "smpp_send", NULL, NULL, mod_smpp_app_send_function, + "smpp_send", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_smpp_shutdown) +{ + switch_hash_index_t *hi; + mod_smpp_gateway_t *gateway = NULL; + /* loop through gateways, and destroy them */ + /* destroy gateways hash */ + + while ((hi = switch_core_hash_first(mod_smpp_globals.gateways))) { + switch_core_hash_this(hi, NULL, NULL, (void **)&gateway); + mod_smpp_gateway_destroy(&gateway); + switch_safe_free(hi); + } + + switch_core_hash_destroy(&(mod_smpp_globals.gateways)); + + return SWITCH_STATUS_SUCCESS; +} + +/* 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/mod/event_handlers/mod_smpp/mod_smpp.h b/src/mod/event_handlers/mod_smpp/mod_smpp.h new file mode 100644 index 0000000000..318ceb765d --- /dev/null +++ b/src/mod/event_handlers/mod_smpp/mod_smpp.h @@ -0,0 +1,109 @@ +/* +* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application +* Copyright (C) 2005-2015, 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): +* +* William King +* +* mod_smpp.c -- smpp client and server implementation using libsmpp +* +* using libsmpp from: http://cgit.osmocom.org/libsmpp/ +* +*/ + +#ifndef MOD_SMPP_H +#define MOD_SMPP_H + +#include +#include +#include +#include + +typedef struct mod_smpp_globals_s { + switch_memory_pool_t *pool; + switch_hash_t *gateways; + uint8_t debug; +} mod_smpp_globals_t; + +extern mod_smpp_globals_t mod_smpp_globals; + +typedef struct mod_smpp_gateway_s { + char *name; + char *host; + char *system_id; + char *password; + char *profile; + char *system_type; + char address_range[64]; + uint32_t port; + uint32_t sequence; + uint32_t running; + uint32_t debug; + switch_socket_t *socket; + switch_sockaddr_t *socketaddr; + switch_thread_t *thread; + switch_threadattr_t *thd_attr; + switch_mutex_t *conn_mutex; + switch_memory_pool_t *pool; +} mod_smpp_gateway_t; + +typedef struct mod_smpp_message_s { + submit_sm_t req; + submit_sm_resp_t res; +} mod_smpp_message_t; + +#define MOD_SMPP_TRANS_RESP "mod_smpp::bind_transceiver_resp" + +/* mod_smpp_gateway.c */ +switch_status_t mod_smpp_gateway_authenticate(mod_smpp_gateway_t *gateway); +switch_status_t mod_smpp_gateway_connect(mod_smpp_gateway_t *gateway); +switch_status_t mod_smpp_gateway_create(mod_smpp_gateway_t **gw, char *name, char*host, int port, int debug, char *system_id, + char *password, char *system_type, char *profile); +switch_status_t mod_smpp_gateway_destroy(mod_smpp_gateway_t **gateway); +switch_status_t mod_smpp_gateway_send_message(mod_smpp_gateway_t *gateway, switch_event_t *message); +switch_status_t mod_smpp_gateway_get_next_sequence(mod_smpp_gateway_t *gateway, uint32_t *seq); +switch_status_t mod_smpp_gateway_connection_read(mod_smpp_gateway_t *gateway, switch_event_t **event, unsigned int *command_id); +switch_status_t mod_smpp_gateway_send_enquire_link_response(mod_smpp_gateway_t *gateway); +switch_status_t mod_smpp_gateway_send_deliver_sm_response(mod_smpp_gateway_t *gateway, switch_event_t *event); + +/* mod_smpp_message.c */ +switch_status_t mod_smpp_message_encode_body(char *body, int length, unsigned char *bin, uint8_t *enc_length); +switch_status_t mod_smpp_message_create(mod_smpp_gateway_t *gateway, switch_event_t *event, mod_smpp_message_t **message); +switch_status_t mod_smpp_message_decode(mod_smpp_gateway_t *gateway, deliver_sm_t *res, switch_event_t **event); +switch_status_t mod_smpp_message_destroy(mod_smpp_message_t **msg); + +/* mod_smpp_utils.c */ +void mod_smpp_dump_pdu(void *pdu); + +#endif /* MOD_SMPP_H */ + +/* 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/mod/event_handlers/mod_smpp/mod_smpp_gateway.c b/src/mod/event_handlers/mod_smpp/mod_smpp_gateway.c new file mode 100644 index 0000000000..1bf3771912 --- /dev/null +++ b/src/mod/event_handlers/mod_smpp/mod_smpp_gateway.c @@ -0,0 +1,549 @@ +/* +* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application +* Copyright (C) 2005-2015, 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): +* +* William King +* +* mod_smpp.c -- smpp client and server implementation using libsmpp +* +* using libsmpp from: http://cgit.osmocom.org/libsmpp/ +* +*/ + + +#include + +static void *SWITCH_THREAD_FUNC mod_smpp_gateway_read_thread(switch_thread_t *thread, void *obj); + +switch_status_t mod_smpp_gateway_create(mod_smpp_gateway_t **gw, char *name, char *host, int port, int debug, char *system_id, + char *password, char *system_type, char *profile) { + mod_smpp_gateway_t *gateway = NULL; + switch_memory_pool_t *pool = NULL; + + switch_core_new_memory_pool(&pool); + + gateway = switch_core_alloc(pool, sizeof(mod_smpp_gateway_t)); + + gateway->pool = pool; + gateway->running = 1; + gateway->sequence = 1; + gateway->name = name ? switch_core_strdup(gateway->pool, name) : "default"; + gateway->host = host ? switch_core_strdup(gateway->pool, host) : "localhost"; + gateway->port = port ? port : 8000; + gateway->debug = debug; + gateway->system_id = system_id ? switch_core_strdup(gateway->pool, system_id) : "username"; + gateway->password = password ? switch_core_strdup(gateway->pool, password) : "password"; + gateway->system_type = system_type ? switch_core_strdup(gateway->pool, system_type) : "freeswitch_smpp"; + gateway->profile = profile ? switch_core_strdup(gateway->pool, profile) : "default"; + + if ( switch_sockaddr_info_get(&(gateway->socketaddr), gateway->host, SWITCH_INET, + gateway->port, 0, mod_smpp_globals.pool) != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to get socketaddr info\n"); + goto err; + } + + if ( switch_socket_create(&(gateway->socket), switch_sockaddr_get_family(gateway->socketaddr), + SOCK_STREAM, SWITCH_PROTO_TCP, mod_smpp_globals.pool) != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create the socket\n"); + goto err; + } + + switch_mutex_init(&(gateway->conn_mutex), SWITCH_MUTEX_UNNESTED, mod_smpp_globals.pool); + if (mod_smpp_gateway_connect(gateway) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to connect to gateway[%s]\n", gateway->name); + goto err; + } + + /* Start gateway read thread */ + switch_threadattr_create(&(gateway->thd_attr), mod_smpp_globals.pool); + switch_threadattr_detach_set(gateway->thd_attr, 1); + switch_threadattr_stacksize_set(gateway->thd_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&(gateway->thread), gateway->thd_attr, mod_smpp_gateway_read_thread, (void *) gateway, mod_smpp_globals.pool); + + switch_core_hash_insert(mod_smpp_globals.gateways, name, (void *) gateway); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Gateway %s created\n", gateway->host); + + *gw = gateway; + return SWITCH_STATUS_SUCCESS; + + err: + mod_smpp_gateway_destroy(&gateway); + return SWITCH_STATUS_GENERR; + +} + +switch_status_t mod_smpp_gateway_authenticate(mod_smpp_gateway_t *gateway) { + bind_transceiver_t *req_b = calloc(sizeof(bind_transceiver_t), 1); + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_event_t *event = NULL; + switch_size_t write_len = 0; + unsigned int command_id = 0; + uint8_t local_buffer[1024] = {0}; + int local_buffer_len = 1024; + + req_b->command_length = 0; + req_b->command_id = BIND_TRANSCEIVER; + req_b->command_status = ESME_ROK; + req_b->addr_npi = 1; + req_b->addr_ton = 1; + + strncpy( (char *)req_b->address_range, gateway->host, sizeof(req_b->address_range)); + + if ( gateway->system_id ) { + strncpy((char *)req_b->system_id, gateway->system_id, sizeof(req_b->system_id)); + } + + if ( gateway->password ) { + strncpy((char *)req_b->password, gateway->password, sizeof(req_b->password)); + } + + if ( gateway->system_type ) { + strncpy((char *)req_b->system_type, gateway->system_type, sizeof(req_b->system_type)); + } + + req_b->interface_version = SMPP_VERSION; + + mod_smpp_gateway_get_next_sequence(gateway, &(req_b->sequence_number)); + + // Not thread safe due to smpp34_errno and smpp34_strerror since they are global to running process. + // The risk here is that the errno and strerror variables will be corrupted due to race conditions if there are errors + if ( smpp34_pack2( local_buffer, sizeof(local_buffer), &local_buffer_len, (void*)req_b) != 0 ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error in smpp_pack():%d:\n%s\n", smpp34_errno, smpp34_strerror); + status = SWITCH_STATUS_GENERR; + goto done; + } + + write_len = local_buffer_len; + + if ( mod_smpp_globals.debug || gateway->debug ) { + mod_smpp_dump_pdu((void *) req_b); + } + + if ( switch_socket_send(gateway->socket, (char *) local_buffer, &write_len) != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send on the socket\n"); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + if( write_len != local_buffer_len ){ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "smpp: Was not able to send entire buffer\n"); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + /* Receive the response */ + if ( mod_smpp_gateway_connection_read(gateway, &event, &command_id) != SWITCH_STATUS_SUCCESS){ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Authentication failed for gateawy[%s]\n", gateway->name); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Authentication successful for gateway[%s]\n", gateway->name); + } + + done: + if ( (mod_smpp_globals.debug || gateway->debug ) && event ) { + char *str = NULL; + switch_event_serialize(event, &str, SWITCH_TRUE); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Auth response: %s\n", str); + switch_safe_free(str); + } + + if ( req_b ) { + switch_safe_free(req_b); + } + + return status; +} + +/* When connecting to a gateway, the first message must be an authentication attempt */ +switch_status_t mod_smpp_gateway_connect(mod_smpp_gateway_t *gateway) { + switch_status_t status; + + if ( (status = switch_socket_connect(gateway->socket, gateway->socketaddr)) != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to connect the socket %d\n", status); + return SWITCH_STATUS_GENERR; + } + + if ( mod_smpp_gateway_authenticate(gateway) != SWITCH_STATUS_SUCCESS ) { + return SWITCH_STATUS_GENERR; + } + + return SWITCH_STATUS_SUCCESS; +} + + +/* Expects the gateway to be locked already */ +switch_status_t mod_smpp_gateway_connection_read(mod_smpp_gateway_t *gateway, switch_event_t **event, unsigned int *command_id) +{ + switch_size_t read_len = 4; /* Default to reading only the first 4 bytes to read how large the PDU is */ + switch_status_t status = SWITCH_STATUS_SUCCESS; + uint8_t *local_buffer = calloc(2048, 1); + uint32_t *local_buffer32 = (uint32_t *) local_buffer; /* TODO: Convert this into a union */ + switch_event_t *evt = NULL; + generic_nack_t *gennack = NULL; + char data[2048] = {0}; /* local buffer for unpacked PDU */ + + /* Read from socket */ + /* TODO: Add/Expand support for partial reads */ + if ( switch_socket_recv(gateway->socket, (char *) local_buffer, &read_len) != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to recv on the socket\n"); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + if ( read_len != 4 ){ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error in recv(PEEK) %d\n", (unsigned int )read_len); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + read_len = ntohl(*local_buffer32 ); + + if ( read_len > 1500 ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Corrupted PDU size from gateway [%s]\n", gateway->name); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + if ( ( status = switch_socket_recv(gateway->socket, (char *) local_buffer + 4, &read_len)) != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to recv on the socket %d\n", status); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + if ( smpp34_unpack2((void *)data, local_buffer, read_len + 4) ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "smpp: error decoding the receive buffer:%d:%s\n", smpp34_errno, smpp34_strerror); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + if ( mod_smpp_globals.debug || gateway->debug ) { + mod_smpp_dump_pdu((void *) data); + } + + gennack = (generic_nack_t *) data; + *command_id = gennack->command_id; + switch(*command_id) { + case BIND_TRANSCEIVER_RESP: + if ( gennack->command_status != ESME_ROK ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Authentication Failure %d\n", gennack->command_status); + } + break; + case DELIVER_SM: + if ( gennack->command_status == ESME_ROK ) { + deliver_sm_t *res = (deliver_sm_t *) data; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "New SMS received from[%s] to[%s] message[%s]\n", + res->source_addr, res->destination_addr, res->short_message); + + mod_smpp_message_decode(gateway, res, &evt); + } + break; + case ENQUIRE_LINK: + case ENQUIRE_LINK_RESP: + case SUBMIT_SM_RESP: + break; + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unrecognized Command ID: %u\n", *command_id); + } + + /* Only need to create/expand the event if it is one of the PDU's that we care about */ + if (evt) { + *event = evt; + } + + done: + switch_safe_free(local_buffer); + return status; +} + +static void *SWITCH_THREAD_FUNC mod_smpp_gateway_read_thread(switch_thread_t *thread, void *obj) +{ + mod_smpp_gateway_t *gateway = (mod_smpp_gateway_t *) obj; + + while ( gateway->running ) { + switch_event_t *event = NULL; + unsigned int command_id = 0; + + if ( mod_smpp_gateway_connection_read(gateway, &event, &command_id) != SWITCH_STATUS_SUCCESS) { + if ( gateway->running ) { + if ( mod_smpp_gateway_connect(gateway) != SWITCH_STATUS_SUCCESS) { + switch_sleep(1000 * 1000); + } + } + continue; + } + + if ( (mod_smpp_globals.debug || gateway->debug) && event) { + char *str = NULL; + switch_event_serialize(event, &str, 0); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Received message[%d]:\n%s\n\n", command_id, str); + switch_safe_free(str); + } + + switch(command_id) { + case BIND_TRANSCEIVER_RESP: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Why did we get an unexpected authentication response?\n"); + break; + case ENQUIRE_LINK: + mod_smpp_gateway_send_enquire_link_response(gateway); + break; + case DELIVER_SM: + if ( event ) { + char *str = NULL; + switch_event_serialize(event, &str, SWITCH_TRUE); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Inbound SMS packet event: \n%s\n\n", str); + switch_safe_free(str); + } + + mod_smpp_gateway_send_deliver_sm_response(gateway, event); + + switch_core_chat_send("smpp", event); + switch_event_destroy(&event); + /* Fire message to the chat plan, then respond */ + break; + case SUBMIT_SM_RESP: + case ENQUIRE_LINK_RESP: + break; + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown command_id[%d]\n", command_id); + if ( event ) { + char *str = NULL; + switch_event_serialize(event, &str, SWITCH_FALSE); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown packet event: %s\n", str); + switch_safe_free(str); + } + } + } + return 0; +} + +switch_status_t mod_smpp_gateway_send_enquire_link_response(mod_smpp_gateway_t *gateway) +{ + enquire_link_resp_t *enquire_resp = calloc(sizeof(enquire_link_resp_t), 1); + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_size_t write_len = 0; + uint8_t local_buffer[128] = {0}; + int local_buffer_len = sizeof(local_buffer); + + enquire_resp->command_length = 0; + enquire_resp->command_id = ENQUIRE_LINK_RESP; + enquire_resp->command_status = ESME_ROK; + + mod_smpp_gateway_get_next_sequence(gateway, &(enquire_resp->sequence_number)); + + if ( smpp34_pack2( local_buffer, sizeof(local_buffer), &local_buffer_len, (void*)enquire_resp) != 0 ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error in smpp_pack():%d:\n%s\n", smpp34_errno, smpp34_strerror); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + write_len = local_buffer_len; + + if ( mod_smpp_globals.debug || gateway->debug ) { + mod_smpp_dump_pdu((void *) enquire_resp); + } + + if ( switch_socket_send(gateway->socket, (char *) local_buffer, &write_len) != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send on the socket\n"); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + if( write_len != local_buffer_len ){ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "smpp: Was not able to send entire buffer\n"); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + done: + switch_safe_free(enquire_resp); + + return status; +} + +switch_status_t mod_smpp_gateway_send_deliver_sm_response(mod_smpp_gateway_t *gateway, switch_event_t *event) +{ + deliver_sm_resp_t *deliver_sm_resp = calloc(sizeof(deliver_sm_resp_t), 1); + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_size_t write_len = 0; + uint8_t local_buffer[128] = {0}; + int local_buffer_len = sizeof(local_buffer); + + deliver_sm_resp->command_length = 0; + deliver_sm_resp->command_id = DELIVER_SM_RESP; + deliver_sm_resp->command_status = ESME_ROK; +/* deliver_sm_resp->message_id = 0; Not used. calloc defaults to 0 */ + deliver_sm_resp->sequence_number = atoi(switch_event_get_header(event, "sequence_number")); + + if ( smpp34_pack2( local_buffer, sizeof(local_buffer), &local_buffer_len, (void*)deliver_sm_resp) != 0 ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error in smpp_pack():%d:\n%s\n", smpp34_errno, smpp34_strerror); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + write_len = local_buffer_len; + + if ( mod_smpp_globals.debug || gateway->debug ) { + mod_smpp_dump_pdu((void *) deliver_sm_resp); + } + + if ( switch_socket_send(gateway->socket, (char *) local_buffer, &write_len) != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send on the socket\n"); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + if( write_len != local_buffer_len ){ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "smpp: Was not able to send entire buffer\n"); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + done: + switch_safe_free(deliver_sm_resp); + return status; +} + +switch_status_t mod_smpp_gateway_send_unbind(mod_smpp_gateway_t *gateway) +{ + unbind_t *unbind_req = calloc(sizeof(unbind_t), 1); + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_size_t write_len = 0; + uint8_t local_buffer[128] = {0}; + int local_buffer_len = sizeof(local_buffer); + + unbind_req->command_length = 0; + unbind_req->command_id = UNBIND; + unbind_req->command_status = ESME_ROK; + + mod_smpp_gateway_get_next_sequence(gateway, &(unbind_req->sequence_number)); + + if ( smpp34_pack2( local_buffer, sizeof(local_buffer), &local_buffer_len, (void*)unbind_req) != 0 ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error in smpp_pack():%d:\n%s\n", smpp34_errno, smpp34_strerror); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + write_len = local_buffer_len; + + if ( mod_smpp_globals.debug || gateway->debug ) { + mod_smpp_dump_pdu((void *) unbind_req); + } + + if ( switch_socket_send(gateway->socket, (char *) local_buffer, &write_len) != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send on the socket\n"); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + if( write_len != local_buffer_len ){ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "smpp: Was not able to send entire buffer\n"); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + done: + switch_safe_free(unbind_req); + + return status; +} + +switch_status_t mod_smpp_gateway_destroy(mod_smpp_gateway_t **gw) +{ + mod_smpp_gateway_t *gateway = NULL; + + if ( !gw || !*gw ) { + return SWITCH_STATUS_SUCCESS; + } + + gateway = *gw; + + switch_core_hash_delete(mod_smpp_globals.gateways, gateway->name); + + gateway->running = 0; + + mod_smpp_gateway_send_unbind(gateway); + + switch_socket_shutdown(gateway->socket, SWITCH_SHUTDOWN_READWRITE); + switch_socket_close(gateway->socket); + + switch_core_destroy_memory_pool(&(gateway->pool)); + switch_mutex_destroy(gateway->conn_mutex); + + *gw = NULL; + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t mod_smpp_gateway_get_next_sequence(mod_smpp_gateway_t *gateway, uint32_t *seq) +{ + + switch_mutex_lock(gateway->conn_mutex); + gateway->sequence++; + *seq = gateway->sequence; + switch_mutex_unlock(gateway->conn_mutex); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t mod_smpp_gateway_send_message(mod_smpp_gateway_t *gateway, switch_event_t *message) { + mod_smpp_message_t *msg = NULL; + uint8_t local_buffer[1024]; + int local_buffer_len = sizeof(local_buffer); + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_size_t write_len = 0; + + if ( mod_smpp_message_create(gateway, message, &msg) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to send message due to message_create failure\n"); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + memset(local_buffer, 0, sizeof(local_buffer)); + + if( smpp34_pack2( local_buffer, sizeof(local_buffer), &local_buffer_len, (void*)&(msg->req)) != 0 ){ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "smpp: Unable to encode message:%d:\n%s\n", smpp34_errno, smpp34_strerror); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + write_len = local_buffer_len; + + if ( mod_smpp_gateway_get_next_sequence(gateway, &(msg->req.sequence_number)) ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to fetch next gateway sequence number\n"); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + if ( mod_smpp_globals.debug || gateway->debug ) { + mod_smpp_dump_pdu((void *) &(msg->req)); + } + + if ( switch_socket_send(gateway->socket, (char *) local_buffer, &write_len) != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send on the socket\n"); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + if ( write_len != local_buffer_len ){ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "smpp: Did not send all of message to gateway"); + switch_goto_status(SWITCH_STATUS_GENERR, done); + } + + done: + mod_smpp_message_destroy(&msg); + return status; +} + + +/* 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/mod/event_handlers/mod_smpp/mod_smpp_message.c b/src/mod/event_handlers/mod_smpp/mod_smpp_message.c new file mode 100644 index 0000000000..6c168b5b83 --- /dev/null +++ b/src/mod/event_handlers/mod_smpp/mod_smpp_message.c @@ -0,0 +1,192 @@ +/* +* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application +* Copyright (C) 2005-2015, 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): +* +* William King +* +* mod_smpp.c -- smpp client and server implementation using libsmpp +* +* using libsmpp from: http://cgit.osmocom.org/libsmpp/ +* +*/ + +#include + +switch_status_t mod_smpp_message_encode_body(char *body, int length, unsigned char *bin, uint8_t *enc_length) +{ + int i = 0; + + for ( i = 0; i < length; i++ ) { + bin[i*2] = body[i] / 16; + bin[i*2 + 1] = body[i] % 16; + } + + *enc_length = (i * 2) + 1; + return SWITCH_STATUS_SUCCESS; +} + +/* + Scratch notes taken during development/interop: + + char *message = "5361792048656c6c6f20746f204d79204c6974746c6520467269656e64"; + ''.join('%02x' % ord(c) for c in u'Скажите привет моему маленькому другу'.encode('utf16')) + + Variable length UTF-16 russian text: + + char *message = "fffe21043a043004360438044204350420003f044004380432043504420420003c043e0435043c04430420003c0430043b0435043d044c043a043e043c044304200034044004430433044304"; + char *mesg_txt = "This is a test SMS message from FreeSWITCH over SMPP"; + */ + +switch_status_t mod_smpp_message_create(mod_smpp_gateway_t *gateway, switch_event_t *event, mod_smpp_message_t **message) +{ + mod_smpp_message_t *msg = calloc(1, sizeof(mod_smpp_message_t)); + char *body = switch_event_get_body(event); + + assert(*message == NULL); + + if ( !body ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to encode message missing body\n"); + goto err; + } + + if ( mod_smpp_globals.debug || gateway->debug ) { + char *str = NULL; + switch_event_serialize(event, &str, 0); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Creating message from event:\n%s\n", str); + switch_safe_free(str); + } + + msg->req.command_id = SUBMIT_SM; + msg->req.command_status = ESME_ROK; + msg->req.command_length = 0; + msg->req.protocol_id = 0; + msg->req.priority_flag = 0; + msg->req.registered_delivery = 0; + msg->req.replace_if_present_flag = 0; + msg->req.sm_default_msg_id = 0; + msg->req.data_coding = 0; + msg->req.source_addr_ton = 1; + msg->req.source_addr_npi = 1; + msg->req.dest_addr_ton = 1; + msg->req.dest_addr_npi = 1; + msg->req.esm_class = 1; /* 0 => default, 1 => datagram, 2 => forward(transaction), 3 => store and forward + 2 is endpoint delivery, all others are into a DB first. + */ + + mod_smpp_gateway_get_next_sequence(gateway, &(msg->req.sequence_number)); + + snprintf((char *)msg->req.service_type, sizeof(msg->req.service_type), "%s", "SMS"); + snprintf((char *)msg->req.source_addr, sizeof(msg->req.source_addr), "%s", switch_event_get_header(event, "from_user")); + snprintf((char *)msg->req.destination_addr, sizeof(msg->req.destination_addr), "%s", switch_event_get_header(event, "to_user")); + snprintf((char *)msg->req.schedule_delivery_time, sizeof(msg->req.schedule_delivery_time), "%s", ""); + snprintf((char *)msg->req.validity_period, sizeof(msg->req.validity_period), "%s", ""); + snprintf((char *)msg->req.short_message, sizeof(msg->req.short_message), "%s", body); + msg->req.sm_length = strlen(body); + + if ( 0 && mod_smpp_message_encode_body(body, strlen(body), + (unsigned char *) &(msg->req.short_message), &(msg->req.sm_length)) != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to encode message body\n"); + goto err; + } + + *message = msg; + return SWITCH_STATUS_SUCCESS; + + err: + switch_safe_free(msg); + return SWITCH_STATUS_GENERR; +} + +switch_status_t mod_smpp_message_decode(mod_smpp_gateway_t *gateway, deliver_sm_t *res, switch_event_t **event) +{ + switch_event_t *evt = NULL; + char *str = NULL; + + if (switch_event_create(&evt, SWITCH_EVENT_MESSAGE) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create new event\n"); + } + + switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM, "endpoint", "mod_smpp"); + + str = switch_mprintf("%d", res->sequence_number); + switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, "sequence_number", str); + + str = switch_mprintf("%d", res->command_status); + switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, "command_status", str); + + str = switch_mprintf("%d", res->command_id); + switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, "command_id", str); + + switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM, "smpp_gateway", gateway->name); + switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM, "proto", "smpp"); + + str = switch_mprintf("%d", res->source_addr_ton); + switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, "source_addr_ton", str); + + str = switch_mprintf("%d", res->source_addr_npi); + switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, "source_addr_npi", str); + + switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM, "from_user", (const char *) res->source_addr); + + str = switch_mprintf("%d", res->dest_addr_ton); + switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, "dest_addr_ton", str); + + str = switch_mprintf("%d", res->dest_addr_npi); + switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, "dest_addr_npi", str); + + switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM, "to_user", (const char *) res->destination_addr); + + str = switch_mprintf("%d", res->data_coding); + switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, "data_coding", str); + str = NULL; + + switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM, "profile", gateway->profile); + + switch_event_add_body(evt, "%s", (const char *) res->short_message); + + *event = evt; + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t mod_smpp_message_destroy(mod_smpp_message_t **msg) +{ + if ( msg ) { + switch_safe_free(*msg); + } + + return SWITCH_STATUS_SUCCESS; +} + + +/* 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/mod/event_handlers/mod_smpp/mod_smpp_utils.c b/src/mod/event_handlers/mod_smpp/mod_smpp_utils.c new file mode 100644 index 0000000000..d948372d2b --- /dev/null +++ b/src/mod/event_handlers/mod_smpp/mod_smpp_utils.c @@ -0,0 +1,58 @@ +/* +* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application +* Copyright (C) 2005-2015, 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): +* +* William King +* +* mod_smpp.c -- smpp client and server implementation using libsmpp +* +* using libsmpp from: http://cgit.osmocom.org/libsmpp/ +* +*/ + +#include + +void mod_smpp_dump_pdu(void *pdu) +{ + uint8_t *print_buffer = calloc(4096, 1); + + if ( !smpp34_dumpPdu2(print_buffer, 4096, (void*) pdu) ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "PDU \n%s\n", print_buffer); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error in smpp_dumpPdu():%d:\n%s\n", smpp34_errno, smpp34_strerror); + } + + free(print_buffer); +} + +/* 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: + */