aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <holger@moiji-mobile.com>2014-03-06 23:48:49 +0100
committerHolger Hans Peter Freyther <holger@moiji-mobile.com>2014-03-06 23:48:49 +0100
commite0bd8efcc06ff83bee75835c4f2652b10846c624 (patch)
tree13261c2cfb6a293830215af5774b1c12ad5cfcf9
parentec6e4f8b3d1f304ee1c6ca73ee47290642ac380a (diff)
parented0d4f62225f534f4b78fa6ace14cd81887f4174 (diff)
Merge branch 'daniel/smpp-fixes'
-rw-r--r--openbsc/src/libmsc/smpp_smsc.c57
-rw-r--r--openbsc/tests/Makefile.am5
-rw-r--r--openbsc/tests/smpp_test_runner.py136
3 files changed, 179 insertions, 19 deletions
diff --git a/openbsc/src/libmsc/smpp_smsc.c b/openbsc/src/libmsc/smpp_smsc.c
index 64ed2005f..048c1b802 100644
--- a/openbsc/src/libmsc/smpp_smsc.c
+++ b/openbsc/src/libmsc/smpp_smsc.c
@@ -23,6 +23,7 @@
#include <string.h>
#include <stdint.h>
#include <errno.h>
+#include <limits.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -762,6 +763,23 @@ static int smpp_pdu_rx(struct osmo_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)
{
@@ -770,22 +788,27 @@ static int esme_link_read_cb(struct osmo_fd *ofd)
uint8_t *lenptr = (uint8_t *) &len;
uint8_t *cur;
struct msgb *msg;
- int rdlen;
- int rc;
+ ssize_t rdlen, 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 (rc < 0)
+ LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %zd (%s)\n",
+ esme->system_id, 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) {
+ LOGP(DSMPP, LOGL_ERROR, "[%s] length invalid %u\n",
+ esme->system_id, esme->read_len);
+ goto dead_socket;
+ }
+
msg = msgb_alloc(esme->read_len, "SMPP Rx");
if (!msg)
return -ENOMEM;
@@ -800,15 +823,13 @@ static int esme_link_read_cb(struct osmo_fd *ofd)
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 (rc < 0)
+ LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %zd (%s)\n",
+ esme->system_id, 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(esme, esme->read_msg);
diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am
index 31b2bb4c7..a29d9fb52 100644
--- a/openbsc/tests/Makefile.am
+++ b/openbsc/tests/Makefile.am
@@ -31,7 +31,7 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac
echo ' [$(PACKAGE_URL)])'; \
} >'$(srcdir)/package.m4'
-EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) vty_test_runner.py ctrl_test_runner.py
+EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) vty_test_runner.py ctrl_test_runner.py smpp_test_runner.py
TESTSUITE = $(srcdir)/testsuite
DISTCLEANFILES = atconfig
@@ -41,6 +41,9 @@ python-tests: $(BUILT_SOURCES)
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
$(PYTHON) $(srcdir)/vty_test_runner.py -w $(abs_top_builddir) -v
$(PYTHON) $(srcdir)/ctrl_test_runner.py -w $(abs_top_builddir) -v
+if BUILD_SMPP
+ $(PYTHON) $(srcdir)/smpp_test_runner.py -w $(abs_top_builddir) -v
+endif
rm -f $(top_builddir)/hlr.sqlite3
else
python-tests: $(BUILT_SOURCES)
diff --git a/openbsc/tests/smpp_test_runner.py b/openbsc/tests/smpp_test_runner.py
new file mode 100644
index 000000000..9dc0d1340
--- /dev/null
+++ b/openbsc/tests/smpp_test_runner.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python
+
+# (C) 2014 by Holger Hans Peter Freyther
+# based on vty_test_runner.py:
+# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
+# (C) 2013 by Holger Hans Peter Freyther
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import time
+import unittest
+import socket
+
+import osmopy.obscvty as obscvty
+import osmopy.osmoutil as osmoutil
+
+confpath = '.'
+
+class TestVTYBase(unittest.TestCase):
+
+ def vty_command(self):
+ raise Exception("Needs to be implemented by a subclass")
+
+ def vty_app(self):
+ raise Exception("Needs to be implemented by a subclass")
+
+ def setUp(self):
+ osmo_vty_cmd = self.vty_command()[:]
+ config_index = osmo_vty_cmd.index('-c')
+ if config_index:
+ cfi = config_index + 1
+ osmo_vty_cmd[cfi] = os.path.join(confpath, osmo_vty_cmd[cfi])
+
+ try:
+ print "Launch: %s from %s" % (' '.join(osmo_vty_cmd), os.getcwd())
+ self.proc = osmoutil.popen_devnull(osmo_vty_cmd)
+ except OSError:
+ print >> sys.stderr, "Current directory: %s" % os.getcwd()
+ print >> sys.stderr, "Consider setting -b"
+ time.sleep(1)
+
+ appstring = self.vty_app()[2]
+ appport = self.vty_app()[0]
+ self.vty = obscvty.VTYInteract(appstring, "127.0.0.1", appport)
+
+ def tearDown(self):
+ self.vty = None
+ osmoutil.end_proc(self.proc)
+
+
+class TestSMPPNITB(TestVTYBase):
+
+ def vty_command(self):
+ return ["./src/osmo-nitb/osmo-nitb", "-c",
+ "doc/examples/osmo-nitb/nanobts/openbsc.cfg"]
+
+ def vty_app(self):
+ return (4242, "./src/osmo-nitb/osmo-nitb", "OpenBSC", "nitb")
+
+ def testSMPPCrashes(self):
+ # Enable the configuration
+ self.vty.enable()
+ self.assertTrue(self.vty.verify("configure terminal", ['']))
+ self.assertEquals(self.vty.node(), 'config')
+
+ self.assertTrue(self.vty.verify('smpp', ['']))
+ self.assertEquals(self.vty.node(), 'config-smpp')
+ self.assertTrue(self.vty.verify('system-id test', ['']))
+ self.assertTrue(self.vty.verify('local-tcp-port 2775', ['']))
+ self.assertTrue(self.vty.verify('esme test', ['']))
+ self.assertEquals(self.vty.node(), 'config-smpp-esme')
+ self.assertTrue(self.vty.verify('default-route', ['']))
+ self.assertTrue(self.vty.verify('end', ['']))
+
+ # NITB should listen to 2775 now!
+ sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sck.setblocking(1)
+ sck.connect(('0.0.0.0', 2775))
+ sck.sendall('\x00\x00\x00\x02\x00')
+ sck.close()
+
+ # Check if the VTY is still there
+ self.vty.verify('disable',[''])
+
+ # Now for the second packet
+ sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sck.setblocking(1)
+ sck.connect(('0.0.0.0', 2775))
+ sck.sendall('\x00\x01\x00\x01\x01')
+ sck.close()
+
+ self.vty.verify('enable',[''])
+
+if __name__ == '__main__':
+ import argparse
+ import sys
+
+ workdir = '.'
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-v", "--verbose", dest="verbose",
+ action="store_true", help="verbose mode")
+ parser.add_argument("-p", "--pythonconfpath", dest="p",
+ help="searchpath for config")
+ parser.add_argument("-w", "--workdir", dest="w",
+ help="Working directory")
+ args = parser.parse_args()
+
+ verbose_level = 1
+ if args.verbose:
+ verbose_level = 2
+
+ if args.w:
+ workdir = args.w
+
+ if args.p:
+ confpath = args.p
+
+ print "confpath %s, workdir %s" % (confpath, workdir)
+ os.chdir(workdir)
+ print "Running tests for specific SMPP"
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestSMPPNITB))
+ res = unittest.TextTestRunner(verbosity=verbose_level).run(suite)
+ sys.exit(len(res.errors) + len(res.failures))