SMPP: move read/write callbacks to libsmpputil

The code is shared between OsmoMSC and smpp_mirror tool.

Related: OS#5568
Change-Id: I875eb5249004d3a960aee46c5099592d18fcaa76
This commit is contained in:
Max 2022-09-15 10:16:21 +07:00
parent dfb1cb80ca
commit 43a6a9a208
4 changed files with 152 additions and 170 deletions

View File

@ -1,5 +1,6 @@
#pragma once
#include <inttypes.h>
#include <osmocom/msc/gsm_data.h>
/* 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);

View File

@ -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;
}

View File

@ -20,11 +20,131 @@
#include "config.h"
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/logging.h>
#include <osmocom/netif/stream.h>
#include <osmocom/msc/debug.h>
#include <osmocom/smpp/smpp.h>
#include <osmocom/smpp/smpp_smsc.h>
#include <smpp34.h>
#include <smpp34_structs.h>
#include <smpp34_params.h>
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)
{

View File

@ -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;
}