Adding mod_smpp as an event_handler module.
FS-7730 #resolve
This commit is contained in:
parent
1a47cd0d44
commit
775ce5c0a8
|
@ -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
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<configuration name="smpp.conf" description="SMPP client and server Gateway">
|
||||
<gateways>
|
||||
<gateway name="example.com">
|
||||
<params>
|
||||
<param name="host" value="example.com"/>
|
||||
<param name="port" value="2775"/>
|
||||
<param name="debug" value="1"/>
|
||||
<param name="profile" value="default"/>
|
||||
<param name="system_id" value="username"/>
|
||||
<param name="password" value="password"/>
|
||||
<param name="system_type" value="remote_smpp"/>
|
||||
</params>
|
||||
</gateway>
|
||||
</gateways>
|
||||
</configuration>
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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. ... ?
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<include>
|
||||
<context name="default">
|
||||
|
||||
<extension name="smpp34_demo_in">
|
||||
<condition field="to_user" expression="^12345551234$"/>
|
||||
<condition field="from_user" expression="^(\d{11})$">
|
||||
<action application="set" data="to=1001@192.168.100.100"/>
|
||||
<action application="set" data="from=$1@192.168.100.100"/>
|
||||
<action application="set" data="proto="/>
|
||||
<action application="set" data="dest_proto=sip"/>
|
||||
<action application="info"/>
|
||||
<action application="set" data="final_delivery=1"/>
|
||||
<action application="send"/>
|
||||
</condition>
|
||||
</extension>
|
||||
</context>
|
||||
|
||||
<context name="public">
|
||||
|
||||
<extension name="smpp34_demo_out">
|
||||
<condition field="from_user" expression="^1001$"/>
|
||||
<condition field="to_user" expression="^(\d{11})$">
|
||||
<action application="set" data="from_user=12345551234"/>
|
||||
<action application="set" data="final_delivery=1"/>
|
||||
<action application="info"/>
|
||||
<action application="smpp_send" data="smppgateway"/>
|
||||
</condition>
|
||||
</extension>
|
||||
|
||||
</context>
|
||||
</include>
|
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005-2015, Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* 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 <anthm@freeswitch.org>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* William King <william.king@quentustech.com>
|
||||
*
|
||||
* mod_smpp.c -- smpp client and server implementation using libsmpp
|
||||
*
|
||||
* using libsmpp from: http://cgit.osmocom.org/libsmpp/
|
||||
*
|
||||
*/
|
||||
|
||||
#include <mod_smpp.h>
|
||||
|
||||
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:
|
||||
*/
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005-2015, Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* 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 <anthm@freeswitch.org>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* William King <william.king@quentustech.com>
|
||||
*
|
||||
* 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 <switch.h>
|
||||
#include <smpp34.h>
|
||||
#include <smpp34_structs.h>
|
||||
#include <smpp34_params.h>
|
||||
|
||||
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:
|
||||
*/
|
|
@ -0,0 +1,549 @@
|
|||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005-2015, Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* 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 <anthm@freeswitch.org>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* William King <william.king@quentustech.com>
|
||||
*
|
||||
* mod_smpp.c -- smpp client and server implementation using libsmpp
|
||||
*
|
||||
* using libsmpp from: http://cgit.osmocom.org/libsmpp/
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <mod_smpp.h>
|
||||
|
||||
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:
|
||||
*/
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005-2015, Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* 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 <anthm@freeswitch.org>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* William King <william.king@quentustech.com>
|
||||
*
|
||||
* mod_smpp.c -- smpp client and server implementation using libsmpp
|
||||
*
|
||||
* using libsmpp from: http://cgit.osmocom.org/libsmpp/
|
||||
*
|
||||
*/
|
||||
|
||||
#include <mod_smpp.h>
|
||||
|
||||
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:
|
||||
*/
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005-2015, Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* 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 <anthm@freeswitch.org>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* William King <william.king@quentustech.com>
|
||||
*
|
||||
* mod_smpp.c -- smpp client and server implementation using libsmpp
|
||||
*
|
||||
* using libsmpp from: http://cgit.osmocom.org/libsmpp/
|
||||
*
|
||||
*/
|
||||
|
||||
#include <mod_smpp.h>
|
||||
|
||||
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:
|
||||
*/
|
Loading…
Reference in New Issue