<p>laforge <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-hlr/+/16202">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  laforge: Looks good to me, approved
  fixeria: Looks good to me, but someone else must approve
  pespin: Looks good to me, but someone else must approve
  Jenkins Builder: Verified

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">add libosmo-mslookup abstract client<br><br>mslookup is a key concept in Distributed GSM, which allows querying the current<br>location of a subscriber in a number of cooperating but independent core<br>network sites, by arbitrary service names and by MSISDN/IMSI.<br><br>Add the abstract mslookup client library. An actual lookup method (besides<br>mslookup_client_fake.c) is added in a subsequent patch.<br><br>For a detailed overview of this and upcoming patches, please see the elaborate<br>comment at the top of mslookup.c.<br><br>Add as separate library, libosmo-mslookup, to allow adding D-GSM capability to<br>arbitrary client programs.<br><br>osmo-hlr will be the only mslookup server implementation, added in a subsequent<br>patch.<br><br>osmo-hlr itself will also use this library and act as an mslookup client, when<br>requesting the home HLR for locally unknown IMSIs.<br><br>Related: OS#4237<br>Patch-by: osmith, nhofmeyr<br>Change-Id: I83487ab8aad1611eb02e997dafbcb8344da13df1<br>---<br>M .gitignore<br>M configure.ac<br>M debian/control<br>A debian/libosmo-mslookup-dev.install<br>A debian/libosmo-mslookup0.install<br>M include/Makefile.am<br>M include/osmocom/hlr/logging.h<br>A include/osmocom/mslookup/mslookup.h<br>A include/osmocom/mslookup/mslookup_client.h<br>A include/osmocom/mslookup/mslookup_client_fake.h<br>A libosmo-mslookup.pc.in<br>M src/Makefile.am<br>M src/logging.c<br>A src/mslookup/Makefile.am<br>A src/mslookup/mslookup.c<br>A src/mslookup/mslookup_client.c<br>A src/mslookup/mslookup_client_fake.c<br>M tests/Makefile.am<br>A tests/mslookup/Makefile.am<br>A tests/mslookup/mslookup_client_test.c<br>A tests/mslookup/mslookup_client_test.err<br>A tests/mslookup/mslookup_test.c<br>A tests/mslookup/mslookup_test.err<br>M tests/testsuite.at<br>24 files changed, 1,622 insertions(+), 3 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/.gitignore b/.gitignore</span><br><span>index 8d4b450..acfea84 100644</span><br><span>--- a/.gitignore</span><br><span>+++ b/.gitignore</span><br><span>@@ -51,6 +51,8 @@</span><br><span> tests/db/db_test</span><br><span> tests/hlr_vty_test.db*</span><br><span> tests/db_upgrade/*.dump</span><br><span style="color: hsl(120, 100%, 40%);">+tests/mslookup/mslookup_client_test</span><br><span style="color: hsl(120, 100%, 40%);">+tests/mslookup/mslookup_test</span><br><span> </span><br><span> # manuals</span><br><span> doc/manuals/*.html</span><br><span>diff --git a/configure.ac b/configure.ac</span><br><span>index 334a7e8..217df9f 100644</span><br><span>--- a/configure.ac</span><br><span>+++ b/configure.ac</span><br><span>@@ -174,10 +174,12 @@</span><br><span>  doc/examples/Makefile</span><br><span>        src/Makefile</span><br><span>         src/gsupclient/Makefile</span><br><span style="color: hsl(120, 100%, 40%);">+       src/mslookup/Makefile</span><br><span>        include/Makefile</span><br><span>     include/osmocom/Makefile</span><br><span>     include/osmocom/hlr/Makefile</span><br><span>         libosmo-gsup-client.pc</span><br><span style="color: hsl(120, 100%, 40%);">+        libosmo-mslookup.pc</span><br><span>  sql/Makefile</span><br><span>         doc/manuals/Makefile</span><br><span>         contrib/Makefile</span><br><span>@@ -188,4 +190,5 @@</span><br><span>       tests/gsup_server/Makefile</span><br><span>   tests/db/Makefile</span><br><span>    tests/db_upgrade/Makefile</span><br><span style="color: hsl(120, 100%, 40%);">+     tests/mslookup/Makefile</span><br><span>      )</span><br><span>diff --git a/debian/control b/debian/control</span><br><span>index a32c68d..c1eb464 100644</span><br><span>--- a/debian/control</span><br><span>+++ b/debian/control</span><br><span>@@ -59,6 +59,28 @@</span><br><span>   .</span><br><span>   This package contains the development headers.</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+Package: libosmo-mslookup0</span><br><span style="color: hsl(120, 100%, 40%);">+Section: libs</span><br><span style="color: hsl(120, 100%, 40%);">+Architecture: any</span><br><span style="color: hsl(120, 100%, 40%);">+Multi-Arch: same</span><br><span style="color: hsl(120, 100%, 40%);">+Depends: ${shlibs:Depends},</span><br><span style="color: hsl(120, 100%, 40%);">+         ${misc:Depends}</span><br><span style="color: hsl(120, 100%, 40%);">+Pre-Depends: ${misc:Pre-Depends}</span><br><span style="color: hsl(120, 100%, 40%);">+Description: Osmocom MS lookup library</span><br><span style="color: hsl(120, 100%, 40%);">+  This shared library contains routines for looking up mobile subscribers.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Package: libosmo-mslookup-dev</span><br><span style="color: hsl(120, 100%, 40%);">+Architecture: any</span><br><span style="color: hsl(120, 100%, 40%);">+Multi-Arch: same</span><br><span style="color: hsl(120, 100%, 40%);">+Depends: ${misc:Depends},</span><br><span style="color: hsl(120, 100%, 40%);">+       libosmo-mslookup0 (= ${binary:Version}),</span><br><span style="color: hsl(120, 100%, 40%);">+      libosmocore-dev</span><br><span style="color: hsl(120, 100%, 40%);">+Pre-Depends: ${misc:Pre-Depends}</span><br><span style="color: hsl(120, 100%, 40%);">+Description: Development headers of Osmocom MS lookup library</span><br><span style="color: hsl(120, 100%, 40%);">+  This shared library contains routines for looking up mobile subscribers.</span><br><span style="color: hsl(120, 100%, 40%);">+  .</span><br><span style="color: hsl(120, 100%, 40%);">+  This package contains the development headers.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> Package: osmo-hlr-doc</span><br><span> Architecture: all</span><br><span> Section: doc</span><br><span>diff --git a/debian/libosmo-mslookup-dev.install b/debian/libosmo-mslookup-dev.install</span><br><span>new file mode 100644</span><br><span>index 0000000..539bba8</span><br><span>--- /dev/null</span><br><span>+++ b/debian/libosmo-mslookup-dev.install</span><br><span>@@ -0,0 +1,5 @@</span><br><span style="color: hsl(120, 100%, 40%);">+usr/include/osmocom/mslookup</span><br><span style="color: hsl(120, 100%, 40%);">+usr/lib/*/libosmo-mslookup*.a</span><br><span style="color: hsl(120, 100%, 40%);">+usr/lib/*/libosmo-mslookup*.so</span><br><span style="color: hsl(120, 100%, 40%);">+usr/lib/*/libosmo-mslookup*.la</span><br><span style="color: hsl(120, 100%, 40%);">+usr/lib/*/pkgconfig/libosmo-mslookup.pc</span><br><span>diff --git a/debian/libosmo-mslookup0.install b/debian/libosmo-mslookup0.install</span><br><span>new file mode 100644</span><br><span>index 0000000..9cad0e8</span><br><span>--- /dev/null</span><br><span>+++ b/debian/libosmo-mslookup0.install</span><br><span>@@ -0,0 +1 @@</span><br><span style="color: hsl(120, 100%, 40%);">+usr/lib/*/libosmo-mslookup*.so.*</span><br><span>diff --git a/include/Makefile.am b/include/Makefile.am</span><br><span>index d8eb1ec..e9a7126 100644</span><br><span>--- a/include/Makefile.am</span><br><span>+++ b/include/Makefile.am</span><br><span>@@ -1,3 +1,8 @@</span><br><span> SUBDIRS = osmocom</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-nobase_include_HEADERS = osmocom/gsupclient/gsup_client.h</span><br><span style="color: hsl(120, 100%, 40%);">+nobase_include_HEADERS = \</span><br><span style="color: hsl(120, 100%, 40%);">+      osmocom/gsupclient/gsup_client.h \</span><br><span style="color: hsl(120, 100%, 40%);">+    osmocom/mslookup/mslookup_client_fake.h \</span><br><span style="color: hsl(120, 100%, 40%);">+     osmocom/mslookup/mslookup_client.h \</span><br><span style="color: hsl(120, 100%, 40%);">+  osmocom/mslookup/mslookup.h \</span><br><span style="color: hsl(120, 100%, 40%);">+ $(NULL)</span><br><span>diff --git a/include/osmocom/hlr/logging.h b/include/osmocom/hlr/logging.h</span><br><span>index ed24075..83f1acd 100644</span><br><span>--- a/include/osmocom/hlr/logging.h</span><br><span>+++ b/include/osmocom/hlr/logging.h</span><br><span>@@ -8,6 +8,7 @@</span><br><span>   DGSUP,</span><br><span>       DAUC,</span><br><span>        DSS,</span><br><span style="color: hsl(120, 100%, 40%);">+  DMSLOOKUP,</span><br><span> };</span><br><span> </span><br><span> extern const struct log_info hlr_log_info;</span><br><span>diff --git a/include/osmocom/mslookup/mslookup.h b/include/osmocom/mslookup/mslookup.h</span><br><span>new file mode 100644</span><br><span>index 0000000..e90af33</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/mslookup/mslookup.h</span><br><span>@@ -0,0 +1,121 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \defgroup mslookup Distributed GSM: finding subscribers</span><br><span style="color: hsl(120, 100%, 40%);">+ *  @{</span><br><span style="color: hsl(120, 100%, 40%);">+ * \file mslookup.h</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/sockaddr_str.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/protocol/gsm_23_003.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define OSMO_MSLOOKUP_SERVICE_MAXLEN 64</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+bool osmo_mslookup_service_valid(const char *service);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum osmo_mslookup_id_type {</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_MSLOOKUP_ID_NONE = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_MSLOOKUP_ID_IMSI,</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_MSLOOKUP_ID_MSISDN,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+extern const struct value_string osmo_mslookup_id_type_names[];</span><br><span style="color: hsl(120, 100%, 40%);">+static inline const char *osmo_mslookup_id_type_name(enum osmo_mslookup_id_type val)</span><br><span style="color: hsl(120, 100%, 40%);">+{ return get_value_string(osmo_mslookup_id_type_names, val); }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_mslookup_id {</span><br><span style="color: hsl(120, 100%, 40%);">+    enum osmo_mslookup_id_type type;</span><br><span style="color: hsl(120, 100%, 40%);">+      union {</span><br><span style="color: hsl(120, 100%, 40%);">+               char imsi[GSM23003_IMSI_MAX_DIGITS+1];</span><br><span style="color: hsl(120, 100%, 40%);">+                char msisdn[GSM23003_MSISDN_MAX_DIGITS+1];</span><br><span style="color: hsl(120, 100%, 40%);">+    };</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_mslookup_id_cmp(const struct osmo_mslookup_id *a, const struct osmo_mslookup_id *b);</span><br><span style="color: hsl(120, 100%, 40%);">+bool osmo_mslookup_id_valid(const struct osmo_mslookup_id *id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum osmo_mslookup_result_code {</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_MSLOOKUP_RC_NONE = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+    /*! An intermediate valid result. The request is still open for more results. */</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_MSLOOKUP_RC_RESULT,</span><br><span style="color: hsl(120, 100%, 40%);">+      /*! Returned when the final request timeout has elapsed without results. */</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_MSLOOKUP_RC_NOT_FOUND,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+extern const struct value_string osmo_mslookup_result_code_names[];</span><br><span style="color: hsl(120, 100%, 40%);">+static inline const char *osmo_mslookup_result_code_name(enum osmo_mslookup_result_code val)</span><br><span style="color: hsl(120, 100%, 40%);">+{ return get_value_string(osmo_mslookup_result_code_names, val); }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Information to request from a lookup. */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_mslookup_query {</span><br><span style="color: hsl(120, 100%, 40%);">+        /*! Which service to request, by freely invented names. For service name conventions (for voice, SMS, HLR,...),</span><br><span style="color: hsl(120, 100%, 40%);">+        * refer to the OsmoHLR user's manual http://ftp.osmocom.org/docs/latest/osmohlr-usermanual.pdf */</span><br><span style="color: hsl(120, 100%, 40%);">+        char service[OSMO_MSLOOKUP_SERVICE_MAXLEN + 1];</span><br><span style="color: hsl(120, 100%, 40%);">+       /*! IMSI or MSISDN to look up. */</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_mslookup_id id;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Caller provided private data, if desired. */</span><br><span style="color: hsl(120, 100%, 40%);">+      void *priv;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Result data as passed back to a lookup client that invoked an osmo_mslookup_client_request. */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_mslookup_result {</span><br><span style="color: hsl(120, 100%, 40%);">+  /*! Outcome of the request. */</span><br><span style="color: hsl(120, 100%, 40%);">+        enum osmo_mslookup_result_code rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /*! IP address and port to reach the given service via IPv4, if any. */</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_sockaddr_str host_v4;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*! IP address and port to reach the given service via IPv6, if any. */</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_sockaddr_str host_v6;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*! How long ago the service last verified presence of the subscriber, in seconds, or zero if the presence is</span><br><span style="color: hsl(120, 100%, 40%);">+  * invariable (like the home HLR record for an IMSI).</span><br><span style="color: hsl(120, 100%, 40%);">+  * If a subscriber has recently moved to a different location, we get multiple replies and want to choose the</span><br><span style="color: hsl(120, 100%, 40%);">+  * most recent one. If this were a timestamp, firstly the time zones would need to be taken care of.</span><br><span style="color: hsl(120, 100%, 40%);">+   * Even if we choose UTC, a service provider with an inaccurate date/time would end up affecting the result.</span><br><span style="color: hsl(120, 100%, 40%);">+   * The least susceptible to configuration errors or difference in local and remote clock is a value that</span><br><span style="color: hsl(120, 100%, 40%);">+       * indicates the actual age of the record in seconds. The time that the lookup query took to be answered should</span><br><span style="color: hsl(120, 100%, 40%);">+        * be neglectable here, since we would typically wait one second (or very few seconds) for lookup replies,</span><br><span style="color: hsl(120, 100%, 40%);">+     * while typical Location Updating periods are in the range of 15 minutes. */</span><br><span style="color: hsl(120, 100%, 40%);">+ uint32_t age;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /*! Whether this is the last result returned for this request. */</span><br><span style="color: hsl(120, 100%, 40%);">+     bool last;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_mslookup_query_init_from_domain_str(struct osmo_mslookup_query *q, const char *domain);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+size_t osmo_mslookup_id_name_buf(char *buf, size_t buflen, const struct osmo_mslookup_id *id);</span><br><span style="color: hsl(120, 100%, 40%);">+char *osmo_mslookup_id_name_c(void *ctx, const struct osmo_mslookup_id *id);</span><br><span style="color: hsl(120, 100%, 40%);">+char *osmo_mslookup_id_name_b(char *buf, size_t buflen, const struct osmo_mslookup_id *id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+size_t osmo_mslookup_result_to_str_buf(char *buf, size_t buflen,</span><br><span style="color: hsl(120, 100%, 40%);">+                                const struct osmo_mslookup_query *query,</span><br><span style="color: hsl(120, 100%, 40%);">+                              const struct osmo_mslookup_result *result);</span><br><span style="color: hsl(120, 100%, 40%);">+char *osmo_mslookup_result_name_c(void *ctx,</span><br><span style="color: hsl(120, 100%, 40%);">+                                  const struct osmo_mslookup_query *query,</span><br><span style="color: hsl(120, 100%, 40%);">+                              const struct osmo_mslookup_result *result);</span><br><span style="color: hsl(120, 100%, 40%);">+char *osmo_mslookup_result_name_b(char *buf, size_t buflen,</span><br><span style="color: hsl(120, 100%, 40%);">+                              const struct osmo_mslookup_query *query,</span><br><span style="color: hsl(120, 100%, 40%);">+                              const struct osmo_mslookup_result *result);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! @} */</span><br><span>diff --git a/include/osmocom/mslookup/mslookup_client.h b/include/osmocom/mslookup/mslookup_client.h</span><br><span>new file mode 100644</span><br><span>index 0000000..cd0c21f</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/mslookup/mslookup_client.h</span><br><span>@@ -0,0 +1,132 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/linuxlist.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/timer.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/sockaddr_str.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mslookup/mslookup.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_mslookup_client;</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_mslookup_result;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+typedef void (*osmo_mslookup_cb_t)(struct osmo_mslookup_client *client,</span><br><span style="color: hsl(120, 100%, 40%);">+                             uint32_t request_handle,</span><br><span style="color: hsl(120, 100%, 40%);">+                              const struct osmo_mslookup_query *query,</span><br><span style="color: hsl(120, 100%, 40%);">+                              const struct osmo_mslookup_result *result);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! This handling information is passed along with a lookup request.</span><br><span style="color: hsl(120, 100%, 40%);">+ * It tells the osmo_mslookup_client layer how to handle responses received from various mslookup methods (at the time</span><br><span style="color: hsl(120, 100%, 40%);">+ * of writing only mDNS exists as a method, but the intention is to easily allow adding other methods in the future).</span><br><span style="color: hsl(120, 100%, 40%);">+ * This query handling info is not seen by the individual method implementations, to clarify that it is the</span><br><span style="color: hsl(120, 100%, 40%);">+ * osmo_mslookup_client layer that takes care of these details. */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_mslookup_query_handling {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Wait at least this long before returning any results.</span><br><span style="color: hsl(120, 100%, 40%);">+      *</span><br><span style="color: hsl(120, 100%, 40%);">+     * If nonzero, result_cb will be called as soon as this delay has elapsed, either with the so far youngest age</span><br><span style="color: hsl(120, 100%, 40%);">+         * result, or with a "not found yet" result. After this delay has elapsed, receiving results will continue</span><br><span style="color: hsl(120, 100%, 40%);">+   * until result_timeout_milliseconds has elapsed.</span><br><span style="color: hsl(120, 100%, 40%);">+      *</span><br><span style="color: hsl(120, 100%, 40%);">+     * If zero, responses are fed to the result_cb right from the start, every time a younger aged result than</span><br><span style="color: hsl(120, 100%, 40%);">+     * before comes in.</span><br><span style="color: hsl(120, 100%, 40%);">+    *</span><br><span style="color: hsl(120, 100%, 40%);">+     * If a result with age == 0 is received, min_wait_milliseconds is ignored, the result is returned immediately</span><br><span style="color: hsl(120, 100%, 40%);">+         * and listening for responses ends.</span><br><span style="color: hsl(120, 100%, 40%);">+   *</span><br><span style="color: hsl(120, 100%, 40%);">+     * Rationale: If a subscriber has recently moved between sites, multiple results will arrive, and the youngest</span><br><span style="color: hsl(120, 100%, 40%);">+         * age wins. It can make sense to wait a minimum time for responses before determining the winning result.</span><br><span style="color: hsl(120, 100%, 40%);">+     *</span><br><span style="color: hsl(120, 100%, 40%);">+     * However, if no result or no valid result has arrived within a short period, the subscriber may be at a site</span><br><span style="color: hsl(120, 100%, 40%);">+         * that is far away or that is currently experiencing high latency. It is thus a good safety net to still</span><br><span style="color: hsl(120, 100%, 40%);">+      * receive results for an extended period of time.</span><br><span style="color: hsl(120, 100%, 40%);">+     *</span><br><span style="color: hsl(120, 100%, 40%);">+     * For some services, it is possible to establish links to every received result, and whichever link succeeds</span><br><span style="color: hsl(120, 100%, 40%);">+  * will be used (for example for SIP calls: first to pick up the call gets connected, the others are dropped</span><br><span style="color: hsl(120, 100%, 40%);">+   * silently).</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+   uint32_t min_wait_milliseconds;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /*! Total time in milliseconds to listen for lookup responses.</span><br><span style="color: hsl(120, 100%, 40%);">+         *</span><br><span style="color: hsl(120, 100%, 40%);">+     * When this timeout elapses, osmo_mslookup_client_request_cancel() is called implicitly; Manually invoking</span><br><span style="color: hsl(120, 100%, 40%);">+    * osmo_mslookup_client_request_cancel() after result_timeout_milliseconds has elapsed is not necessary, but is</span><br><span style="color: hsl(120, 100%, 40%);">+        * still safe to do anyway.</span><br><span style="color: hsl(120, 100%, 40%);">+    *</span><br><span style="color: hsl(120, 100%, 40%);">+     * If zero, min_wait_milliseconds is also used as result_timeout_milliseconds; if that is also zero, a default</span><br><span style="color: hsl(120, 100%, 40%);">+         * timeout value is used.</span><br><span style="color: hsl(120, 100%, 40%);">+      *</span><br><span style="color: hsl(120, 100%, 40%);">+     * If result_timeout_milliseconds <= min_wait_milliseconds, then min_wait_milliseconds is used as</span><br><span style="color: hsl(120, 100%, 40%);">+   * result_timeout_milliseconds, i.e. the timeout triggers as soon as min_wait_milliseconds hits.</span><br><span style="color: hsl(120, 100%, 40%);">+       *</span><br><span style="color: hsl(120, 100%, 40%);">+     * osmo_mslookup_client_request_cancel() can be called any time to end the request.</span><br><span style="color: hsl(120, 100%, 40%);">+    */</span><br><span style="color: hsl(120, 100%, 40%);">+   uint32_t result_timeout_milliseconds;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /*! Invoked every time a result with a younger age than the previous result has arrived.</span><br><span style="color: hsl(120, 100%, 40%);">+       * To stop receiving results before result_timeout_milliseconds has elapsed, call</span><br><span style="color: hsl(120, 100%, 40%);">+      * osmo_mslookup_client_request_cancel().</span><br><span style="color: hsl(120, 100%, 40%);">+      */</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_mslookup_cb_t result_cb;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+uint32_t osmo_mslookup_client_request(struct osmo_mslookup_client *client,</span><br><span style="color: hsl(120, 100%, 40%);">+                                     const struct osmo_mslookup_query *query,</span><br><span style="color: hsl(120, 100%, 40%);">+                                      const struct osmo_mslookup_query_handling *handling);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_mslookup_client_request_cancel(struct osmo_mslookup_client *client, uint32_t request_handle);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_mslookup_client *osmo_mslookup_client_new(void *ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+bool osmo_mslookup_client_active(struct osmo_mslookup_client *client);</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_mslookup_client_free(struct osmo_mslookup_client *client);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Describe a specific mslookup client method implementation. This struct is only useful for a lookup method</span><br><span style="color: hsl(120, 100%, 40%);">+ * implementation to add itself to an osmo_mslookup_client, see for example osmo_mslookup_client_add_mdns(). */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_mslookup_client_method {</span><br><span style="color: hsl(120, 100%, 40%);">+     struct llist_head entry;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /*! Human readable name of this lookup method. */</span><br><span style="color: hsl(120, 100%, 40%);">+     const char *name;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*! Private data for the lookup method implementation. */</span><br><span style="color: hsl(120, 100%, 40%);">+     void *priv;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Backpointer to the client this method is added to. */</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_mslookup_client *client;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /*! Launch a lookup query. Called from osmo_mslookup_client_request().</span><br><span style="color: hsl(120, 100%, 40%);">+         * The implementation returns results by calling osmo_mslookup_client_rx_result(). */</span><br><span style="color: hsl(120, 100%, 40%);">+ void (*request)(struct osmo_mslookup_client_method *method,</span><br><span style="color: hsl(120, 100%, 40%);">+                   const struct osmo_mslookup_query *query,</span><br><span style="color: hsl(120, 100%, 40%);">+                      uint32_t request_handle);</span><br><span style="color: hsl(120, 100%, 40%);">+     /*! End a lookup query. Called from osmo_mslookup_client_request_cancel(). It is guaranteed to be called</span><br><span style="color: hsl(120, 100%, 40%);">+       * exactly once per above request() invocation. (The API user is required to invoke</span><br><span style="color: hsl(120, 100%, 40%);">+    * osmo_mslookup_client_request_cancel() exactly once per osmo_mslookup_client_request().) */</span><br><span style="color: hsl(120, 100%, 40%);">+ void (*request_cleanup)(struct osmo_mslookup_client_method *method,</span><br><span style="color: hsl(120, 100%, 40%);">+                           uint32_t request_handle);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*! The mslookup_client is removing this method, clean up all open requests, lists and allocations. */</span><br><span style="color: hsl(120, 100%, 40%);">+        void (*destruct)(struct osmo_mslookup_client_method *method);</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_mslookup_client_method_add(struct osmo_mslookup_client *client,</span><br><span style="color: hsl(120, 100%, 40%);">+                                     struct osmo_mslookup_client_method *method);</span><br><span style="color: hsl(120, 100%, 40%);">+bool osmo_mslookup_client_method_del(struct osmo_mslookup_client *client,</span><br><span style="color: hsl(120, 100%, 40%);">+                               struct osmo_mslookup_client_method *method);</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_mslookup_client_rx_result(struct osmo_mslookup_client *client, uint32_t request_handle,</span><br><span style="color: hsl(120, 100%, 40%);">+                              const struct osmo_mslookup_result *result);</span><br><span>diff --git a/include/osmocom/mslookup/mslookup_client_fake.h b/include/osmocom/mslookup/mslookup_client_fake.h</span><br><span>new file mode 100644</span><br><span>index 0000000..9fffc94</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/mslookup/mslookup_client_fake.h</span><br><span>@@ -0,0 +1,34 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! MS lookup fake API for testing purposes. */</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mslookup/mslookup_client.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_mslookup_fake_response {</span><br><span style="color: hsl(120, 100%, 40%);">+        struct timeval time_to_reply;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_mslookup_id for_id;</span><br><span style="color: hsl(120, 100%, 40%);">+       const char *for_service;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_mslookup_result result;</span><br><span style="color: hsl(120, 100%, 40%);">+   bool sent;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_mslookup_client_method *osmo_mslookup_client_add_fake(struct osmo_mslookup_client *client,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                struct osmo_mslookup_fake_response *responses,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                size_t responses_len);</span><br><span>diff --git a/libosmo-mslookup.pc.in b/libosmo-mslookup.pc.in</span><br><span>new file mode 100644</span><br><span>index 0000000..25a873c</span><br><span>--- /dev/null</span><br><span>+++ b/libosmo-mslookup.pc.in</span><br><span>@@ -0,0 +1,11 @@</span><br><span style="color: hsl(120, 100%, 40%);">+prefix=@prefix@</span><br><span style="color: hsl(120, 100%, 40%);">+exec_prefix=@exec_prefix@</span><br><span style="color: hsl(120, 100%, 40%);">+libdir=@libdir@</span><br><span style="color: hsl(120, 100%, 40%);">+includedir=@includedir@</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Name: Osmocom MS Lookup Library</span><br><span style="color: hsl(120, 100%, 40%);">+Description: C Utility Library</span><br><span style="color: hsl(120, 100%, 40%);">+Version: @VERSION@</span><br><span style="color: hsl(120, 100%, 40%);">+Libs: -L${libdir} @TALLOC_LIBS@ -losmogsm -losmo-mslookup -losmocore</span><br><span style="color: hsl(120, 100%, 40%);">+Cflags: -I${includedir}/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>diff --git a/src/Makefile.am b/src/Makefile.am</span><br><span>index a5b71cf..f858ff0 100644</span><br><span>--- a/src/Makefile.am</span><br><span>+++ b/src/Makefile.am</span><br><span>@@ -1,4 +1,7 @@</span><br><span style="color: hsl(0, 100%, 40%);">-SUBDIRS = gsupclient</span><br><span style="color: hsl(120, 100%, 40%);">+SUBDIRS = \</span><br><span style="color: hsl(120, 100%, 40%);">+  gsupclient \</span><br><span style="color: hsl(120, 100%, 40%);">+  mslookup \</span><br><span style="color: hsl(120, 100%, 40%);">+    $(NULL)</span><br><span> </span><br><span> AM_CFLAGS = \</span><br><span>         -Wall \</span><br><span>diff --git a/src/logging.c b/src/logging.c</span><br><span>index 3713ab3..d0b79cf 100644</span><br><span>--- a/src/logging.c</span><br><span>+++ b/src/logging.c</span><br><span>@@ -25,7 +25,12 @@</span><br><span>                .color = "\033[1;34m",</span><br><span>             .enabled = 1, .loglevel = LOGL_NOTICE,</span><br><span>       },</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+    [DMSLOOKUP] = {</span><br><span style="color: hsl(120, 100%, 40%);">+               .name = "DMSLOOKUP",</span><br><span style="color: hsl(120, 100%, 40%);">+                .description = "Mobile Subscriber Lookup",</span><br><span style="color: hsl(120, 100%, 40%);">+          .color = "\033[1;35m",</span><br><span style="color: hsl(120, 100%, 40%);">+              .enabled = 1, .loglevel = LOGL_NOTICE,</span><br><span style="color: hsl(120, 100%, 40%);">+        },</span><br><span> };</span><br><span> </span><br><span> const struct log_info hlr_log_info = {</span><br><span>diff --git a/src/mslookup/Makefile.am b/src/mslookup/Makefile.am</span><br><span>new file mode 100644</span><br><span>index 0000000..01be401</span><br><span>--- /dev/null</span><br><span>+++ b/src/mslookup/Makefile.am</span><br><span>@@ -0,0 +1,23 @@</span><br><span style="color: hsl(120, 100%, 40%);">+# This is _NOT_ the library release version, it's an API version.</span><br><span style="color: hsl(120, 100%, 40%);">+# Please read chapter "Library interface versions" of the libtool documentation</span><br><span style="color: hsl(120, 100%, 40%);">+# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html</span><br><span style="color: hsl(120, 100%, 40%);">+LIBVERSION=0:0:0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include</span><br><span style="color: hsl(120, 100%, 40%);">+AM_CFLAGS = -fPIC -Wall $(PCSC_CFLAGS) $(TALLOC_CFLAGS) $(LIBOSMOCORE_CFLAGS)</span><br><span style="color: hsl(120, 100%, 40%);">+AM_LDFLAGS = $(COVERAGE_LDFLAGS)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+lib_LTLIBRARIES = libosmo-mslookup.la</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+libosmo_mslookup_la_SOURCES = \</span><br><span style="color: hsl(120, 100%, 40%);">+   mslookup.c \</span><br><span style="color: hsl(120, 100%, 40%);">+  mslookup_client.c \</span><br><span style="color: hsl(120, 100%, 40%);">+   mslookup_client_fake.c \</span><br><span style="color: hsl(120, 100%, 40%);">+      $(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+libosmo_mslookup_la_LDFLAGS = -version-info $(LIBVERSION)</span><br><span style="color: hsl(120, 100%, 40%);">+libosmo_mslookup_la_LIBADD = \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(LIBOSMOCORE_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+ $(LIBOSMOGSM_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(TALLOC_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+      $(NULL)</span><br><span>diff --git a/src/mslookup/mslookup.c b/src/mslookup/mslookup.c</span><br><span>new file mode 100644</span><br><span>index 0000000..d399e3a</span><br><span>--- /dev/null</span><br><span>+++ b/src/mslookup/mslookup.c</span><br><span>@@ -0,0 +1,321 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <errno.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gsm23003.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mslookup/mslookup.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \addtogroup mslookup</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Distributed GSM: finding subscribers</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * There are various aspects of the D-GSM code base in osmo-hlr.git, here is an overview:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * mslookup is the main enabler of D-GSM, a concept for connecting services between independent core network stacks.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * D-GSM consists of:</span><br><span style="color: hsl(120, 100%, 40%);">+ * (1) mslookup client to find subscribers:</span><br><span style="color: hsl(120, 100%, 40%);">+ *     (a) external clients like ESME, SIP PBX, ... ask osmo-hlr to tell where to send SMS, voice calls, ...</span><br><span style="color: hsl(120, 100%, 40%);">+ *     (b) osmo-hlr's own mslookup client asks remote osmo-hlrs whether they know a given IMSI.</span><br><span style="color: hsl(120, 100%, 40%);">+ * (2) when a subscriber was found at a remote HLR, GSUP gets forwarded there:</span><br><span style="color: hsl(120, 100%, 40%);">+ *     (a) to deliver messages for the GSUP proxy, osmo-hlr manages many GSUP clients to establish links to remote HLRs.</span><br><span style="color: hsl(120, 100%, 40%);">+ *     (b) osmo-hlr has a GSUP proxy layer that caches data of IMSIs that get proxied to a remote HLR.</span><br><span style="color: hsl(120, 100%, 40%);">+ *     (c) decision making to distinguish local IMSIs from ones proxied to a remote HLR.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * (1) mslookup is a method of finding subscribers using (multicast) queries, by MSISDN or by IMSI.</span><br><span style="color: hsl(120, 100%, 40%);">+ * It is open to various lookup methods, the first one being multicast DNS.</span><br><span style="color: hsl(120, 100%, 40%);">+ * An mslookup client sends a request, and an mslookup server responds.</span><br><span style="color: hsl(120, 100%, 40%);">+ * The mslookup server is implemented by osmo-hlr. mslookup clients are arbitrary programs, like an ESME or a SIP PBX.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Hence the mslookup client is public API, while the mslookup server is implemented "privately" in osmo-hlr.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * (1a) Public mslookup client: libosmo-mslookup</span><br><span style="color: hsl(120, 100%, 40%);">+ *   src/mslookup/mslookup.c               Things useful for both client and server.</span><br><span style="color: hsl(120, 100%, 40%);">+ *   src/mslookup/mslookup_client.c        The client API, which can use various lookup methods,</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                         and consolidates results from various responders.</span><br><span style="color: hsl(120, 100%, 40%);">+ *   src/mslookup/mslookup_client_mdns.c   lookup method implementing multicast DNS, client side.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *   src/mslookup/osmo-mslookup-client.c   Utility program to ease invocation for (blocking) mslookup clients.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *   src/mslookup/mslookup_client_fake.c   lookup method generating fake results, for testing client implementations.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *   src/mslookup/mdns*.c                  implementation of DNS to be used by mslookup_client_mdns.c,</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                         and the mslookup_server.c.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *   contrib/dgsm/esme_dgsm.py                 Example implementation for an mslookup enabled SMS handler.</span><br><span style="color: hsl(120, 100%, 40%);">+ *   contrib/dgsm/freeswitch_dialplan_dgsm.py  Example implementation for an mslookup enabled FreeSWITCH dialplan.</span><br><span style="color: hsl(120, 100%, 40%);">+ *   contrib/dgsm/osmo-mslookup-pipe.py        Example for writing a python client using the osmo-mslookup-client</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                             cmdline.</span><br><span style="color: hsl(120, 100%, 40%);">+ *   contrib/dgsm/osmo-mslookup-socket.py      Example for writing a python client using the osmo-mslookup-client</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                             unix domain socket.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * (1b) "Private" mslookup server in osmo-hlr:</span><br><span style="color: hsl(120, 100%, 40%);">+ *   src/mslookup_server.c        Respond to mslookup queries, independent from the particular lookup method.</span><br><span style="color: hsl(120, 100%, 40%);">+ *   src/mslookup_server_mdns.c   mDNS specific implementation for mslookup_server.c.</span><br><span style="color: hsl(120, 100%, 40%);">+ *   src/dgsm_vty.c               Configure services that mslookup server sends to remote requests.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * (2) Proxy and GSUP clients to remote HLR instances:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * (a) Be a GSUP client to forward to a remote HLR:</span><br><span style="color: hsl(120, 100%, 40%);">+ *  src/gsupclient/   The same API that is used by osmo-{msc,sgsn} is also used to forward GSUP to remote osmo-hlrs.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  src/remote_hlr.c  Establish links to remote osmo-hlrs, where this osmo-hlr is a client (proxying e.g. for an MSC).</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * (b) Keep track of remotely handled IMSIs:</span><br><span style="color: hsl(120, 100%, 40%);">+ *  src/proxy.c       Keep track of proxied IMSIs and cache important subscriber data.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * (c) Direct GSUP request to the right destination: either the local or a remote HLR:</span><br><span style="color: hsl(120, 100%, 40%);">+ *  src/dgsm.c        The glue that makes osmo-hlr distinguish between local IMSIs and those that are proxied to another</span><br><span style="color: hsl(120, 100%, 40%);">+ *                    osmo-hlr.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  src/dgsm_vty.c    Config.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *  @{</span><br><span style="color: hsl(120, 100%, 40%);">+ * \file mslookup.c</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct value_string osmo_mslookup_id_type_names[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+       { OSMO_MSLOOKUP_ID_NONE, "none" },</span><br><span style="color: hsl(120, 100%, 40%);">+  { OSMO_MSLOOKUP_ID_IMSI, "imsi" },</span><br><span style="color: hsl(120, 100%, 40%);">+  { OSMO_MSLOOKUP_ID_MSISDN, "msisdn" },</span><br><span style="color: hsl(120, 100%, 40%);">+      {}</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct value_string osmo_mslookup_result_code_names[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+     { OSMO_MSLOOKUP_RC_NONE, "none" },</span><br><span style="color: hsl(120, 100%, 40%);">+  { OSMO_MSLOOKUP_RC_RESULT, "result" },</span><br><span style="color: hsl(120, 100%, 40%);">+      { OSMO_MSLOOKUP_RC_NOT_FOUND, "not-found" },</span><br><span style="color: hsl(120, 100%, 40%);">+        {}</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Compare two struct osmo_mslookup_id.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns   0 if a and b are equal,</span><br><span style="color: hsl(120, 100%, 40%);">+ *          < 0 if a (or the ID type / start of ID) is < b,</span><br><span style="color: hsl(120, 100%, 40%);">+ *          > 0 if a (or the ID type / start of ID) is > b.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_mslookup_id_cmp(const struct osmo_mslookup_id *a, const struct osmo_mslookup_id *b)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int cmp;</span><br><span style="color: hsl(120, 100%, 40%);">+      if (a == b)</span><br><span style="color: hsl(120, 100%, 40%);">+           return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!a)</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!b)</span><br><span style="color: hsl(120, 100%, 40%);">+               return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   cmp = OSMO_CMP(a->type, b->type);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (cmp)</span><br><span style="color: hsl(120, 100%, 40%);">+              return cmp;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (a->type) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case OSMO_MSLOOKUP_ID_IMSI:</span><br><span style="color: hsl(120, 100%, 40%);">+           return strncmp(a->imsi, b->imsi, sizeof(a->imsi));</span><br><span style="color: hsl(120, 100%, 40%);">+   case OSMO_MSLOOKUP_ID_MSISDN:</span><br><span style="color: hsl(120, 100%, 40%);">+         return strncmp(a->msisdn, b->msisdn, sizeof(a->msisdn));</span><br><span style="color: hsl(120, 100%, 40%);">+     default:</span><br><span style="color: hsl(120, 100%, 40%);">+              return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+bool osmo_mslookup_id_valid(const struct osmo_mslookup_id *id)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     switch (id->type) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case OSMO_MSLOOKUP_ID_IMSI:</span><br><span style="color: hsl(120, 100%, 40%);">+           return osmo_imsi_str_valid(id->imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+      case OSMO_MSLOOKUP_ID_MSISDN:</span><br><span style="color: hsl(120, 100%, 40%);">+         return osmo_msisdn_str_valid(id->msisdn);</span><br><span style="color: hsl(120, 100%, 40%);">+  default:</span><br><span style="color: hsl(120, 100%, 40%);">+              return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+bool osmo_mslookup_service_valid(const char *service)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      return strlen(service) > 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Write ID and ID type to a buffer.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[out] buf  nul-terminated {id}.{id_type} string (e.g. "1234.msisdn") or</span><br><span style="color: hsl(120, 100%, 40%);">+*                   "?.none" if the ID type is invalid.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns amount of bytes written to buf.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+size_t osmo_mslookup_id_name_buf(char *buf, size_t buflen, const struct osmo_mslookup_id *id)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };</span><br><span style="color: hsl(120, 100%, 40%);">+        switch (id->type) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case OSMO_MSLOOKUP_ID_IMSI:</span><br><span style="color: hsl(120, 100%, 40%);">+           OSMO_STRBUF_PRINTF(sb, "%s", id->imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+          break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case OSMO_MSLOOKUP_ID_MSISDN:</span><br><span style="color: hsl(120, 100%, 40%);">+         OSMO_STRBUF_PRINTF(sb, "%s", id->msisdn);</span><br><span style="color: hsl(120, 100%, 40%);">+                break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_STRBUF_PRINTF(sb, "?");</span><br><span style="color: hsl(120, 100%, 40%);">+                break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_STRBUF_PRINTF(sb, ".%s", osmo_mslookup_id_type_name(id->type));</span><br><span style="color: hsl(120, 100%, 40%);">+     return sb.chars_needed;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Same as osmo_mslookup_id_name_buf(), but return a talloc allocated string of sufficient size. */</span><br><span style="color: hsl(120, 100%, 40%);">+char *osmo_mslookup_id_name_c(void *ctx, const struct osmo_mslookup_id *id)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_mslookup_id_name_buf, id)</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Same as osmo_mslookup_id_name_buf(), but directly return the char* (for printf-like string formats). */</span><br><span style="color: hsl(120, 100%, 40%);">+char *osmo_mslookup_id_name_b(char *buf, size_t buflen, const struct osmo_mslookup_id *id)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  int rc = osmo_mslookup_id_name_buf(buf, buflen, id);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (rc < 0 && buflen)</span><br><span style="color: hsl(120, 100%, 40%);">+              buf[0] = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+        return buf;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Write mslookup result string to buffer.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] query  with the service, ID and ID type to be written to buf like a domain string, or NULL to omit.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] result with the result code, IPv4/v6 and age to be written to buf or NULL to omit.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[out] buf  result as flat string, which looks like the following for a valid query and result with IPv4 and v6</span><br><span style="color: hsl(120, 100%, 40%);">+ *                  answer: "sip.voice.1234.msisdn -> ipv4: 42.42.42.42:1337 -> ipv6: [1234:5678:9ABC::]:1338 (age=1)",</span><br><span style="color: hsl(120, 100%, 40%);">+ *                  the result part can also be " -> timeout" or " -> rc=5" depending on the result code.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns amount of bytes written to buf.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+size_t osmo_mslookup_result_to_str_buf(char *buf, size_t buflen,</span><br><span style="color: hsl(120, 100%, 40%);">+                              const struct osmo_mslookup_query *query,</span><br><span style="color: hsl(120, 100%, 40%);">+                              const struct osmo_mslookup_result *result)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_strbuf sb = { .buf = buf, .len = buflen };</span><br><span style="color: hsl(120, 100%, 40%);">+        if (query) {</span><br><span style="color: hsl(120, 100%, 40%);">+          OSMO_STRBUF_PRINTF(sb, "%s.", query->service);</span><br><span style="color: hsl(120, 100%, 40%);">+           OSMO_STRBUF_APPEND(sb, osmo_mslookup_id_name_buf, &query->id);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (result && result->rc == OSMO_MSLOOKUP_RC_NONE)</span><br><span style="color: hsl(120, 100%, 40%);">+         result = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+        if (result) {</span><br><span style="color: hsl(120, 100%, 40%);">+         if (result->rc != OSMO_MSLOOKUP_RC_RESULT) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       OSMO_STRBUF_PRINTF(sb, " %s", osmo_mslookup_result_code_name(result->rc));</span><br><span style="color: hsl(120, 100%, 40%);">+               } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      if (result->host_v4.ip[0]) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               OSMO_STRBUF_PRINTF(sb, " -> ipv4: " OSMO_SOCKADDR_STR_FMT,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                  OSMO_SOCKADDR_STR_FMT_ARGS(&result->host_v4));</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (result->host_v6.ip[0]) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               OSMO_STRBUF_PRINTF(sb, " -> ipv6: " OSMO_SOCKADDR_STR_FMT,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                  OSMO_SOCKADDR_STR_FMT_ARGS(&result->host_v6));</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+                     OSMO_STRBUF_PRINTF(sb, " (age=%u)", result->age);</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+             OSMO_STRBUF_PRINTF(sb, " %s", result->last ? "(last)" : "(not-last)");</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     return sb.chars_needed;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Same as osmo_mslookup_result_to_str_buf(), but return a talloc allocated string of sufficient size. */</span><br><span style="color: hsl(120, 100%, 40%);">+char *osmo_mslookup_result_name_c(void *ctx,</span><br><span style="color: hsl(120, 100%, 40%);">+                                  const struct osmo_mslookup_query *query,</span><br><span style="color: hsl(120, 100%, 40%);">+                              const struct osmo_mslookup_result *result)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_mslookup_result_to_str_buf, query, result)</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Same as osmo_mslookup_result_to_str_buf(), but directly return the char* (for printf-like string formats). */</span><br><span style="color: hsl(120, 100%, 40%);">+char *osmo_mslookup_result_name_b(char *buf, size_t buflen,</span><br><span style="color: hsl(120, 100%, 40%);">+                               const struct osmo_mslookup_query *query,</span><br><span style="color: hsl(120, 100%, 40%);">+                              const struct osmo_mslookup_result *result)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       int rc = osmo_mslookup_result_to_str_buf(buf, buflen, query, result);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rc < 0 && buflen)</span><br><span style="color: hsl(120, 100%, 40%);">+              buf[0] = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+        return buf;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Copy part of a string to a buffer and nul-terminate it.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, negative on error.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int token(char *dest, size_t dest_size, const char *start, const char *end)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       int len;</span><br><span style="color: hsl(120, 100%, 40%);">+      if (start >= end)</span><br><span style="color: hsl(120, 100%, 40%);">+          return -10;</span><br><span style="color: hsl(120, 100%, 40%);">+   len = end - start;</span><br><span style="color: hsl(120, 100%, 40%);">+    if (len >= dest_size)</span><br><span style="color: hsl(120, 100%, 40%);">+              return -11;</span><br><span style="color: hsl(120, 100%, 40%);">+   strncpy(dest, start, len);</span><br><span style="color: hsl(120, 100%, 40%);">+    dest[len] = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+     return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Parse a string like "foo.moo.goo.123456789012345.msisdn" into service="foo.moo.goo", id="123456789012345" and</span><br><span style="color: hsl(120, 100%, 40%);">+ * id_type="msisdn", placed in a struct osmo_mslookup_query.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param q  Write parsed query to this osmo_mslookup_query.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param domain  Human readable domain string like "sip.voice.12345678.msisdn".</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, negative on error.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_mslookup_query_init_from_domain_str(struct osmo_mslookup_query *q, const char *domain)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *last_dot;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *second_last_dot;</span><br><span style="color: hsl(120, 100%, 40%);">+  const char *id_type;</span><br><span style="color: hsl(120, 100%, 40%);">+  const char *id;</span><br><span style="color: hsl(120, 100%, 40%);">+       int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     *q = (struct osmo_mslookup_query){};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!domain)</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  last_dot = strrchr(domain, '.');</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!last_dot)</span><br><span style="color: hsl(120, 100%, 40%);">+                return -2;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (last_dot <= domain)</span><br><span style="color: hsl(120, 100%, 40%);">+            return -3;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  for (second_last_dot = last_dot - 1; second_last_dot > domain && *second_last_dot != '.'; second_last_dot--);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (second_last_dot == domain || *second_last_dot != '.')</span><br><span style="color: hsl(120, 100%, 40%);">+             return -3;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  id_type = last_dot + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!*id_type)</span><br><span style="color: hsl(120, 100%, 40%);">+                return -4;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  q->id.type = get_string_value(osmo_mslookup_id_type_names, id_type);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     id = second_last_dot + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+     switch (q->id.type) {</span><br><span style="color: hsl(120, 100%, 40%);">+      case OSMO_MSLOOKUP_ID_IMSI:</span><br><span style="color: hsl(120, 100%, 40%);">+           rc = token(q->id.imsi, sizeof(q->id.imsi), id, last_dot);</span><br><span style="color: hsl(120, 100%, 40%);">+               if (rc)</span><br><span style="color: hsl(120, 100%, 40%);">+                       return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+            if (!osmo_imsi_str_valid(q->id.imsi))</span><br><span style="color: hsl(120, 100%, 40%);">+                      return -5;</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case OSMO_MSLOOKUP_ID_MSISDN:</span><br><span style="color: hsl(120, 100%, 40%);">+         rc = token(q->id.msisdn, sizeof(q->id.msisdn), id, last_dot);</span><br><span style="color: hsl(120, 100%, 40%);">+           if (rc)</span><br><span style="color: hsl(120, 100%, 40%);">+                       return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+            if (!osmo_msisdn_str_valid(q->id.msisdn))</span><br><span style="color: hsl(120, 100%, 40%);">+                  return -6;</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              return -7;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return token(q->service, sizeof(q->service), domain, second_last_dot);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! @} */</span><br><span>diff --git a/src/mslookup/mslookup_client.c b/src/mslookup/mslookup_client.c</span><br><span>new file mode 100644</span><br><span>index 0000000..67977e4</span><br><span>--- /dev/null</span><br><span>+++ b/src/mslookup/mslookup_client.c</span><br><span>@@ -0,0 +1,310 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/hlr/logging.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mslookup/mslookup_client.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Lookup client's internal data for a query. */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_mslookup_client {</span><br><span style="color: hsl(120, 100%, 40%);">+      struct llist_head lookup_methods;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct llist_head requests;</span><br><span style="color: hsl(120, 100%, 40%);">+   uint32_t next_request_handle;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Lookup client's internal data for a query.</span><br><span style="color: hsl(120, 100%, 40%);">+ * The request methods only get to see the query part, and result handling is done commonly for all request methods. */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_mslookup_client_request {</span><br><span style="color: hsl(120, 100%, 40%);">+       struct llist_head entry;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_mslookup_client *client;</span><br><span style="color: hsl(120, 100%, 40%);">+  uint32_t request_handle;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_mslookup_query query;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_mslookup_query_handling handling;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_timer_list timeout;</span><br><span style="color: hsl(120, 100%, 40%);">+       bool waiting_min_delay;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_mslookup_result result;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_mslookup_client_request *get_request(struct osmo_mslookup_client *client, uint32_t request_handle)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_mslookup_client_request *r;</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!request_handle)</span><br><span style="color: hsl(120, 100%, 40%);">+          return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  llist_for_each_entry(r, &client->requests, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+            if (r->request_handle == request_handle)</span><br><span style="color: hsl(120, 100%, 40%);">+                   return r;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_mslookup_client *osmo_mslookup_client_new(void *ctx)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_mslookup_client *client = talloc_zero(ctx, struct osmo_mslookup_client);</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_ASSERT(client);</span><br><span style="color: hsl(120, 100%, 40%);">+  INIT_LLIST_HEAD(&client->lookup_methods);</span><br><span style="color: hsl(120, 100%, 40%);">+      INIT_LLIST_HEAD(&client->requests);</span><br><span style="color: hsl(120, 100%, 40%);">+    return client;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Return whether any lookup methods are available.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] client  Client to query.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return true when a client is present that has at least one osmo_mslookup_client_method registered.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+bool osmo_mslookup_client_active(struct osmo_mslookup_client *client)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!client)</span><br><span style="color: hsl(120, 100%, 40%);">+          return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (llist_empty(&client->lookup_methods))</span><br><span style="color: hsl(120, 100%, 40%);">+              return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ return true;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void _osmo_mslookup_client_method_del(struct osmo_mslookup_client_method *method)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        if (method->destruct)</span><br><span style="color: hsl(120, 100%, 40%);">+              method->destruct(method);</span><br><span style="color: hsl(120, 100%, 40%);">+  llist_del(&method->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+     talloc_free(method);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Stop and free mslookup client and all registered lookup methods.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_mslookup_client_free(struct osmo_mslookup_client *client)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_mslookup_client_method *m, *n;</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!client)</span><br><span style="color: hsl(120, 100%, 40%);">+          return;</span><br><span style="color: hsl(120, 100%, 40%);">+       llist_for_each_entry_safe(m, n, &client->lookup_methods, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+              _osmo_mslookup_client_method_del(m);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     talloc_free(client);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Add an osmo_mslookup_client_method to service MS Lookup requests.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Note, osmo_mslookup_client_method_del() will talloc_free() the method pointer, so it needs to be dynamically</span><br><span style="color: hsl(120, 100%, 40%);">+ * allocated.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param client  The osmo_mslookup_client instance to add to.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param method  A fully initialized method struct, allocated by talloc.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_mslookup_client_method_add(struct osmo_mslookup_client *client,</span><br><span style="color: hsl(120, 100%, 40%);">+                                struct osmo_mslookup_client_method *method)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   method->client = client;</span><br><span style="color: hsl(120, 100%, 40%);">+   llist_add_tail(&method->entry, &client->lookup_methods);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \return false if the method was not listed, true if the method was listed, removed and talloc_free()d.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+bool osmo_mslookup_client_method_del(struct osmo_mslookup_client *client,</span><br><span style="color: hsl(120, 100%, 40%);">+                                    struct osmo_mslookup_client_method *method)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_mslookup_client_method *m;</span><br><span style="color: hsl(120, 100%, 40%);">+        llist_for_each_entry(m, &client->lookup_methods, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (m == method) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    _osmo_mslookup_client_method_del(method);</span><br><span style="color: hsl(120, 100%, 40%);">+                     return true;</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     return false;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void osmo_mslookup_request_send_result(struct osmo_mslookup_client_request *r, bool finish)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_mslookup_client *client = r->client;</span><br><span style="color: hsl(120, 100%, 40%);">+   uint32_t request_handle = r->request_handle;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     r->result.last = finish;</span><br><span style="color: hsl(120, 100%, 40%);">+   r->handling.result_cb(r->client, r->request_handle, &r->query, &r->result);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Make sure the request struct is discarded.</span><br><span style="color: hsl(120, 100%, 40%);">+  * The result_cb() may already have triggered a cleanup, so query by request_handle. */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (finish)</span><br><span style="color: hsl(120, 100%, 40%);">+           osmo_mslookup_client_request_cancel(client, request_handle);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_mslookup_client_rx_result(struct osmo_mslookup_client *client, uint32_t request_handle,</span><br><span style="color: hsl(120, 100%, 40%);">+                              const struct osmo_mslookup_result *result)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_mslookup_client_request *req = get_request(client, request_handle);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!req) {</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGP(DMSLOOKUP, LOGL_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                "Internal error: Got mslookup result for a request that does not exist (handle %u)\n",</span><br><span style="color: hsl(120, 100%, 40%);">+              req->request_handle);</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Ignore incoming results that are not successful */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (result->rc != OSMO_MSLOOKUP_RC_RESULT)</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* If we already stored an earlier successful result, keep that if its age is younger. */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (req->result.rc == OSMO_MSLOOKUP_RC_RESULT</span><br><span style="color: hsl(120, 100%, 40%);">+          && result->age >= req->result.age)</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     req->result = *result;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* If age == 0, it doesn't get any better, so return the result immediately. */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (req->result.age == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                osmo_mslookup_request_send_result(req, true);</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (req->waiting_min_delay)</span><br><span style="color: hsl(120, 100%, 40%);">+                return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_mslookup_request_send_result(req, false);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void _osmo_mslookup_client_request_cleanup(struct osmo_mslookup_client_request *r)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_mslookup_client_method *m;</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_timer_del(&r->timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+   llist_for_each_entry(m, &r->client->lookup_methods, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (!m->request_cleanup)</span><br><span style="color: hsl(120, 100%, 40%);">+                   continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             m->request_cleanup(m, r->request_handle);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_del(&r->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+  talloc_free(r);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void timeout_cb(void *data);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void set_timer(struct osmo_mslookup_client_request *r, unsigned long milliseconds)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_timer_setup(&r->timeout, timeout_cb, r);</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_timer_schedule(&r->timeout, milliseconds / 1000, (milliseconds % 1000) * 1000);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void timeout_cb(void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_mslookup_client_request *r = data;</span><br><span style="color: hsl(120, 100%, 40%);">+        if (r->waiting_min_delay) {</span><br><span style="color: hsl(120, 100%, 40%);">+                /* The initial delay has passed. See if it stops here, or whether the overall timeout continues. */</span><br><span style="color: hsl(120, 100%, 40%);">+           r->waiting_min_delay = false;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            if (r->handling.result_timeout_milliseconds <= r->handling.min_wait_milliseconds) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  /* It ends here. Return a final result. */</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (r->result.rc != OSMO_MSLOOKUP_RC_RESULT)</span><br><span style="color: hsl(120, 100%, 40%);">+                               r->result.rc = OSMO_MSLOOKUP_RC_NOT_FOUND;</span><br><span style="color: hsl(120, 100%, 40%);">+                 osmo_mslookup_request_send_result(r, true);</span><br><span style="color: hsl(120, 100%, 40%);">+                   return;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           /* We continue to listen for results. If one is already on record, send it now. */</span><br><span style="color: hsl(120, 100%, 40%);">+            if (r->result.rc == OSMO_MSLOOKUP_RC_RESULT)</span><br><span style="color: hsl(120, 100%, 40%);">+                       osmo_mslookup_request_send_result(r, false);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                set_timer(r, r->handling.result_timeout_milliseconds - r->handling.min_wait_milliseconds);</span><br><span style="color: hsl(120, 100%, 40%);">+              return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     /* The final timeout has passed, finish and clean up the request. */</span><br><span style="color: hsl(120, 100%, 40%);">+  switch (r->result.rc) {</span><br><span style="color: hsl(120, 100%, 40%);">+    case OSMO_MSLOOKUP_RC_RESULT:</span><br><span style="color: hsl(120, 100%, 40%);">+         /* If the rc == OSMO_MSLOOKUP_RC_RESULT, this result has already been sent.</span><br><span style="color: hsl(120, 100%, 40%);">+            * Don't send it again, instead send an RC_NONE, last=true result. */</span><br><span style="color: hsl(120, 100%, 40%);">+             r->result.rc = OSMO_MSLOOKUP_RC_NONE;</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              r->result.rc = OSMO_MSLOOKUP_RC_NOT_FOUND;</span><br><span style="color: hsl(120, 100%, 40%);">+         break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_mslookup_request_send_result(r, true);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Launch a subscriber lookup with the provided query.</span><br><span style="color: hsl(120, 100%, 40%);">+ * A request is cleared implicitly when the handling->result_cb is invoked; if the quer->priv pointer becomes invalid</span><br><span style="color: hsl(120, 100%, 40%);">+ * before that, a request should be canceled by calling osmo_mslookup_client_request_cancel() with the returned</span><br><span style="color: hsl(120, 100%, 40%);">+ * request_handle. A request handle of zero indicates error.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return a nonzero request_handle that allows ending the request, or 0 on invalid query data. */</span><br><span style="color: hsl(120, 100%, 40%);">+uint32_t osmo_mslookup_client_request(struct osmo_mslookup_client *client,</span><br><span style="color: hsl(120, 100%, 40%);">+                               const struct osmo_mslookup_query *query,</span><br><span style="color: hsl(120, 100%, 40%);">+                                      const struct osmo_mslookup_query_handling *handling)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_mslookup_client_request *r;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_mslookup_client_request *other;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_mslookup_client_method *m;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!osmo_mslookup_service_valid(query->service)</span><br><span style="color: hsl(120, 100%, 40%);">+       || !osmo_mslookup_id_valid(&query->id)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          char buf[256];</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGP(DMSLOOKUP, LOGL_ERROR, "Invalid query: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+               osmo_mslookup_result_name_b(buf, sizeof(buf), query, NULL));</span><br><span style="color: hsl(120, 100%, 40%);">+             return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   r = talloc_zero(client, struct osmo_mslookup_client_request);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(r);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* A request_handle of zero means error, so make sure we don't use a zero handle. */</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!client->next_request_handle)</span><br><span style="color: hsl(120, 100%, 40%);">+          client->next_request_handle++;</span><br><span style="color: hsl(120, 100%, 40%);">+     *r = (struct osmo_mslookup_client_request){</span><br><span style="color: hsl(120, 100%, 40%);">+           .client = client,</span><br><span style="color: hsl(120, 100%, 40%);">+             .query = *query,</span><br><span style="color: hsl(120, 100%, 40%);">+              .handling = *handling,</span><br><span style="color: hsl(120, 100%, 40%);">+                .request_handle = client->next_request_handle++,</span><br><span style="color: hsl(120, 100%, 40%);">+   };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!r->handling.result_timeout_milliseconds)</span><br><span style="color: hsl(120, 100%, 40%);">+              r->handling.result_timeout_milliseconds = r->handling.min_wait_milliseconds;</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!r->handling.result_timeout_milliseconds)</span><br><span style="color: hsl(120, 100%, 40%);">+              r->handling.result_timeout_milliseconds = 1000;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Paranoia: make sure a request_handle exists only once, by expiring an already existing one. This is unlikely</span><br><span style="color: hsl(120, 100%, 40%);">+        * to happen in practice: before we get near wrapping a uint32_t range, previous requests should long have</span><br><span style="color: hsl(120, 100%, 40%);">+     * timed out or ended. */</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry(other, &client->requests, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (other->request_handle != r->request_handle)</span><br><span style="color: hsl(120, 100%, 40%);">+                 continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_mslookup_request_send_result(other, true);</span><br><span style="color: hsl(120, 100%, 40%);">+               /* we're sure it exists only once. */</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Now sure that the new request_handle does not exist a second time. */</span><br><span style="color: hsl(120, 100%, 40%);">+      llist_add_tail(&r->entry, &client->requests);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (r->handling.min_wait_milliseconds) {</span><br><span style="color: hsl(120, 100%, 40%);">+           r->waiting_min_delay = true;</span><br><span style="color: hsl(120, 100%, 40%);">+               set_timer(r, r->handling.min_wait_milliseconds);</span><br><span style="color: hsl(120, 100%, 40%);">+   } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              set_timer(r, r->handling.result_timeout_milliseconds);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Let the lookup implementations know */</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry(m, &client->lookup_methods, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+              m->request(m, query, r->request_handle);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     return r->request_handle;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! End or cancel a subscriber lookup. This *must* be invoked exactly once per osmo_mslookup_client_request() invocation,</span><br><span style="color: hsl(120, 100%, 40%);">+ * either after a lookup has concluded or to abort an ongoing lookup.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] request_handle  The request_handle returned by an osmo_mslookup_client_request() invocation.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_mslookup_client_request_cancel(struct osmo_mslookup_client *client, uint32_t request_handle)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_mslookup_client_request *r = get_request(client, request_handle);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!r)</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       _osmo_mslookup_client_request_cleanup(r);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/mslookup/mslookup_client_fake.c b/src/mslookup/mslookup_client_fake.c</span><br><span>new file mode 100644</span><br><span>index 0000000..cae73f2</span><br><span>--- /dev/null</span><br><span>+++ b/src/mslookup/mslookup_client_fake.c</span><br><span>@@ -0,0 +1,156 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/hlr/logging.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mslookup/mslookup_client.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mslookup/mslookup_client_fake.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Fake mslookup method */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct fake_lookup_state {</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_mslookup_client *client;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct llist_head requests;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_timer_list async_response_timer;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_mslookup_fake_response *responses;</span><br><span style="color: hsl(120, 100%, 40%);">+        size_t responses_len;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct fake_lookup_request {</span><br><span style="color: hsl(120, 100%, 40%);">+     struct llist_head entry;</span><br><span style="color: hsl(120, 100%, 40%);">+      uint32_t request_handle;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_mslookup_query query;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct timeval received_at;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Args for osmo_timer_schedule: seconds and microseconds. */</span><br><span style="color: hsl(120, 100%, 40%);">+#define ASYNC_RESPONSE_PERIOD 0, (1e6 / 10)</span><br><span style="color: hsl(120, 100%, 40%);">+static void fake_lookup_async_response(void *state);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void fake_lookup_request(struct osmo_mslookup_client_method *method,</span><br><span style="color: hsl(120, 100%, 40%);">+                           const struct osmo_mslookup_query *query,</span><br><span style="color: hsl(120, 100%, 40%);">+                              uint32_t request_handle)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct fake_lookup_state *state = method->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+    char buf[256];</span><br><span style="color: hsl(120, 100%, 40%);">+        LOGP(DMSLOOKUP, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_mslookup_result_name_b(buf, sizeof(buf), query, NULL));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* A real implementation would send packets to some remote server.</span><br><span style="color: hsl(120, 100%, 40%);">+     * Here this is simulated: add to the list of requests, which fake_lookup_async_response() will reply upon</span><br><span style="color: hsl(120, 100%, 40%);">+     * according to the test data listing the replies that the test wants to generate. */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       struct fake_lookup_request *r = talloc_zero(method->client, struct fake_lookup_request);</span><br><span style="color: hsl(120, 100%, 40%);">+   *r = (struct fake_lookup_request){</span><br><span style="color: hsl(120, 100%, 40%);">+            .request_handle = request_handle,</span><br><span style="color: hsl(120, 100%, 40%);">+             .query = *query,</span><br><span style="color: hsl(120, 100%, 40%);">+      };</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_gettimeofday(&r->received_at, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+      llist_add_tail(&r->entry, &state->requests);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void fake_lookup_request_cleanup(struct osmo_mslookup_client_method *method,</span><br><span style="color: hsl(120, 100%, 40%);">+                                  uint32_t request_handle)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct fake_lookup_state *state = method->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Tear down any state associated with this handle. */</span><br><span style="color: hsl(120, 100%, 40%);">+        struct fake_lookup_request *r;</span><br><span style="color: hsl(120, 100%, 40%);">+        llist_for_each_entry(r, &state->requests, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (r->request_handle != request_handle)</span><br><span style="color: hsl(120, 100%, 40%);">+                   continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             llist_del(&r->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+          talloc_free(r);</span><br><span style="color: hsl(120, 100%, 40%);">+               LOGP(DMSLOOKUP, LOGL_DEBUG, "%s() ok\n", __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     LOGP(DMSLOOKUP, LOGL_DEBUG, "%s() FAILED\n", __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void fake_lookup_async_response(void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct fake_lookup_state *state = data;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct fake_lookup_request *req, *n;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct timeval now;</span><br><span style="color: hsl(120, 100%, 40%);">+   char str[256];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_gettimeofday(&now, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  llist_for_each_entry_safe(req, n, &state->requests, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+           struct osmo_mslookup_fake_response *resp;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           for (resp = state->responses;</span><br><span style="color: hsl(120, 100%, 40%);">+                   (resp - state->responses) < state->responses_len;</span><br><span style="color: hsl(120, 100%, 40%);">+                    resp++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        struct timeval diff;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                        if (resp->sent)</span><br><span style="color: hsl(120, 100%, 40%);">+                            continue;</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (osmo_mslookup_id_cmp(&req->query.id, &resp->for_id) != 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                           continue;</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (strcmp(req->query.service, resp->for_service) != 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                         continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                   timersub(&now, &req->received_at, &diff);</span><br><span style="color: hsl(120, 100%, 40%);">+                      if (timercmp(&diff, &resp->time_to_reply, <))</span><br><span style="color: hsl(120, 100%, 40%);">+                           continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                   /* It's time to reply to this request. */</span><br><span style="color: hsl(120, 100%, 40%);">+                 LOGP(DMSLOOKUP, LOGL_DEBUG, "osmo_mslookup_client_rx_result(): %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                            osmo_mslookup_result_name_b(str, sizeof(str), &req->query, &resp->result));</span><br><span style="color: hsl(120, 100%, 40%);">+                        osmo_mslookup_client_rx_result(state->client, req->request_handle, &resp->result);</span><br><span style="color: hsl(120, 100%, 40%);">+                       resp->sent = true;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                       /* The req will have been cleaned up now, so we must not iterate over state->responses anymore</span><br><span style="color: hsl(120, 100%, 40%);">+                      * with this req. */</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_timer_schedule(&state->async_response_timer, ASYNC_RESPONSE_PERIOD);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_mslookup_client_method *osmo_mslookup_client_add_fake(struct osmo_mslookup_client *client,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                           struct osmo_mslookup_fake_response *responses,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                size_t responses_len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_mslookup_client_method *method = talloc_zero(client, struct osmo_mslookup_client_method);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(method);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        struct fake_lookup_state *state = talloc_zero(method, struct fake_lookup_state);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(state);</span><br><span style="color: hsl(120, 100%, 40%);">+   *state = (struct fake_lookup_state){</span><br><span style="color: hsl(120, 100%, 40%);">+          .client = client,</span><br><span style="color: hsl(120, 100%, 40%);">+             .responses = responses,</span><br><span style="color: hsl(120, 100%, 40%);">+               .responses_len = responses_len,</span><br><span style="color: hsl(120, 100%, 40%);">+       };</span><br><span style="color: hsl(120, 100%, 40%);">+    INIT_LLIST_HEAD(&state->requests);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   *method = (struct osmo_mslookup_client_method){</span><br><span style="color: hsl(120, 100%, 40%);">+               .name = "fake",</span><br><span style="color: hsl(120, 100%, 40%);">+             .priv = state,</span><br><span style="color: hsl(120, 100%, 40%);">+                .request = fake_lookup_request,</span><br><span style="color: hsl(120, 100%, 40%);">+               .request_cleanup = fake_lookup_request_cleanup,</span><br><span style="color: hsl(120, 100%, 40%);">+       };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_timer_setup(&state->async_response_timer, fake_lookup_async_response, state);</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_mslookup_client_method_add(client, method);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_timer_schedule(&state->async_response_timer, ASYNC_RESPONSE_PERIOD);</span><br><span style="color: hsl(120, 100%, 40%);">+      return method;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/tests/Makefile.am b/tests/Makefile.am</span><br><span>index f8591a5..776f8a9 100644</span><br><span>--- a/tests/Makefile.am</span><br><span>+++ b/tests/Makefile.am</span><br><span>@@ -3,6 +3,7 @@</span><br><span>         gsup_server \</span><br><span>        db \</span><br><span>         db_upgrade \</span><br><span style="color: hsl(120, 100%, 40%);">+  mslookup \</span><br><span>   $(NULL)</span><br><span> </span><br><span> # The `:;' works around a Bash 3.2 bug when the output is not writeable.</span><br><span>diff --git a/tests/mslookup/Makefile.am b/tests/mslookup/Makefile.am</span><br><span>new file mode 100644</span><br><span>index 0000000..71602a3</span><br><span>--- /dev/null</span><br><span>+++ b/tests/mslookup/Makefile.am</span><br><span>@@ -0,0 +1,49 @@</span><br><span style="color: hsl(120, 100%, 40%);">+AM_CPPFLAGS = \</span><br><span style="color: hsl(120, 100%, 40%);">+     $(all_includes) \</span><br><span style="color: hsl(120, 100%, 40%);">+     $(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AM_CFLAGS = \</span><br><span style="color: hsl(120, 100%, 40%);">+      -Wall \</span><br><span style="color: hsl(120, 100%, 40%);">+       -ggdb3 \</span><br><span style="color: hsl(120, 100%, 40%);">+      -I$(top_srcdir)/include \</span><br><span style="color: hsl(120, 100%, 40%);">+     $(LIBOSMOCORE_CFLAGS) \</span><br><span style="color: hsl(120, 100%, 40%);">+       $(LIBOSMOGSM_CFLAGS) \</span><br><span style="color: hsl(120, 100%, 40%);">+        $(LIBOSMOABIS_CFLAGS) \</span><br><span style="color: hsl(120, 100%, 40%);">+       $(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AM_LDFLAGS = \</span><br><span style="color: hsl(120, 100%, 40%);">+     -no-install \</span><br><span style="color: hsl(120, 100%, 40%);">+ $(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+EXTRA_DIST = \</span><br><span style="color: hsl(120, 100%, 40%);">+     mslookup_client_test.err \</span><br><span style="color: hsl(120, 100%, 40%);">+    mslookup_test.err \</span><br><span style="color: hsl(120, 100%, 40%);">+   $(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+check_PROGRAMS = \</span><br><span style="color: hsl(120, 100%, 40%);">+ mslookup_client_test \</span><br><span style="color: hsl(120, 100%, 40%);">+        mslookup_test \</span><br><span style="color: hsl(120, 100%, 40%);">+       $(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+mslookup_test_SOURCES = \</span><br><span style="color: hsl(120, 100%, 40%);">+  mslookup_test.c \</span><br><span style="color: hsl(120, 100%, 40%);">+     $(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+mslookup_test_LDADD = \</span><br><span style="color: hsl(120, 100%, 40%);">+      $(top_builddir)/src/mslookup/libosmo-mslookup.la \</span><br><span style="color: hsl(120, 100%, 40%);">+    $(LIBOSMOGSM_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+mslookup_client_test_SOURCES = \</span><br><span style="color: hsl(120, 100%, 40%);">+   mslookup_client_test.c \</span><br><span style="color: hsl(120, 100%, 40%);">+      $(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+mslookup_client_test_LDADD = \</span><br><span style="color: hsl(120, 100%, 40%);">+       $(top_builddir)/src/mslookup/libosmo-mslookup.la \</span><br><span style="color: hsl(120, 100%, 40%);">+    $(LIBOSMOGSM_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+.PHONY: update_exp</span><br><span style="color: hsl(120, 100%, 40%);">+update_exp:</span><br><span style="color: hsl(120, 100%, 40%);">+    for i in $(check_PROGRAMS); do \</span><br><span style="color: hsl(120, 100%, 40%);">+              echo "Updating $$i.err"; \</span><br><span style="color: hsl(120, 100%, 40%);">+          $(builddir)/$$i 2>"$(srcdir)/$$i.err"; \</span><br><span style="color: hsl(120, 100%, 40%);">+ done</span><br><span>diff --git a/tests/mslookup/mslookup_client_test.c b/tests/mslookup/mslookup_client_test.c</span><br><span>new file mode 100644</span><br><span>index 0000000..40be011</span><br><span>--- /dev/null</span><br><span>+++ b/tests/mslookup/mslookup_client_test.c</span><br><span>@@ -0,0 +1,245 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU Affero General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <sys/time.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/application.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/select.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/hlr/logging.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mslookup/mslookup_client_fake.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mslookup/mslookup_client.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define SERVICE_HLR_GSUP "gsup.hlr"</span><br><span style="color: hsl(120, 100%, 40%);">+#define SERVICE_SIP "sip.voice"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void *ctx = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_mslookup_fake_response fake_lookup_responses[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+       {</span><br><span style="color: hsl(120, 100%, 40%);">+             .time_to_reply = { .tv_sec = 1, },</span><br><span style="color: hsl(120, 100%, 40%);">+            .for_id = {</span><br><span style="color: hsl(120, 100%, 40%);">+                   .type = OSMO_MSLOOKUP_ID_IMSI,</span><br><span style="color: hsl(120, 100%, 40%);">+                        .imsi = "1234567",</span><br><span style="color: hsl(120, 100%, 40%);">+          },</span><br><span style="color: hsl(120, 100%, 40%);">+            .for_service = SERVICE_HLR_GSUP,</span><br><span style="color: hsl(120, 100%, 40%);">+              .result = {</span><br><span style="color: hsl(120, 100%, 40%);">+                   .rc = OSMO_MSLOOKUP_RC_RESULT,</span><br><span style="color: hsl(120, 100%, 40%);">+                        .host_v4 = {</span><br><span style="color: hsl(120, 100%, 40%);">+                          .af = AF_INET,</span><br><span style="color: hsl(120, 100%, 40%);">+                                .ip = "12.34.56.7",</span><br><span style="color: hsl(120, 100%, 40%);">+                         .port = 42,</span><br><span style="color: hsl(120, 100%, 40%);">+                   },</span><br><span style="color: hsl(120, 100%, 40%);">+                    .host_v6 = {</span><br><span style="color: hsl(120, 100%, 40%);">+                          .af = AF_INET6,</span><br><span style="color: hsl(120, 100%, 40%);">+                               .ip = "be:ef:ed:ca:fe:fa:ce::1",</span><br><span style="color: hsl(120, 100%, 40%);">+                            .port = 42,</span><br><span style="color: hsl(120, 100%, 40%);">+                   },</span><br><span style="color: hsl(120, 100%, 40%);">+                    .age = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+             },</span><br><span style="color: hsl(120, 100%, 40%);">+    },</span><br><span style="color: hsl(120, 100%, 40%);">+    {</span><br><span style="color: hsl(120, 100%, 40%);">+             .time_to_reply = { .tv_usec = 600 * 1000, },</span><br><span style="color: hsl(120, 100%, 40%);">+          .for_id = {</span><br><span style="color: hsl(120, 100%, 40%);">+                   .type = OSMO_MSLOOKUP_ID_MSISDN,</span><br><span style="color: hsl(120, 100%, 40%);">+                      .msisdn = "112",</span><br><span style="color: hsl(120, 100%, 40%);">+            },</span><br><span style="color: hsl(120, 100%, 40%);">+            .for_service = SERVICE_SIP,</span><br><span style="color: hsl(120, 100%, 40%);">+           .result = {</span><br><span style="color: hsl(120, 100%, 40%);">+                   .rc = OSMO_MSLOOKUP_RC_RESULT,</span><br><span style="color: hsl(120, 100%, 40%);">+                        .host_v4 = {</span><br><span style="color: hsl(120, 100%, 40%);">+                          .af = AF_INET,</span><br><span style="color: hsl(120, 100%, 40%);">+                                .ip = "66.66.66.66",</span><br><span style="color: hsl(120, 100%, 40%);">+                                .port = 666,</span><br><span style="color: hsl(120, 100%, 40%);">+                  },</span><br><span style="color: hsl(120, 100%, 40%);">+                    .host_v6 = {</span><br><span style="color: hsl(120, 100%, 40%);">+                          .af = AF_INET,</span><br><span style="color: hsl(120, 100%, 40%);">+                                .ip = "6666:6666:6666::6",</span><br><span style="color: hsl(120, 100%, 40%);">+                          .port = 666,</span><br><span style="color: hsl(120, 100%, 40%);">+                  },</span><br><span style="color: hsl(120, 100%, 40%);">+                    .age = 423,</span><br><span style="color: hsl(120, 100%, 40%);">+           },</span><br><span style="color: hsl(120, 100%, 40%);">+    },</span><br><span style="color: hsl(120, 100%, 40%);">+    {</span><br><span style="color: hsl(120, 100%, 40%);">+             .time_to_reply = { .tv_usec = 800 * 1000, },</span><br><span style="color: hsl(120, 100%, 40%);">+          .for_id = {</span><br><span style="color: hsl(120, 100%, 40%);">+                   .type = OSMO_MSLOOKUP_ID_MSISDN,</span><br><span style="color: hsl(120, 100%, 40%);">+                      .msisdn = "112",</span><br><span style="color: hsl(120, 100%, 40%);">+            },</span><br><span style="color: hsl(120, 100%, 40%);">+            .for_service = SERVICE_SIP,</span><br><span style="color: hsl(120, 100%, 40%);">+           .result = {</span><br><span style="color: hsl(120, 100%, 40%);">+                   .rc = OSMO_MSLOOKUP_RC_RESULT,</span><br><span style="color: hsl(120, 100%, 40%);">+                        .host_v4 = {</span><br><span style="color: hsl(120, 100%, 40%);">+                          .af = AF_INET,</span><br><span style="color: hsl(120, 100%, 40%);">+                                .ip = "112.112.112.112",</span><br><span style="color: hsl(120, 100%, 40%);">+                            .port = 23,</span><br><span style="color: hsl(120, 100%, 40%);">+                   },</span><br><span style="color: hsl(120, 100%, 40%);">+                    .age = 235,</span><br><span style="color: hsl(120, 100%, 40%);">+           },</span><br><span style="color: hsl(120, 100%, 40%);">+    },</span><br><span style="color: hsl(120, 100%, 40%);">+    {</span><br><span style="color: hsl(120, 100%, 40%);">+             .time_to_reply = { .tv_sec = 1, .tv_usec = 200 * 1000, },</span><br><span style="color: hsl(120, 100%, 40%);">+             .for_id = {</span><br><span style="color: hsl(120, 100%, 40%);">+                   .type = OSMO_MSLOOKUP_ID_MSISDN,</span><br><span style="color: hsl(120, 100%, 40%);">+                      .msisdn = "112",</span><br><span style="color: hsl(120, 100%, 40%);">+            },</span><br><span style="color: hsl(120, 100%, 40%);">+            .for_service = SERVICE_SIP,</span><br><span style="color: hsl(120, 100%, 40%);">+           .result = {</span><br><span style="color: hsl(120, 100%, 40%);">+                   .rc = OSMO_MSLOOKUP_RC_RESULT,</span><br><span style="color: hsl(120, 100%, 40%);">+                        .host_v4 = {</span><br><span style="color: hsl(120, 100%, 40%);">+                          .af = AF_INET,</span><br><span style="color: hsl(120, 100%, 40%);">+                                .ip = "99.99.99.99",</span><br><span style="color: hsl(120, 100%, 40%);">+                                .port = 999,</span><br><span style="color: hsl(120, 100%, 40%);">+                  },</span><br><span style="color: hsl(120, 100%, 40%);">+                    .host_v6 = {</span><br><span style="color: hsl(120, 100%, 40%);">+                          .af = AF_INET,</span><br><span style="color: hsl(120, 100%, 40%);">+                                .ip = "9999:9999:9999::9",</span><br><span style="color: hsl(120, 100%, 40%);">+                          .port = 999,</span><br><span style="color: hsl(120, 100%, 40%);">+                  },</span><br><span style="color: hsl(120, 100%, 40%);">+                    .age = 335,</span><br><span style="color: hsl(120, 100%, 40%);">+           },</span><br><span style="color: hsl(120, 100%, 40%);">+    },</span><br><span style="color: hsl(120, 100%, 40%);">+    {</span><br><span style="color: hsl(120, 100%, 40%);">+             .time_to_reply = { .tv_sec = 1, .tv_usec = 500 * 1000, },</span><br><span style="color: hsl(120, 100%, 40%);">+             .for_id = {</span><br><span style="color: hsl(120, 100%, 40%);">+                   .type = OSMO_MSLOOKUP_ID_MSISDN,</span><br><span style="color: hsl(120, 100%, 40%);">+                      .msisdn = "112",</span><br><span style="color: hsl(120, 100%, 40%);">+            },</span><br><span style="color: hsl(120, 100%, 40%);">+            .for_service = SERVICE_SIP,</span><br><span style="color: hsl(120, 100%, 40%);">+           .result = {</span><br><span style="color: hsl(120, 100%, 40%);">+                   .rc = OSMO_MSLOOKUP_RC_RESULT,</span><br><span style="color: hsl(120, 100%, 40%);">+                        .host_v4 = {</span><br><span style="color: hsl(120, 100%, 40%);">+                          .af = AF_INET,</span><br><span style="color: hsl(120, 100%, 40%);">+                                .ip = "99.99.99.99",</span><br><span style="color: hsl(120, 100%, 40%);">+                                .port = 999,</span><br><span style="color: hsl(120, 100%, 40%);">+                  },</span><br><span style="color: hsl(120, 100%, 40%);">+                    .age = 999,</span><br><span style="color: hsl(120, 100%, 40%);">+           },</span><br><span style="color: hsl(120, 100%, 40%);">+    },</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct timeval fake_time_start_time = { 0, 0 };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define fake_time_passes(secs, usecs) do \</span><br><span style="color: hsl(120, 100%, 40%);">+{ \</span><br><span style="color: hsl(120, 100%, 40%);">+    struct timeval diff; \</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_gettimeofday_override_add(secs, usecs); \</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_clock_override_add(CLOCK_MONOTONIC, secs, usecs * 1000); \</span><br><span style="color: hsl(120, 100%, 40%);">+       timersub(&osmo_gettimeofday_override_time, &fake_time_start_time, &diff); \</span><br><span style="color: hsl(120, 100%, 40%);">+       LOGP(DMSLOOKUP, LOGL_DEBUG, "Total time passed: %d.%06d s\n", \</span><br><span style="color: hsl(120, 100%, 40%);">+            (int)diff.tv_sec, (int)diff.tv_usec); \</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_timers_prepare(); \</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_timers_update(); \</span><br><span style="color: hsl(120, 100%, 40%);">+} while (0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void fake_time_start()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct timespec *clock_override;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_gettimeofday_override_time = fake_time_start_time;</span><br><span style="color: hsl(120, 100%, 40%);">+       osmo_gettimeofday_override = true;</span><br><span style="color: hsl(120, 100%, 40%);">+    clock_override = osmo_clock_override_gettimespec(CLOCK_MONOTONIC);</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(clock_override);</span><br><span style="color: hsl(120, 100%, 40%);">+  clock_override->tv_sec = fake_time_start_time.tv_sec;</span><br><span style="color: hsl(120, 100%, 40%);">+      clock_override->tv_nsec = fake_time_start_time.tv_usec * 1000;</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_clock_override_enable(CLOCK_MONOTONIC, true);</span><br><span style="color: hsl(120, 100%, 40%);">+    fake_time_passes(0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void result_cb_once(struct osmo_mslookup_client *client,</span><br><span style="color: hsl(120, 100%, 40%);">+                    uint32_t request_handle,</span><br><span style="color: hsl(120, 100%, 40%);">+                      const struct osmo_mslookup_query *query,</span><br><span style="color: hsl(120, 100%, 40%);">+                      const struct osmo_mslookup_result *result)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      LOGP(DMSLOOKUP, LOGL_DEBUG, "result_cb(): %s\n", osmo_mslookup_result_name_c(ctx, query, result));</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int main()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      ctx = talloc_named_const(NULL, 0, "main");</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_init_logging2(ctx, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      log_set_print_filename(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+        log_set_print_level(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+   log_set_print_category(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+        log_set_print_category_hex(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+    log_set_use_color(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+     log_set_category_filter(osmo_stderr_target, DMSLOOKUP, true, LOGL_DEBUG);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   fake_time_start();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_mslookup_client *client = osmo_mslookup_client_new(ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_mslookup_client_add_fake(client, fake_lookup_responses, ARRAY_SIZE(fake_lookup_responses));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* Place some requests to be replied upon asynchronously */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_mslookup_query_handling handling = {</span><br><span style="color: hsl(120, 100%, 40%);">+              .result_timeout_milliseconds = 1, /* set some timeout < min_wait_milliseconds */</span><br><span style="color: hsl(120, 100%, 40%);">+           .min_wait_milliseconds = 2000,</span><br><span style="color: hsl(120, 100%, 40%);">+                .result_cb = result_cb_once,</span><br><span style="color: hsl(120, 100%, 40%);">+  };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_mslookup_query q1 = {</span><br><span style="color: hsl(120, 100%, 40%);">+             .service = SERVICE_HLR_GSUP,</span><br><span style="color: hsl(120, 100%, 40%);">+          .id = {</span><br><span style="color: hsl(120, 100%, 40%);">+                       .type = OSMO_MSLOOKUP_ID_IMSI,</span><br><span style="color: hsl(120, 100%, 40%);">+                        .imsi = "1234567",</span><br><span style="color: hsl(120, 100%, 40%);">+          },</span><br><span style="color: hsl(120, 100%, 40%);">+    };</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(osmo_mslookup_client_request(client, &q1, &handling));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_mslookup_query q2 = {</span><br><span style="color: hsl(120, 100%, 40%);">+             .service = SERVICE_SIP,</span><br><span style="color: hsl(120, 100%, 40%);">+               .id = {</span><br><span style="color: hsl(120, 100%, 40%);">+                       .type = OSMO_MSLOOKUP_ID_MSISDN,</span><br><span style="color: hsl(120, 100%, 40%);">+                      .msisdn = "112",</span><br><span style="color: hsl(120, 100%, 40%);">+            },</span><br><span style="color: hsl(120, 100%, 40%);">+    };</span><br><span style="color: hsl(120, 100%, 40%);">+    handling.min_wait_milliseconds = 3000;</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(osmo_mslookup_client_request(client, &q2, &handling));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_mslookup_query q3 = {</span><br><span style="color: hsl(120, 100%, 40%);">+             .service = "smpp.sms",</span><br><span style="color: hsl(120, 100%, 40%);">+              .id = {</span><br><span style="color: hsl(120, 100%, 40%);">+                       .type = OSMO_MSLOOKUP_ID_MSISDN,</span><br><span style="color: hsl(120, 100%, 40%);">+                      .msisdn = "00000",</span><br><span style="color: hsl(120, 100%, 40%);">+          },</span><br><span style="color: hsl(120, 100%, 40%);">+    };</span><br><span style="color: hsl(120, 100%, 40%);">+    handling.min_wait_milliseconds = 5000;</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(osmo_mslookup_client_request(client, &q3, &handling));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_mslookup_query q4 = {</span><br><span style="color: hsl(120, 100%, 40%);">+             .service = SERVICE_HLR_GSUP,</span><br><span style="color: hsl(120, 100%, 40%);">+          .id = {</span><br><span style="color: hsl(120, 100%, 40%);">+                       .type = OSMO_MSLOOKUP_ID_MSISDN,</span><br><span style="color: hsl(120, 100%, 40%);">+                      .msisdn = "666",</span><br><span style="color: hsl(120, 100%, 40%);">+            },</span><br><span style="color: hsl(120, 100%, 40%);">+    };</span><br><span style="color: hsl(120, 100%, 40%);">+    handling.min_wait_milliseconds = 10000;</span><br><span style="color: hsl(120, 100%, 40%);">+       uint32_t q4_handle;</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT((q4_handle = osmo_mslookup_client_request(client, &q4, &handling)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    while (osmo_gettimeofday_override_time.tv_sec < 6) {</span><br><span style="color: hsl(120, 100%, 40%);">+               log_reset_context();</span><br><span style="color: hsl(120, 100%, 40%);">+          fake_time_passes(0, 1e6 / 5);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_mslookup_client_request_cancel(client, q4_handle);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/tests/mslookup/mslookup_client_test.err b/tests/mslookup/mslookup_client_test.err</span><br><span>new file mode 100644</span><br><span>index 0000000..c552837</span><br><span>--- /dev/null</span><br><span>+++ b/tests/mslookup/mslookup_client_test.err</span><br><span>@@ -0,0 +1,47 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 0.000000 s</span><br><span style="color: hsl(120, 100%, 40%);">+fake_lookup_request(gsup.hlr.1234567.imsi)</span><br><span style="color: hsl(120, 100%, 40%);">+fake_lookup_request(sip.voice.112.msisdn)</span><br><span style="color: hsl(120, 100%, 40%);">+fake_lookup_request(smpp.sms.00000.msisdn)</span><br><span style="color: hsl(120, 100%, 40%);">+fake_lookup_request(gsup.hlr.666.msisdn)</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 0.200000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 0.400000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 0.600000 s</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_mslookup_client_rx_result(): sip.voice.112.msisdn -> ipv4: 66.66.66.66:666 -> ipv6: 6666:6666:6666::6:666 (age=423) (not-last)</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 0.800000 s</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_mslookup_client_rx_result(): sip.voice.112.msisdn -> ipv4: 112.112.112.112:23 (age=235) (not-last)</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 1.000000 s</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_mslookup_client_rx_result(): gsup.hlr.1234567.imsi -> ipv4: 12.34.56.7:42 -> ipv6: [be:ef:ed:ca:fe:fa:ce::1]:42 (age=0) (not-last)</span><br><span style="color: hsl(120, 100%, 40%);">+result_cb(): gsup.hlr.1234567.imsi -> ipv4: 12.34.56.7:42 -> ipv6: [be:ef:ed:ca:fe:fa:ce::1]:42 (age=0) (last)</span><br><span style="color: hsl(120, 100%, 40%);">+fake_lookup_request_cleanup() ok</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 1.200000 s</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_mslookup_client_rx_result(): sip.voice.112.msisdn -> ipv4: 99.99.99.99:999 -> ipv6: 9999:9999:9999::9:999 (age=335) (not-last)</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 1.400000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 1.600000 s</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_mslookup_client_rx_result(): sip.voice.112.msisdn -> ipv4: 99.99.99.99:999 (age=999) (not-last)</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 1.800000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 2.000000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 2.200000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 2.400000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 2.600000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 2.800000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 3.000000 s</span><br><span style="color: hsl(120, 100%, 40%);">+result_cb(): sip.voice.112.msisdn -> ipv4: 112.112.112.112:23 (age=235) (last)</span><br><span style="color: hsl(120, 100%, 40%);">+fake_lookup_request_cleanup() ok</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 3.200000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 3.400000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 3.600000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 3.800000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 4.000000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 4.200000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 4.400000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 4.600000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 4.800000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 5.000000 s</span><br><span style="color: hsl(120, 100%, 40%);">+result_cb(): smpp.sms.00000.msisdn not-found (last)</span><br><span style="color: hsl(120, 100%, 40%);">+fake_lookup_request_cleanup() ok</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 5.200000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 5.400000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 5.600000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 5.800000 s</span><br><span style="color: hsl(120, 100%, 40%);">+Total time passed: 6.000000 s</span><br><span style="color: hsl(120, 100%, 40%);">+fake_lookup_request_cleanup() ok</span><br><span>diff --git a/tests/mslookup/mslookup_test.c b/tests/mslookup/mslookup_test.c</span><br><span>new file mode 100644</span><br><span>index 0000000..1672bd0</span><br><span>--- /dev/null</span><br><span>+++ b/tests/mslookup/mslookup_test.c</span><br><span>@@ -0,0 +1,88 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU Affero General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/application.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/select.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/hlr/logging.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/mslookup/mslookup_client.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void *ctx;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const char *domains[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ "gsup.hlr.123456789012345.imsi",</span><br><span style="color: hsl(120, 100%, 40%);">+    "gsup.hlr.1.imsi",</span><br><span style="color: hsl(120, 100%, 40%);">+  "sip.voice.1.msisdn",</span><br><span style="color: hsl(120, 100%, 40%);">+       "a.b.c.imsi",</span><br><span style="color: hsl(120, 100%, 40%);">+       "",</span><br><span style="color: hsl(120, 100%, 40%);">+ ".",</span><br><span style="color: hsl(120, 100%, 40%);">+        "...",</span><br><span style="color: hsl(120, 100%, 40%);">+      ".....",</span><br><span style="color: hsl(120, 100%, 40%);">+    ".....1.msisdn",</span><br><span style="color: hsl(120, 100%, 40%);">+    "fofdndsf. d.ads ofdsf. ads.kj.1243455132.msisdn",</span><br><span style="color: hsl(120, 100%, 40%);">+  "foo.12345678901234567890.imsi",</span><br><span style="color: hsl(120, 100%, 40%);">+    "gsup.hlr.123456789012345.what",</span><br><span style="color: hsl(120, 100%, 40%);">+    NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+ "blarg",</span><br><span style="color: hsl(120, 100%, 40%);">+    "blarg.",</span><br><span style="color: hsl(120, 100%, 40%);">+   "blarg.1.",</span><br><span style="color: hsl(120, 100%, 40%);">+ "blarg.1.msisdn",</span><br><span style="color: hsl(120, 100%, 40%);">+   "blarg.1.msisdn.",</span><br><span style="color: hsl(120, 100%, 40%);">+  ".1.msisdn",</span><br><span style="color: hsl(120, 100%, 40%);">+        "1.msisdn",</span><br><span style="color: hsl(120, 100%, 40%);">+ "qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmm.1.msisdn",</span><br><span style="color: hsl(120, 100%, 40%);">+ "qwerty.1.qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmm",</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void test_osmo_mslookup_query_init_from_domain_str()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        for (i = 0; i < ARRAY_SIZE(domains); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                const char *d = domains[i];</span><br><span style="color: hsl(120, 100%, 40%);">+           struct osmo_mslookup_query q;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               int rc = osmo_mslookup_query_init_from_domain_str(&q, d);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (rc)</span><br><span style="color: hsl(120, 100%, 40%);">+                       fprintf(stderr, "%s -> rc = %d\n", osmo_quote_str(d, -1), rc);</span><br><span style="color: hsl(120, 100%, 40%);">+           else</span><br><span style="color: hsl(120, 100%, 40%);">+                  fprintf(stderr, "%s -> %s %s %s\n", osmo_quote_str(d, -1),</span><br><span style="color: hsl(120, 100%, 40%);">+                              osmo_quote_str_c(ctx, q.service, -1),</span><br><span style="color: hsl(120, 100%, 40%);">+                         osmo_quote_str_c(ctx, q.id.imsi, -1),</span><br><span style="color: hsl(120, 100%, 40%);">+                         osmo_mslookup_id_type_name(q.id.type));</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int main()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ ctx = talloc_named_const(NULL, 0, "main");</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_init_logging2(ctx, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      log_set_print_filename(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+        log_set_print_level(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+   log_set_print_category(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+        log_set_print_category_hex(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+    log_set_use_color(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+     log_set_category_filter(osmo_stderr_target, DMSLOOKUP, true, LOGL_DEBUG);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   test_osmo_mslookup_query_init_from_domain_str();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    talloc_free(ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/tests/mslookup/mslookup_test.err b/tests/mslookup/mslookup_test.err</span><br><span>new file mode 100644</span><br><span>index 0000000..ee5ff21</span><br><span>--- /dev/null</span><br><span>+++ b/tests/mslookup/mslookup_test.err</span><br><span>@@ -0,0 +1,22 @@</span><br><span style="color: hsl(120, 100%, 40%);">+"gsup.hlr.123456789012345.imsi" -> "gsup.hlr" "123456789012345" imsi</span><br><span style="color: hsl(120, 100%, 40%);">+"gsup.hlr.1.imsi" -> rc = -5</span><br><span style="color: hsl(120, 100%, 40%);">+"sip.voice.1.msisdn" -> "sip.voice" "1" msisdn</span><br><span style="color: hsl(120, 100%, 40%);">+"a.b.c.imsi" -> rc = -5</span><br><span style="color: hsl(120, 100%, 40%);">+"" -> rc = -2</span><br><span style="color: hsl(120, 100%, 40%);">+"." -> rc = -3</span><br><span style="color: hsl(120, 100%, 40%);">+"..." -> rc = -4</span><br><span style="color: hsl(120, 100%, 40%);">+"....." -> rc = -4</span><br><span style="color: hsl(120, 100%, 40%);">+".....1.msisdn" -> "...." "1" msisdn</span><br><span style="color: hsl(120, 100%, 40%);">+"fofdndsf. d.ads ofdsf. ads.kj.1243455132.msisdn" -> "fofdndsf. d.ads ofdsf. ads.kj" "1243455132" msisdn</span><br><span style="color: hsl(120, 100%, 40%);">+"foo.12345678901234567890.imsi" -> rc = -11</span><br><span style="color: hsl(120, 100%, 40%);">+"gsup.hlr.123456789012345.what" -> rc = -7</span><br><span style="color: hsl(120, 100%, 40%);">+NULL -> rc = -1</span><br><span style="color: hsl(120, 100%, 40%);">+"blarg" -> rc = -2</span><br><span style="color: hsl(120, 100%, 40%);">+"blarg." -> rc = -3</span><br><span style="color: hsl(120, 100%, 40%);">+"blarg.1." -> rc = -4</span><br><span style="color: hsl(120, 100%, 40%);">+"blarg.1.msisdn" -> "blarg" "1" msisdn</span><br><span style="color: hsl(120, 100%, 40%);">+"blarg.1.msisdn." -> rc = -4</span><br><span style="color: hsl(120, 100%, 40%);">+".1.msisdn" -> rc = -3</span><br><span style="color: hsl(120, 100%, 40%);">+"1.msisdn" -> rc = -3</span><br><span style="color: hsl(120, 100%, 40%);">+"qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmm.1.msisdn" -> rc = -11</span><br><span style="color: hsl(120, 100%, 40%);">+"qwerty.1.qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmm" -> rc = -7</span><br><span>diff --git a/tests/testsuite.at b/tests/testsuite.at</span><br><span>index 58c197d..39df7aa 100644</span><br><span>--- a/tests/testsuite.at</span><br><span>+++ b/tests/testsuite.at</span><br><span>@@ -39,3 +39,15 @@</span><br><span> cat $abs_srcdir/db_upgrade/db_upgrade_test.err > experr</span><br><span> AT_CHECK([$abs_srcdir/db_upgrade/db_upgrade_test.sh $abs_srcdir/db_upgrade $abs_builddir/db_upgrade], [], [expout], [experr])</span><br><span> AT_CLEANUP</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AT_SETUP([mslookup])</span><br><span style="color: hsl(120, 100%, 40%);">+AT_KEYWORDS([mslookup])</span><br><span style="color: hsl(120, 100%, 40%);">+cat $abs_srcdir/mslookup/mslookup_test.err > experr</span><br><span style="color: hsl(120, 100%, 40%);">+AT_CHECK([$abs_top_builddir/tests/mslookup/mslookup_test], [0], [ignore], [experr])</span><br><span style="color: hsl(120, 100%, 40%);">+AT_CLEANUP</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AT_SETUP([mslookup_client])</span><br><span style="color: hsl(120, 100%, 40%);">+AT_KEYWORDS([mslookup_client])</span><br><span style="color: hsl(120, 100%, 40%);">+cat $abs_srcdir/mslookup/mslookup_client_test.err > experr</span><br><span style="color: hsl(120, 100%, 40%);">+AT_CHECK([$abs_top_builddir/tests/mslookup/mslookup_client_test], [0], [ignore], [experr])</span><br><span style="color: hsl(120, 100%, 40%);">+AT_CLEANUP</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-hlr/+/16202">change 16202</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-hlr/+/16202"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-hlr </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I83487ab8aad1611eb02e997dafbcb8344da13df1 </div>
<div style="display:none"> Gerrit-Change-Number: 16202 </div>
<div style="display:none"> Gerrit-PatchSet: 21 </div>
<div style="display:none"> Gerrit-Owner: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: fixeria <axilirator@gmail.com> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: osmith <osmith@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>