add libosmo-mslookup abstract client

mslookup is a key concept in Distributed GSM, which allows querying the current
location of a subscriber in a number of cooperating but independent core
network sites, by arbitrary service names and by MSISDN/IMSI.

Add the abstract mslookup client library. An actual lookup method (besides
mslookup_client_fake.c) is added in a subsequent patch.

For a detailed overview of this and upcoming patches, please see the elaborate
comment at the top of mslookup.c.

Add as separate library, libosmo-mslookup, to allow adding D-GSM capability to
arbitrary client programs.

osmo-hlr will be the only mslookup server implementation, added in a subsequent
patch.

osmo-hlr itself will also use this library and act as an mslookup client, when
requesting the home HLR for locally unknown IMSIs.

Related: OS#4237
Patch-by: osmith, nhofmeyr
Change-Id: I83487ab8aad1611eb02e997dafbcb8344da13df1
diff --git a/include/osmocom/mslookup/mslookup.h b/include/osmocom/mslookup/mslookup.h
new file mode 100644
index 0000000..e90af33
--- /dev/null
+++ b/include/osmocom/mslookup/mslookup.h
@@ -0,0 +1,121 @@
+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*! \defgroup mslookup Distributed GSM: finding subscribers
+ *  @{
+ * \file mslookup.h
+ */
+
+#pragma once
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/sockaddr_str.h>
+#include <osmocom/gsm/protocol/gsm_23_003.h>
+
+#define OSMO_MSLOOKUP_SERVICE_MAXLEN 64
+
+bool osmo_mslookup_service_valid(const char *service);
+
+enum osmo_mslookup_id_type {
+	OSMO_MSLOOKUP_ID_NONE = 0,
+	OSMO_MSLOOKUP_ID_IMSI,
+	OSMO_MSLOOKUP_ID_MSISDN,
+};
+
+extern const struct value_string osmo_mslookup_id_type_names[];
+static inline const char *osmo_mslookup_id_type_name(enum osmo_mslookup_id_type val)
+{ return get_value_string(osmo_mslookup_id_type_names, val); }
+
+struct osmo_mslookup_id {
+	enum osmo_mslookup_id_type type;
+	union {
+		char imsi[GSM23003_IMSI_MAX_DIGITS+1];
+		char msisdn[GSM23003_MSISDN_MAX_DIGITS+1];
+	};
+};
+
+int osmo_mslookup_id_cmp(const struct osmo_mslookup_id *a, const struct osmo_mslookup_id *b);
+bool osmo_mslookup_id_valid(const struct osmo_mslookup_id *id);
+
+enum osmo_mslookup_result_code {
+	OSMO_MSLOOKUP_RC_NONE = 0,
+	/*! An intermediate valid result. The request is still open for more results. */
+	OSMO_MSLOOKUP_RC_RESULT,
+	/*! Returned when the final request timeout has elapsed without results. */
+	OSMO_MSLOOKUP_RC_NOT_FOUND,
+};
+
+extern const struct value_string osmo_mslookup_result_code_names[];
+static inline const char *osmo_mslookup_result_code_name(enum osmo_mslookup_result_code val)
+{ return get_value_string(osmo_mslookup_result_code_names, val); }
+
+/*! Information to request from a lookup. */
+struct osmo_mslookup_query {
+	/*! Which service to request, by freely invented names. For service name conventions (for voice, SMS, HLR,...),
+	 * refer to the OsmoHLR user's manual http://ftp.osmocom.org/docs/latest/osmohlr-usermanual.pdf */
+	char service[OSMO_MSLOOKUP_SERVICE_MAXLEN + 1];
+	/*! IMSI or MSISDN to look up. */
+	struct osmo_mslookup_id id;
+
+	/*! Caller provided private data, if desired. */
+	void *priv;
+};
+
+/*! Result data as passed back to a lookup client that invoked an osmo_mslookup_client_request. */
+struct osmo_mslookup_result {
+	/*! Outcome of the request. */
+	enum osmo_mslookup_result_code rc;
+
+	/*! IP address and port to reach the given service via IPv4, if any. */
+	struct osmo_sockaddr_str host_v4;
+
+	/*! IP address and port to reach the given service via IPv6, if any. */
+	struct osmo_sockaddr_str host_v6;
+
+	/*! How long ago the service last verified presence of the subscriber, in seconds, or zero if the presence is
+	 * invariable (like the home HLR record for an IMSI).
+	 * If a subscriber has recently moved to a different location, we get multiple replies and want to choose the
+	 * most recent one. If this were a timestamp, firstly the time zones would need to be taken care of.
+	 * Even if we choose UTC, a service provider with an inaccurate date/time would end up affecting the result.
+	 * The least susceptible to configuration errors or difference in local and remote clock is a value that
+	 * indicates the actual age of the record in seconds. The time that the lookup query took to be answered should
+	 * be neglectable here, since we would typically wait one second (or very few seconds) for lookup replies,
+	 * while typical Location Updating periods are in the range of 15 minutes. */
+	uint32_t age;
+
+	/*! Whether this is the last result returned for this request. */
+	bool last;
+};
+
+int osmo_mslookup_query_init_from_domain_str(struct osmo_mslookup_query *q, const char *domain);
+
+size_t osmo_mslookup_id_name_buf(char *buf, size_t buflen, const struct osmo_mslookup_id *id);
+char *osmo_mslookup_id_name_c(void *ctx, const struct osmo_mslookup_id *id);
+char *osmo_mslookup_id_name_b(char *buf, size_t buflen, const struct osmo_mslookup_id *id);
+
+size_t osmo_mslookup_result_to_str_buf(char *buf, size_t buflen,
+				     const struct osmo_mslookup_query *query,
+				     const struct osmo_mslookup_result *result);
+char *osmo_mslookup_result_name_c(void *ctx,
+				  const struct osmo_mslookup_query *query,
+				  const struct osmo_mslookup_result *result);
+char *osmo_mslookup_result_name_b(char *buf, size_t buflen,
+				  const struct osmo_mslookup_query *query,
+				  const struct osmo_mslookup_result *result);
+
+/*! @} */