From 43a6a9a20886e9d8d365a62f35977befc8918067 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 15 Sep 2022 10:16:21 +0700 Subject: [PATCH] SMPP: move read/write callbacks to libsmpputil The code is shared between OsmoMSC and smpp_mirror tool. Related: OS#5568 Change-Id: I875eb5249004d3a960aee46c5099592d18fcaa76 --- include/osmocom/smpp/smpp.h | 7 +- src/libsmpputil/smpp_smsc.c | 107 +++++-------------------------- src/libsmpputil/smpp_utils.c | 120 +++++++++++++++++++++++++++++++++++ src/utils/smpp_mirror.c | 88 +++---------------------- 4 files changed, 152 insertions(+), 170 deletions(-) diff --git a/include/osmocom/smpp/smpp.h b/include/osmocom/smpp/smpp.h index a2832d304..94232928c 100644 --- a/include/osmocom/smpp/smpp.h +++ b/include/osmocom/smpp/smpp.h @@ -1,5 +1,6 @@ #pragma once +#include #include /* Length limits according to SMPP 3.4 spec including NUL-byte: */ @@ -39,7 +40,7 @@ struct esme { #define PACK_AND_SEND(esme, ptr) pack_and_send(esme, (ptr)->command_id, ptr) -/*! \brief initialize the libsmpp34 data structure for a response */ +/* initialize the libsmpp34 data structure for a response */ #define INIT_RESP(type, resp, req) { \ memset((resp), 0, sizeof(*(resp))); \ (resp)->command_length = 0; \ @@ -48,6 +49,10 @@ struct esme { (resp)->sequence_number = (req)->sequence_number; } struct esme *esme_alloc(void *ctx); +void esme_read_state_reset(struct esme *esme); +void esme_queue_reset(struct esme *esme); +int esme_write_callback(struct esme *esme, int fd, struct msgb *msg); +int esme_read_callback(struct esme *esme, int fd); uint32_t smpp_msgb_cmdid(struct msgb *msg); uint32_t esme_inc_seq_nr(struct esme *esme); int pack_and_send(struct esme *esme, uint32_t type, void *ptr); diff --git a/src/libsmpputil/smpp_smsc.c b/src/libsmpputil/smpp_smsc.c index a1f2b2f44..7d765fc9d 100644 --- a/src/libsmpputil/smpp_smsc.c +++ b/src/libsmpputil/smpp_smsc.c @@ -160,11 +160,9 @@ void smpp_acl_delete(struct osmo_smpp_acl *acl) /* kill any active ESMEs */ if (acl->esme) { struct esme *esme = acl->esme->esme; - osmo_fd_unregister(&esme->wqueue.bfd); - close(esme->wqueue.bfd.fd); - esme->wqueue.bfd.fd = -1; - acl->esme = NULL; + esme_queue_reset(esme); smpp_esme_put(acl->esme); + acl->esme = NULL; } /* delete all routes for this ACL */ @@ -240,8 +238,7 @@ static void esme_destroy(struct smpp_esme *esme) { osmo_wqueue_clear(&esme->esme->wqueue); if (esme->esme->wqueue.bfd.fd >= 0) { - osmo_fd_unregister(&esme->esme->wqueue.bfd); - close(esme->esme->wqueue.bfd.fd); + esme_queue_reset(esme->esme); } smpp_cmd_flush_pending(esme); llist_del(&esme->list); @@ -761,113 +758,41 @@ static int smpp_pdu_rx(struct smpp_esme *esme, struct msgb *msg __uses) return rc; } -/* This macro should be called after a call to read() in the read_cb of an - * osmo_fd to properly check for errors. - * rc is the return value of read, err_label is the label to jump to in case of - * an error. The code there should handle closing the connection. - * FIXME: This code should go in libosmocore utils.h so it can be used by other - * projects as well. - * */ -#define OSMO_FD_CHECK_READ(rc, err_label) \ - if (rc < 0) { \ - /* EINTR is a non-fatal error, just try again */ \ - if (errno == EINTR) \ - return 0; \ - goto err_label; \ - } else if (rc == 0) { \ - goto err_label; \ - } - /* !\brief call-back when per-ESME TCP socket has some data to be read */ static int esme_link_read_cb(struct osmo_fd *ofd) { struct smpp_esme *e = ofd->data; struct esme *esme = e->esme; - uint32_t len; - uint8_t *lenptr = (uint8_t *) &len; - uint8_t *cur; - struct msgb *msg; - ssize_t rdlen, rc; + int rc = esme_read_callback(esme, ofd->fd); - 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) - LOGPESME(esme, LOGL_ERROR, "read returned %zd (%s)\n", rc, strerror(errno)); - OSMO_FD_CHECK_READ(rc, dead_socket); - - esme->read_idx += rc; - - if (esme->read_idx >= sizeof(uint32_t)) { - esme->read_len = ntohl(len); - if (esme->read_len < 8 || esme->read_len > UINT16_MAX) { - LOGPESME(esme, LOGL_ERROR, "length invalid %u\n", esme->read_len); - goto dead_socket; - } - - 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); - } + switch (rc) { + case 1: + rc = smpp_pdu_rx(e, esme->read_msg); + esme_read_state_reset(esme); 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) - LOGPESME(esme, LOGL_ERROR, "read returned %zd (%s)\n", - rc, strerror(errno)); - OSMO_FD_CHECK_READ(rc, dead_socket); - - esme->read_idx += rc; - msgb_put(msg, rc); - - if (esme->read_idx >= esme->read_len) { - rc = smpp_pdu_rx(e, esme->read_msg); - msgb_free(esme->read_msg); - esme->read_msg = NULL; - esme->read_idx = 0; - esme->read_len = 0; - esme->read_state = READ_ST_IN_LEN; - } + case -EBADF: + if (e->acl) + e->acl->esme = NULL; + smpp_esme_put(e); break; + default: + return rc; } 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; - if (e->acl) - e->acl->esme = NULL; - smpp_esme_put(e); - - return -EBADF; } /* call-back of write queue once it wishes to write a message to the socket */ static int esme_link_write_cb(struct osmo_fd *ofd, struct msgb *msg) { struct smpp_esme *esme = ofd->data; - int rc; + int rc = esme_write_callback(esme->esme, ofd->fd, msg); - rc = write(ofd->fd, msgb_data(msg), msgb_length(msg)); if (rc == 0) { - osmo_fd_unregister(&esme->esme->wqueue.bfd); - close(esme->esme->wqueue.bfd.fd); - esme->esme->wqueue.bfd.fd = -1; if (esme->acl) esme->acl->esme = NULL; smpp_esme_put(esme); - } else if (rc < msgb_length(msg)) { - LOGPESME(esme->esme, LOGL_ERROR, "Short write\n"); + } else if (rc == -1) { return -1; } diff --git a/src/libsmpputil/smpp_utils.c b/src/libsmpputil/smpp_utils.c index bada97217..4180f06f4 100644 --- a/src/libsmpputil/smpp_utils.c +++ b/src/libsmpputil/smpp_utils.c @@ -20,11 +20,131 @@ #include "config.h" #include +#include +#include +#include +#include +#include +#include #include #include +#include +#include #include +#include +#include +#include + +void esme_read_state_reset(struct esme *esme) +{ + if (esme->read_msg) { + msgb_free(esme->read_msg); + esme->read_msg = NULL; + } + esme->read_idx = 0; + esme->read_len = 0; + esme->read_state = READ_ST_IN_LEN; +} + +void esme_queue_reset(struct esme *esme) +{ + osmo_fd_unregister(&esme->wqueue.bfd); + close(esme->wqueue.bfd.fd); + esme->wqueue.bfd.fd = -1; +} + +/* This macro should be called after a call to read() in the read_cb of an + * osmo_fd to properly check for errors. + * rc is the return value of read, err_label is the label to jump to in case of + * an error. The code there should handle closing the connection. + * FIXME: This code should go in libosmocore utils.h so it can be used by other + * projects as well. + */ +#define OSMO_FD_CHECK_READ(rc, err_label) do { \ + if (rc < 0) { \ + /* EINTR is a non-fatal error, just try again */ \ + if (errno == EINTR) \ + return 0; \ + goto err_label; \ + } else if (rc == 0) { \ + goto err_label; \ + } } while (0) + +/* call-back when per-ESME TCP socket has some data to be read */ +int esme_read_callback(struct esme *esme, int fd) +{ + uint32_t len; + uint8_t *lenptr = (uint8_t *) &len; + uint8_t *cur; + struct msgb *msg; + ssize_t rdlen, rc; + + switch (esme->read_state) { + case READ_ST_IN_LEN: + rdlen = sizeof(uint32_t) - esme->read_idx; + rc = read(fd, lenptr + esme->read_idx, rdlen); + if (rc < 0) + LOGPESME(esme, LOGL_ERROR, "read returned %zd (%s)\n", rc, strerror(errno)); + OSMO_FD_CHECK_READ(rc, dead_socket); + + esme->read_idx += rc; + + if (esme->read_idx >= sizeof(uint32_t)) { + esme->read_len = ntohl(len); + if (esme->read_len < 8 || esme->read_len > UINT16_MAX) { + LOGPESME(esme, LOGL_ERROR, "length invalid %u\n", esme->read_len); + goto dead_socket; + } + + 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(fd, msg->tail, OSMO_MIN(rdlen, msgb_tailroom(msg))); + if (rc < 0) + LOGPESME(esme, LOGL_ERROR, "read returned %zd (%s)\n", rc, strerror(errno)); + OSMO_FD_CHECK_READ(rc, dead_socket); + + esme->read_idx += rc; + msgb_put(msg, rc); + + if (esme->read_idx >= esme->read_len) + return 1; + break; + } + + return 0; +dead_socket: + esme_queue_reset(esme); + esme_read_state_reset(esme); + return -EBADF; +} + +int esme_write_callback(struct esme *esme, int fd, struct msgb *msg) +{ + int rc = write(fd, msgb_data(msg), msgb_length(msg)); + if (rc == 0) { + esme_queue_reset(esme); + return 0; + } else if (rc < msgb_length(msg)) { + LOGPESME(esme, LOGL_ERROR, "Short write\n"); + return -1; + } + + return rc; +} + /*! \brief retrieve SMPP command ID from a msgb */ uint32_t smpp_msgb_cmdid(struct msgb *msg) { diff --git a/src/utils/smpp_mirror.c b/src/utils/smpp_mirror.c index 48b9ea292..f0f767e8e 100644 --- a/src/utils/smpp_mirror.c +++ b/src/utils/smpp_mirror.c @@ -143,98 +143,30 @@ static int smpp_pdu_rx(struct esme *esme, struct msgb *msg) return rc; } -static void esme_read_state_reset(struct esme *esme) -{ - esme->read_msg = NULL; - esme->read_idx = 0; - esme->read_len = 0; - esme->read_state = READ_ST_IN_LEN; -} - -/* 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; + int rc = esme_read_callback(esme, ofd->fd); - 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) { - LOGPESME(esme, LOGL_ERROR, "read returned %d\n", 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); - if (esme->read_len > 65535) { - /* unrealistic */ - goto dead_socket; - } - 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); - } + switch (rc) { + case 1: + rc = smpp_pdu_rx(esme, esme->read_msg); + esme_read_state_reset(esme); 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) { - LOGPESME(esme, LOGL_ERROR, "read returned %d\n", 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_state_reset(esme); - } + case -EBADF: + exit(2342); break; + default: + return rc; } - 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; - esme_read_state_reset(esme); - exit(2342); - return 0; } static int 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; + if (esme_write_callback(ofd->data, ofd->fd, msg) == 0) exit(99); - } else if (rc < msgb_length(msg)) { - LOGPESME(esme, LOGL_ERROR, "Short write\n"); - return 0; - } return 0; }