From c0de14da8f868de3a68b485ec382d8aa7dabbec9 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 23 Nov 2012 23:35:01 +0100 Subject: SMPP: add small utility program 'smpp_mirror' This program binds as ESME transceiver to a SMSC and simply mirrors back all SMS that it receives. --- openbsc/include/openbsc/gsm_data.h | 4 +- openbsc/src/libmsc/db.c | 7 +- openbsc/src/libmsc/gsm_04_11.c | 23 +-- openbsc/src/libmsc/smpp_openbsc.c | 23 ++- openbsc/src/utils/Makefile.am | 8 + openbsc/src/utils/smpp_mirror.c | 316 +++++++++++++++++++++++++++++++++++++ 6 files changed, 357 insertions(+), 24 deletions(-) create mode 100644 openbsc/src/utils/smpp_mirror.c diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h index 3b0f248b2..ea9f601ea 100644 --- a/openbsc/include/openbsc/gsm_data.h +++ b/openbsc/include/openbsc/gsm_data.h @@ -293,7 +293,7 @@ struct gsm_sms { unsigned long long id; struct gsm_subscriber *sender; struct gsm_subscriber *receiver; - struct gsm_sms_addr destination; + struct gsm_sms_addr src, dst; enum gsm_sms_source_id source; struct { @@ -310,8 +310,6 @@ struct gsm_sms { uint8_t protocol_id; uint8_t data_coding_scheme; uint8_t msg_ref; - char dest_addr[20+1]; /* DA LV is 12 bytes max, i.e. 10 bytes - * BCD == 20 bytes string */ uint8_t user_data_len; uint8_t user_data[SMS_TEXT_SIZE]; diff --git a/openbsc/src/libmsc/db.c b/openbsc/src/libmsc/db.c index 790cde37f..9a5f18db7 100644 --- a/openbsc/src/libmsc/db.c +++ b/openbsc/src/libmsc/db.c @@ -993,7 +993,7 @@ int db_sms_store(struct gsm_sms *sms) /* FIXME: generate validity timestamp based on validity_minutes */ dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text); - dbi_conn_quote_string_copy(conn, (char *)sms->dest_addr, &q_daddr); + dbi_conn_quote_string_copy(conn, (char *)sms->dst.addr, &q_daddr); dbi_conn_quote_binary_copy(conn, sms->user_data, sms->user_data_len, &q_udata); /* FIXME: correct validity period */ @@ -1035,6 +1035,7 @@ static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result resul sender_id = dbi_result_get_ulonglong(result, "sender_id"); sms->sender = subscr_get_by_id(net, sender_id); + strncpy(sms->src.addr, sms->sender->extension, sizeof(sms->src.addr)-1); receiver_id = dbi_result_get_ulonglong(result, "receiver_id"); sms->receiver = subscr_get_by_id(net, receiver_id); @@ -1051,8 +1052,8 @@ static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result resul daddr = dbi_result_get_string(result, "dest_addr"); if (daddr) { - strncpy(sms->dest_addr, daddr, sizeof(sms->dest_addr)); - sms->dest_addr[sizeof(sms->dest_addr)-1] = '\0'; + strncpy(sms->dst.addr, daddr, sizeof(sms->dst.addr)); + sms->dst.addr[sizeof(sms->dst.addr)-1] = '\0'; } sms->user_data_len = dbi_result_get_field_length(result, "user_data"); diff --git a/openbsc/src/libmsc/gsm_04_11.c b/openbsc/src/libmsc/gsm_04_11.c index 5beae5301..be6b5b078 100644 --- a/openbsc/src/libmsc/gsm_04_11.c +++ b/openbsc/src/libmsc/gsm_04_11.c @@ -143,12 +143,13 @@ struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, int dcs, const ch /* FIXME: don't use ID 1 static */ sms->sender = subscr_get_by_id(receiver->net, 1); + strncpy(sms->src.addr, sms->sender->extension, sizeof(sms->src.addr)-1); sms->reply_path_req = 0; sms->status_rep_req = 0; sms->ud_hdr_ind = 0; sms->protocol_id = 0; /* implicit */ sms->data_coding_scheme = dcs; - strncpy(sms->dest_addr, receiver->extension, sizeof(sms->dest_addr)-1); + strncpy(sms->dst.addr, receiver->extension, sizeof(sms->dst.addr)-1); /* Generate user_data */ sms->user_data_len = gsm_7bit_encode(sms->user_data, sms->text); @@ -270,10 +271,10 @@ static int gsm340_rx_sms_submit(struct msgb *msg, struct gsm_sms *gsms) /* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */ static int gsm340_gen_oa_sub(uint8_t *oa, unsigned int oa_len, - struct gsm_subscriber *subscr) + const struct gsm_sms_addr *src) { /* network specific, private numbering plan */ - return gsm340_gen_oa(oa, oa_len, 0x3, 0x9, subscr->extension); + return gsm340_gen_oa(oa, oa_len, src->ton, src->npi, src->addr); } /* generate a msgb containing a TPDU derived from struct gsm_sms, @@ -299,9 +300,9 @@ static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms) /* TP-UDHI (indicating TP-UD contains a header) */ if (sms->ud_hdr_ind) *smsp |= 0x40; - + /* generate originator address */ - oa_len = gsm340_gen_oa_sub(oa, sizeof(oa), sms->sender); + oa_len = gsm340_gen_oa_sub(oa, sizeof(oa), &sms->src); smsp = msgb_put(msg, oa_len); memcpy(smsp, oa, oa_len); @@ -392,11 +393,11 @@ static int gsm340_rx_tpdu(struct gsm_subscriber_connection *conn, struct msgb *m /* mangle first byte to reflect length in bytes, not digits */ address_lv[0] = da_len_bytes - 1; - gsms->destination.ton = (address_lv[1] >> 4) & 7; - gsms->destination.npi = address_lv[1] & 0xF; + gsms->dst.ton = (address_lv[1] >> 4) & 7; + gsms->dst.npi = address_lv[1] & 0xF; /* convert to real number */ - gsm48_decode_bcd_number(gsms->destination.addr, - sizeof(gsms->destination.addr), address_lv, 1); + gsm48_decode_bcd_number(gsms->dst.addr, + sizeof(gsms->dst.addr), address_lv, 1); smsp += da_len_bytes; gsms->protocol_id = *smsp++; @@ -449,7 +450,7 @@ static int gsm340_rx_tpdu(struct gsm_subscriber_connection *conn, struct msgb *m "MR: 0x%02x PID: 0x%02x, DCS: 0x%02x, DA: %s, " "UserDataLength: 0x%02x, UserData: \"%s\"\n", subscr_name(gsms->sender), sms_mti, sms_vpf, gsms->msg_ref, - gsms->protocol_id, gsms->data_coding_scheme, gsms->dest_addr, + gsms->protocol_id, gsms->data_coding_scheme, gsms->dst.addr, gsms->user_data_len, sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text : osmo_hexdump(gsms->user_data, gsms->user_data_len)); @@ -460,7 +461,7 @@ static int gsm340_rx_tpdu(struct gsm_subscriber_connection *conn, struct msgb *m send_signal(0, NULL, gsms, 0); /* determine gsms->receiver based on dialled number */ - gsms->receiver = subscr_get_by_extension(conn->bts->network, gsms->dest_addr); + gsms->receiver = subscr_get_by_extension(conn->bts->network, gsms->dst.addr); if (!gsms->receiver) { #ifdef BUILD_SMPP rc = smpp_try_deliver(gsms); diff --git a/openbsc/src/libmsc/smpp_openbsc.c b/openbsc/src/libmsc/smpp_openbsc.c index b3b8d3697..378cbf63e 100644 --- a/openbsc/src/libmsc/smpp_openbsc.c +++ b/openbsc/src/libmsc/smpp_openbsc.c @@ -118,9 +118,18 @@ static int submit_to_sms(struct gsm_sms **psms, struct gsm_network *net, sms = sms_alloc(); sms->source = SMS_SOURCE_SMPP; sms->smpp.sequence_nr = submit->sequence_number; + + /* fill in the destination address */ sms->receiver = dest; - strncpy(sms->dest_addr, dest->extension, sizeof(sms->dest_addr)-1); + sms->dst.ton = submit->dest_addr_ton; + sms->dst.npi = submit->dest_addr_npi; + strncpy(sms->dst.addr, dest->extension, sizeof(sms->dst.addr)-1); + + /* fill in the source address */ sms->sender = subscr_get_by_id(net, 1); + sms->src.ton = submit->source_addr_ton; + sms->src.npi = submit->source_addr_npi; + strncpy(sms->src.addr, (char *)submit->source_addr, sizeof(sms->src.addr)-1); if (submit->esm_class & 0x40) sms->ud_hdr_ind = 1; @@ -312,9 +321,9 @@ static int deliver_to_esme(struct osmo_esme *esme, struct gsm_sms *sms) sms->sender->extension); } - deliver.dest_addr_ton = sms->destination.ton; - deliver.dest_addr_npi = sms->destination.npi; - memcpy(deliver.destination_addr, sms->destination.addr, + deliver.dest_addr_ton = sms->dst.ton; + deliver.dest_addr_npi = sms->dst.npi; + memcpy(deliver.destination_addr, sms->dst.addr, sizeof(deliver.destination_addr)); deliver.esm_class = 1; /* datagram mode */ @@ -368,9 +377,9 @@ int smpp_try_deliver(struct gsm_sms *sms) struct osmo_smpp_addr dst; memset(&dst, 0, sizeof(dst)); - dst.ton = sms->destination.ton; - dst.npi = sms->destination.npi; - memcpy(dst.addr, sms->destination.addr, sizeof(dst.addr)); + dst.ton = sms->dst.ton; + dst.npi = sms->dst.npi; + memcpy(dst.addr, sms->dst.addr, sizeof(dst.addr)); esme = smpp_route(g_smsc, &dst); if (!esme) diff --git a/openbsc/src/utils/Makefile.am b/openbsc/src/utils/Makefile.am index c5b6a7add..701e9b86b 100644 --- a/openbsc/src/utils/Makefile.am +++ b/openbsc/src/utils/Makefile.am @@ -4,6 +4,10 @@ AM_LDFLAGS = $(COVERAGE_LDFLAGS) bin_PROGRAMS = bs11_config isdnsync +if BUILD_SMPP +noinst_PROGRAMS = smpp_mirror +endif + bs11_config_SOURCES = bs11_config.c bs11_config_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ $(top_builddir)/src/libbsc/libbsc.a \ @@ -11,3 +15,7 @@ bs11_config_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOABIS_LIBS) isdnsync_SOURCES = isdnsync.c + +smpp_mirror_SOURCES = smpp_mirror.c +smpp_mirror_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) $(LIBSMPP34_LIBS) diff --git a/openbsc/src/utils/smpp_mirror.c b/openbsc/src/utils/smpp_mirror.c new file mode 100644 index 000000000..213c681e6 --- /dev/null +++ b/openbsc/src/utils/smpp_mirror.c @@ -0,0 +1,316 @@ +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +/* FIXME: merge with smpp_smsc.c */ +#define SMPP_SYS_ID_LEN 16 +enum esme_read_state { + READ_ST_IN_LEN = 0, + READ_ST_IN_MSG = 1, +}; +/* FIXME: merge with smpp_smsc.c */ + +struct esme { + struct osmo_fd ofd; + + uint32_t own_seq_nr; + + struct osmo_wqueue wqueue; + enum esme_read_state read_state; + uint32_t read_len; + uint32_t read_idx; + struct msgb *read_msg; + + uint8_t smpp_version; + char system_id[SMPP_SYS_ID_LEN+1]; + char password[SMPP_SYS_ID_LEN+1]; +}; + +/* FIXME: merge with smpp_smsc.c */ +#define SMPP34_UNPACK(rc, type, str, data, len) \ + memset(str, 0, sizeof(*str)); \ + rc = smpp34_unpack(type, str, data, len) +#define INIT_RESP(type, resp, req) { \ + memset((resp), 0, sizeof(*(resp))); \ + (resp)->command_length = 0; \ + (resp)->command_id = type; \ + (resp)->command_status = ESME_ROK; \ + (resp)->sequence_number = (req)->sequence_number; \ +} +#define PACK_AND_SEND(esme, ptr) pack_and_send(esme, (ptr)->command_id, ptr) +static inline uint32_t smpp_msgb_cmdid(struct msgb *msg) +{ + uint8_t *tmp = msgb_data(msg) + 4; + return ntohl(*(uint32_t *)tmp); +} +static uint32_t esme_inc_seq_nr(struct esme *esme) +{ + esme->own_seq_nr++; + if (esme->own_seq_nr > 0x7fffffff) + esme->own_seq_nr = 1; + + return esme->own_seq_nr; +} +static int pack_and_send(struct esme *esme, uint32_t type, void *ptr) +{ + struct msgb *msg = msgb_alloc(4096, "SMPP_Tx"); + int rc, rlen; + if (!msg) + return -ENOMEM; + + rc = smpp34_pack(type, msg->tail, msgb_tailroom(msg), &rlen, ptr); + if (rc != 0) { + LOGP(DSMPP, LOGL_ERROR, "[%s] Error during smpp34_pack(): %s\n", + esme->system_id, smpp34_strerror); + msgb_free(msg); + return -EINVAL; + } + msgb_put(msg, rlen); + + return osmo_wqueue_enqueue(&esme->wqueue, msg); +} +/* FIXME: merge with smpp_smsc.c */ + + +static int smpp_handle_deliver(struct esme *esme, struct msgb *msg) +{ + struct deliver_sm_t deliver; + struct deliver_sm_resp_t deliver_r; + struct submit_sm_t submit; + int rc; + + memset(&deliver, 0, sizeof(deliver)); + SMPP34_UNPACK(rc, DELIVER_SM, &deliver, msgb_data(msg), msgb_length(msg)); + if (rc < 0) + return rc; + + INIT_RESP(DELIVER_SM_RESP, &deliver_r, &deliver); + + PACK_AND_SEND(esme, &deliver_r); + + memset(&submit, 0, sizeof(submit)); + submit.command_id = SUBMIT_SM; + submit.command_status = ESME_ROK; + submit.sequence_number = esme_inc_seq_nr(esme); + + submit.dest_addr_ton = deliver.source_addr_ton; + submit.dest_addr_npi = deliver.source_addr_npi; + memcpy(submit.destination_addr, deliver.source_addr, + OSMO_MIN(sizeof(submit.destination_addr), + sizeof(deliver.source_addr))); + + submit.source_addr_ton = deliver.dest_addr_ton; + submit.source_addr_npi = deliver.dest_addr_npi; + memcpy(submit.source_addr, deliver.destination_addr, + OSMO_MIN(sizeof(submit.source_addr), + sizeof(deliver.destination_addr))); + + submit.esm_class = deliver.esm_class; + submit.protocol_id = deliver.protocol_id; + submit.priority_flag = deliver.priority_flag; + memcpy(submit.schedule_delivery_time, deliver.schedule_delivery_time, + OSMO_MIN(sizeof(submit.schedule_delivery_time), + sizeof(deliver.schedule_delivery_time))); + memcpy(submit.validity_period, deliver.validity_period, + OSMO_MIN(sizeof(submit.validity_period), + sizeof(deliver.validity_period))); + submit.registered_delivery = deliver.registered_delivery; + submit.replace_if_present_flag = deliver.replace_if_present_flag; + submit.data_coding = deliver.data_coding; + submit.sm_default_msg_id = deliver.sm_default_msg_id; + submit.sm_length = deliver.sm_length; + memcpy(submit.short_message, deliver.short_message, + OSMO_MIN(sizeof(submit.short_message), + sizeof(deliver.short_message))); + /* FIXME: TLV? */ + + return PACK_AND_SEND(esme, &submit); +} + +static int bind_transceiver(struct esme *esme) +{ + struct bind_transceiver_t bind; + + memset(&bind, 0, sizeof(bind)); + bind.command_id = BIND_TRANSCEIVER; + bind.sequence_number = esme_inc_seq_nr(esme); + snprintf((char *)bind.system_id, sizeof(bind.system_id), "%s", esme->system_id); + snprintf((char *)bind.password, sizeof(bind.password), "%s", esme->password); + snprintf((char *)bind.system_type, sizeof(bind.system_type), "mirror"); + bind.interface_version = esme->smpp_version; + + return PACK_AND_SEND(esme, &bind); +} + +static int smpp_pdu_rx(struct esme *esme, struct msgb *msg) +{ + uint32_t cmd_id = smpp_msgb_cmdid(msg); + int rc; + + switch (cmd_id) { + case DELIVER_SM: + rc = smpp_handle_deliver(esme, msg); + break; + default: + break; + } + + return rc; +} + +/* FIXME: merge with smpp_smsc.c */ +static int esme_read_cb(struct osmo_fd *ofd) +{ + struct esme *esme = ofd->data; + uint32_t len; + uint8_t *lenptr = (uint8_t *) &len; + uint8_t *cur; + struct msgb *msg; + int rdlen; + int rc; + + switch (esme->read_state) { + case READ_ST_IN_LEN: + rdlen = sizeof(uint32_t) - esme->read_idx; + rc = read(ofd->fd, lenptr + esme->read_idx, rdlen); + if (rc < 0) { + LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %d\n", + esme->system_id, rc); + } else if (rc == 0) { + goto dead_socket; + } else + esme->read_idx += rc; + if (esme->read_idx >= sizeof(uint32_t)) { + esme->read_len = ntohl(len); + msg = msgb_alloc(esme->read_len, "SMPP Rx"); + if (!msg) + return -ENOMEM; + esme->read_msg = msg; + cur = msgb_put(msg, sizeof(uint32_t)); + memcpy(cur, lenptr, sizeof(uint32_t)); + esme->read_state = READ_ST_IN_MSG; + esme->read_idx = sizeof(uint32_t); + } + break; + case READ_ST_IN_MSG: + msg = esme->read_msg; + rdlen = esme->read_len - esme->read_idx; + rc = read(ofd->fd, msg->tail, OSMO_MIN(rdlen, msgb_tailroom(msg))); + if (rc < 0) { + LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %d\n", + esme->system_id, rc); + } else if (rc == 0) { + goto dead_socket; + } else { + esme->read_idx += rc; + msgb_put(msg, rc); + } + + if (esme->read_idx >= esme->read_len) { + rc = smpp_pdu_rx(esme, esme->read_msg); + esme->read_msg = NULL; + esme->read_idx = 0; + esme->read_len = 0; + esme->read_state = READ_ST_IN_LEN; + } + break; + } + + return 0; +dead_socket: + msgb_free(esme->read_msg); + osmo_fd_unregister(&esme->wqueue.bfd); + close(esme->wqueue.bfd.fd); + esme->wqueue.bfd.fd = -1; + exit(2342); + + return 0; +} + +static void esme_write_cb(struct osmo_fd *ofd, struct msgb *msg) +{ + struct esme *esme = ofd->data; + int rc; + + rc = write(ofd->fd, msgb_data(msg), msgb_length(msg)); + if (rc == 0) { + osmo_fd_unregister(&esme->wqueue.bfd); + close(esme->wqueue.bfd.fd); + esme->wqueue.bfd.fd = -1; + exit(99); + } else if (rc < msgb_length(msg)) { + LOGP(DSMPP, LOGL_ERROR, "[%s] Short write\n", esme->system_id); + return; + } +} + +static int smpp_esme_init(struct esme *esme, const char *host, uint16_t port) +{ + int rc; + + if (port == 0) + port = 2775; + + esme->own_seq_nr = rand(); + esme_inc_seq_nr(esme); + osmo_wqueue_init(&esme->wqueue, 10); + esme->wqueue.bfd.data = esme; + esme->wqueue.read_cb = esme_read_cb; + esme->wqueue.write_cb = esme_write_cb; + + rc = osmo_sock_init_ofd(&esme->wqueue.bfd, AF_UNSPEC, SOCK_STREAM, + IPPROTO_TCP, host, port, OSMO_SOCK_F_CONNECT); + if (rc < 0) + return rc; + + return bind_transceiver(esme); +} + + +int main(int argc, char **argv) +{ + struct esme esme; + char *host = "localhost"; + int port = 0; + int rc; + + memset(&esme, 0, sizeof(esme)); + + osmo_init_logging(&log_info); + + snprintf((char *) esme.system_id, sizeof(esme.system_id), "mirror"); + snprintf((char *) esme.password, sizeof(esme.password), "mirror"); + esme.smpp_version = 0x34; + + if (argc >= 2) + host = argv[1]; + if (argc >= 3) + port = atoi(argv[2]); + + rc = smpp_esme_init(&esme, host, port); + if (rc < 0) + exit(1); + + while (1) { + osmo_select_main(0); + } + + exit(0); +} -- cgit v1.2.3