diff options
author | Holger Hans Peter Freyther <holger@moiji-mobile.com> | 2014-03-06 23:48:49 +0100 |
---|---|---|
committer | Holger Hans Peter Freyther <holger@moiji-mobile.com> | 2014-03-06 23:48:49 +0100 |
commit | e0bd8efcc06ff83bee75835c4f2652b10846c624 (patch) | |
tree | 13261c2cfb6a293830215af5774b1c12ad5cfcf9 | |
parent | ec6e4f8b3d1f304ee1c6ca73ee47290642ac380a (diff) | |
parent | ed0d4f62225f534f4b78fa6ace14cd81887f4174 (diff) |
Merge branch 'daniel/smpp-fixes'
-rw-r--r-- | openbsc/src/libmsc/smpp_smsc.c | 57 | ||||
-rw-r--r-- | openbsc/tests/Makefile.am | 5 | ||||
-rw-r--r-- | openbsc/tests/smpp_test_runner.py | 136 |
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)) |