/* ====================================================================
* The Kannel Software License, Version 1.0
*
* Copyright (c) 2001-2005 Kannel Group
* Copyright (c) 1998-2001 WapIT Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Kannel Group (http://www.kannel.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Kannel" and "Kannel Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please
* contact org@kannel.org.
*
* 5. Products derived from this software may not be called "Kannel",
* nor may "Kannel" appear in their name, without prior written
* permission of the Kannel Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Kannel Group. For more information on
* the Kannel Group, please see .
*
* Portions of this software are based upon software originally written at
* WapIT Ltd., Helsinki, Finland for the Kannel project.
*/
/*
* drive_smpp.c - SMPP server for testing purposes
*
* Lars Wirzenius
*/
#include
#include
#include
#include
#include "gwlib/gwlib.h"
#include "gw/smsc/smpp_pdu.h"
#include "gw/msg.h"
static int quitting = 0;
static Octstr *smsc_system_id;
static Octstr *smsc_source_addr;
static Counter *message_id_counter;
static Octstr *bearerbox_host;
static int port_for_smsbox;
static Counter *num_to_esme;
static long max_to_esme;
static Counter *num_from_bearerbox;
static Counter *num_to_bearerbox;
static Counter *num_from_esme;
static time_t start_time = (time_t) -1;
static time_t first_to_esme = (time_t) -1;
static time_t last_to_esme = (time_t) -1;
static time_t last_from_esme = (time_t) -1;
static time_t first_from_bb = (time_t) -1;
static time_t last_to_bb = (time_t) -1;
static long enquire_interval = 1; /* Measured in messages, not time. */
static void quit(void)
{
quitting = 1;
gwthread_wakeup_all();
}
typedef struct {
Connection *conn;
int transmitter;
int receiver;
} ESME;
static ESME *esme_create(Connection *conn)
{
ESME *esme;
esme = gw_malloc(sizeof(*esme));
esme->conn = conn;
esme->transmitter = 0;
esme->receiver = 0;
return esme;
}
static void esme_destroy(ESME *esme)
{
if (esme != NULL) {
conn_destroy(esme->conn);
gw_free(esme);
}
}
static SMPP_PDU *handle_bind_transmitter(ESME *esme, SMPP_PDU *pdu)
{
SMPP_PDU *resp;
esme->transmitter = 1;
resp = smpp_pdu_create(bind_transmitter_resp,
pdu->u.bind_transmitter.sequence_number);
#if 0 /* XXX system_id is not implemented in the PDU at the moment */
resp->u.bind_transmitter_resp.system_id =
octstr_duplicate(smsc_system_id);
#endif
return resp;
}
static SMPP_PDU *handle_bind_receiver(ESME *esme, SMPP_PDU *pdu)
{
SMPP_PDU *resp;
esme->receiver = 1;
resp = smpp_pdu_create(bind_receiver_resp,
pdu->u.bind_receiver.sequence_number);
#if 0 /* XXX system_id is not implemented in the PDU at the moment */
resp->u.bind_receiver_resp.system_id = octstr_duplicate(smsc_system_id);
#endif
return resp;
}
static SMPP_PDU *handle_submit_sm(ESME *esme, SMPP_PDU *pdu)
{
SMPP_PDU *resp;
unsigned long id;
debug("test.smpp", 0, "submit_sm: short_message = <%s>",
octstr_get_cstr(pdu->u.submit_sm.short_message));
id = counter_increase(num_from_esme) + 1;
if (id == max_to_esme)
info(0, "ESME has submitted all messages to SMSC.");
time(&last_from_esme);
resp = smpp_pdu_create(submit_sm_resp, pdu->u.submit_sm.sequence_number);
#if 0 /* XXX message_id is not implemented in the PDU at the moment */
resp->u.submit_sm_resp.message_id =
octstr_format("%ld", counter_increase(message_id_counter));
#endif
return resp;
}
static SMPP_PDU *handle_deliver_sm_resp(ESME *esme, SMPP_PDU *pdu)
{
return NULL;
}
static SMPP_PDU *handle_unbind(ESME *esme, SMPP_PDU *pdu)
{
SMPP_PDU *resp;
resp = smpp_pdu_create(unbind_resp, pdu->u.unbind.sequence_number);
return resp;
}
static SMPP_PDU *handle_enquire_link(ESME *esme, SMPP_PDU *pdu)
{
return smpp_pdu_create(enquire_link_resp,
pdu->u.enquire_link.sequence_number);
}
static SMPP_PDU *handle_enquire_link_resp(ESME *esme, SMPP_PDU *pdu)
{
return NULL;
}
static struct {
unsigned long type;
SMPP_PDU *(*handler)(ESME *, SMPP_PDU *);
} handlers[] = {
#define HANDLER(name) { name, handle_ ## name },
HANDLER(bind_transmitter)
HANDLER(bind_receiver)
HANDLER(submit_sm)
HANDLER(deliver_sm_resp)
HANDLER(unbind)
HANDLER(enquire_link)
HANDLER(enquire_link_resp)
#undef HANDLER
};
static int num_handlers = sizeof(handlers) / sizeof(handlers[0]);
static void handle_pdu(ESME *esme, SMPP_PDU *pdu)
{
SMPP_PDU *resp;
Octstr *os;
int i;
debug("test.smpp", 0, "Handling SMPP PDU of type %s", pdu->type_name);
for (i = 0; i < num_handlers; ++i) {
if (handlers[i].type == pdu->type) {
resp = handlers[i].handler(esme, pdu);
if (resp != NULL) {
os = smpp_pdu_pack(resp);
conn_write(esme->conn, os);
octstr_destroy(os);
smpp_pdu_destroy(resp);
}
return;
}
}
error(0, "Unhandled SMPP PDU.");
smpp_pdu_dump(pdu);
}
static void send_smpp_thread(void *arg)
{
ESME *esme;
Octstr *os;
SMPP_PDU *pdu;
unsigned long id;
esme = arg;
id = 0;
while (!quitting && counter_value(num_to_esme) < max_to_esme) {
id = counter_increase(num_to_esme) + 1;
while (!quitting && counter_value(num_from_esme) + 500 < id)
gwthread_sleep(1.0);
if (quitting)
break;
pdu = smpp_pdu_create(deliver_sm,
counter_increase(message_id_counter));
pdu->u.deliver_sm.source_addr = octstr_create("456");
pdu->u.deliver_sm.destination_addr = octstr_create("123");
pdu->u.deliver_sm.short_message = octstr_format("%ld", id);
os = smpp_pdu_pack(pdu);
conn_write(esme->conn, os);
octstr_destroy(os);
smpp_pdu_destroy(pdu);
if (first_to_esme == (time_t) -1)
time(&first_to_esme);
debug("test.smpp", 0,
"Delivered SMS %ld of %ld to bearerbox via SMPP.",
id, max_to_esme);
if ((id % enquire_interval) == 0) {
pdu = smpp_pdu_create(enquire_link,
counter_increase(message_id_counter));
os = smpp_pdu_pack(pdu);
conn_write(esme->conn, os);
octstr_destroy(os);
smpp_pdu_destroy(pdu);
debug("test.smpp", 0, "Sent enquire_link to bearerbox.");
}
}
time(&last_to_esme);
if (id == max_to_esme)
info(0, "All messages sent to ESME.");
debug("test.smpp", 0, "%s terminates.", __func__);
}
static void receive_smpp_thread(void *arg)
{
ESME *esme;
Octstr *os;
long len;
long sender_id;
SMPP_PDU *pdu;
esme = arg;
sender_id = -1;
len = 0;
while (!quitting && conn_wait(esme->conn, -1.0) != -1) {
for (;;) {
if (len == 0) {
len = smpp_pdu_read_len(esme->conn);
if (len == -1) {
error(0, "Client sent garbage, closing connection.");
goto error;
} else if (len == 0) {
if (conn_eof(esme->conn) || conn_error(esme->conn))
goto error;
break;
}
}
gw_assert(len > 0);
os = smpp_pdu_read_data(esme->conn, len);
if (os != NULL) {
len = 0;
pdu = smpp_pdu_unpack(os);
if (pdu == NULL) {
error(0, "PDU unpacking failed!");
octstr_dump(os, 0);
} else {
handle_pdu(esme, pdu);
smpp_pdu_destroy(pdu);
}
octstr_destroy(os);
} else if (conn_eof(esme->conn) || conn_error(esme->conn))
goto error;
else
break;
}
if (!quitting && esme->receiver && sender_id == -1)
sender_id = gwthread_create(send_smpp_thread, esme);
}
error:
if (sender_id != -1) {
quit();
gwthread_join(sender_id);
}
esme_destroy(esme);
quit();
debug("test.smpp", 0, "%s terminates.", __func__);
}
static void smsbox_thread(void *arg)
{
Connection *conn;
Msg *msg;
Octstr *os;
Octstr *reply_msg;
unsigned long count;
msg = msg_create(sms);
msg->sms.sender = octstr_create("123");
msg->sms.receiver = octstr_create("456");
msg->sms.msgdata = octstr_create("hello world");
reply_msg = msg_pack(msg);
msg_destroy(msg);
gwthread_sleep(1.0);
conn = conn_open_tcp(bearerbox_host, port_for_smsbox, NULL);
if (conn == NULL) {
gwthread_sleep(2.0);
conn = conn_open_tcp(bearerbox_host, port_for_smsbox, NULL);
if (conn == NULL)
panic(0, "Couldn't connect to bearerbox as smsbox");
}
while (!quitting && conn_wait(conn, -1.0) != -1) {
for (;;) {
os = conn_read_withlen(conn);
if (os == NULL) {
if (conn_eof(conn) || conn_error(conn))
goto error;
break;
}
msg = msg_unpack(os);
if (msg == NULL || msg->type == wdp_datagram)
error(0, "Bearerbox sent garbage to smsbox");
if (msg->type == sms) {
if (first_from_bb == (time_t) -1)
time(&first_from_bb);
count = counter_increase(num_from_bearerbox) + 1;
debug("test.smpp", 0,
"Bearerbox sent sms #%ld <%s> to smsbox, sending reply.",
count, octstr_get_cstr(msg->sms.msgdata));
if (count == max_to_esme)
info(0, "Bearerbox has sent all messages to smsbox.");
conn_write_withlen(conn, reply_msg);
counter_increase(num_to_bearerbox);
}
msg_destroy(msg);
octstr_destroy(os);
time(&last_to_bb);
}
}
error:
conn_destroy(conn);
octstr_destroy(reply_msg);
debug("test.smpp", 0, "%s terminates.", __func__);
}
static void accept_thread(void *arg)
{
int fd;
int new_fd;
int port;
socklen_t addrlen;
struct sockaddr addr;
long smsbox_thread_id;
port = *(int *) arg;
fd = make_server_socket(port, NULL);
if (fd == -1)
panic(0, "Couldn't create SMPP listen port.");
smsbox_thread_id = -1;
for (;;) {
if (gwthread_pollfd(fd, POLLIN, -1.0) != POLLIN)
break;
addrlen = sizeof(addr);
new_fd = accept(fd, &addr, &addrlen);
if (start_time == (time_t) -1)
time(&start_time);
gwthread_create(receive_smpp_thread,
esme_create(conn_wrap_fd(new_fd, 0)));
if (smsbox_thread_id == -1)
smsbox_thread_id = gwthread_create(smsbox_thread, NULL);
}
debug("test.smpp", 0, "%s terminates.", __func__);
}
static void handler(int signal)
{
panic(0, "Caught signal %d.", signal);
}
static void help(void)
{
info(0, "drive_smpp [-h] [-v level] [-p port]");
}
int main(int argc, char **argv)
{
struct sigaction act;
int http_port;
int port;
int opt;
double run_time;
char *log_file;
gwlib_init();
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGTERM, &act, NULL);
sigaction(SIGINT, &act, NULL);
port = 2345;
http_port = 8080;
smsc_system_id = octstr_create("kannel_smpp");
smsc_source_addr = octstr_create("123456");
message_id_counter = counter_create();
bearerbox_host = octstr_create("127.0.0.1");
port_for_smsbox = 13001;
max_to_esme = 1;
num_to_esme = counter_create();
num_from_esme = counter_create();
num_to_bearerbox = counter_create();
num_from_bearerbox = counter_create();
log_file = NULL;
while ((opt = getopt(argc, argv, "hv:p:P:m:l:")) != EOF) {
switch (opt) {
case 'v':
log_set_output_level(atoi(optarg));
break;
case 'h':
help();
exit(0);
case 'm':
max_to_esme = atoi(optarg);
break;
case 'p':
port = atoi(optarg);
break;
case 'P':
http_port = atoi(optarg);
break;
case 'l':
log_file = optarg;
break;
case '?':
default:
error(0, "Invalid option %c", opt);
help();
panic(0, "Stopping.");
}
}
if (log_file != NULL)
log_open(log_file, GW_DEBUG, GW_NON_EXCL);
info(0, "Starting drive_smpp test.");
gwthread_create(accept_thread, &port);
gwthread_join_all();
debug("test.smpp", 0, "Program exiting normally.");
run_time = difftime(last_from_esme, first_to_esme);
info(0, "Number of messages sent to ESME: %ld",
counter_value(num_to_esme));
info(0, "Number of messages sent to smsbox: %ld",
counter_value(num_from_bearerbox));
info(0, "Number of messages sent to bearerbox: %ld",
counter_value(num_to_bearerbox));
info(0, "Number of messages sent to SMSC: %ld",
counter_value(num_from_esme));
info(0, "Time: %.0f secs", run_time);
info(0, "Time until all sent to ESME: %.0f secs",
difftime(last_to_esme, start_time));
info(0, "Time from first from bb to last to bb: %.0f secs",
difftime(last_to_bb, first_from_bb));
info(0, "Time until all sent to SMSC: %.0f secs",
difftime(last_from_esme, start_time));
info(0, "SMPP messages SMSC to ESME: %.1f msgs/sec",
counter_value(num_to_esme) / run_time);
info(0, "SMPP messages ESME to SMSC: %.1f msgs/sec",
counter_value(num_from_esme) / run_time);
octstr_destroy(smsc_system_id);
octstr_destroy(smsc_source_addr);
octstr_destroy(bearerbox_host);
counter_destroy(num_to_esme);
counter_destroy(num_from_esme);
counter_destroy(num_to_bearerbox);
counter_destroy(num_from_bearerbox);
counter_destroy(message_id_counter);
gwlib_shutdown();
return 0;
}