blob: 591908fac39f01b5cda99826d38ca8fd48f422cc [file] [log] [blame]
Harald Weltee687be52016-05-03 18:49:27 +02001/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
2 *
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010020#define _POSIX_C_SOURCE 200809L /* for strptime(3) */
21/* These are needed as well due to the above _POSIX_C_SOURCE definition: */
22#define _DEFAULT_SOURCE /* for struct timezone */
23#define _XOPEN_SOURCE /* for clockid_t */
24
Harald Weltee687be52016-05-03 18:49:27 +020025#include <string.h>
Max00b37152017-02-20 11:09:27 +010026#include <errno.h>
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020027#include <inttypes.h>
Stefan Sperling638ba8c2018-12-04 15:07:29 +010028#include <time.h>
Harald Weltee687be52016-05-03 18:49:27 +020029
30#include <osmocom/core/utils.h>
Neels Hofmeyrad868e22019-11-20 02:36:45 +010031#include <osmocom/core/timer.h>
Harald Weltee687be52016-05-03 18:49:27 +020032#include <osmocom/crypt/auth.h>
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020033#include <osmocom/gsm/gsm23003.h>
Harald Weltee687be52016-05-03 18:49:27 +020034
35#include <sqlite3.h>
36
Neels Hofmeyr2f758032019-11-20 00:37:07 +010037#include <osmocom/hlr/logging.h>
38#include <osmocom/hlr/hlr.h>
39#include <osmocom/hlr/db.h>
Neels Hofmeyrc79bcde2019-12-04 01:04:32 +010040#include <osmocom/gsupclient/cni_peer_id.h>
Harald Weltee687be52016-05-03 18:49:27 +020041
Neels Hofmeyr40aa61c2017-10-09 17:56:04 +020042#define LOGHLR(imsi, level, fmt, args ...) LOGP(DAUC, level, "IMSI='%s': " fmt, imsi, ## args)
Harald Weltee687be52016-05-03 18:49:27 +020043
Neels Hofmeyr16140f72017-10-25 19:17:18 +020044/*! Add new subscriber record to the HLR database.
45 * \param[in,out] dbc database context.
46 * \param[in] imsi ASCII string of IMSI digits, is validated.
Oliver Smithcd2af5e2019-03-06 13:17:39 +010047 * \param[in] flags Bitmask of DB_SUBSCR_FLAG_*.
Neels Hofmeyr16140f72017-10-25 19:17:18 +020048 * \returns 0 on success, -EINVAL on invalid IMSI, -EIO on database error.
49 */
Oliver Smithcd2af5e2019-03-06 13:17:39 +010050int db_subscr_create(struct db_context *dbc, const char *imsi, uint8_t flags)
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020051{
52 sqlite3_stmt *stmt;
53 int rc;
54
55 if (!osmo_imsi_str_valid(imsi)) {
56 LOGP(DAUC, LOGL_ERROR, "Cannot create subscriber: invalid IMSI: '%s'\n",
57 imsi);
58 return -EINVAL;
59 }
60
61 stmt = dbc->stmt[DB_STMT_SUBSCR_CREATE];
62
63 if (!db_bind_text(stmt, "$imsi", imsi))
64 return -EIO;
Oliver Smithcd2af5e2019-03-06 13:17:39 +010065 if (!db_bind_int(stmt, "$nam_cs", (flags & DB_SUBSCR_FLAG_NAM_CS) != 0))
66 return -EIO;
67 if (!db_bind_int(stmt, "$nam_ps", (flags & DB_SUBSCR_FLAG_NAM_PS) != 0))
68 return -EIO;
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020069
70 /* execute the statement */
71 rc = sqlite3_step(stmt);
72 db_remove_reset(stmt);
73 if (rc != SQLITE_DONE) {
74 LOGHLR(imsi, LOGL_ERROR, "Cannot create subscriber: SQL error: (%d) %s\n",
75 rc, sqlite3_errmsg(dbc->db));
76 return -EIO;
77 }
78
79 return 0;
80}
81
Neels Hofmeyr16140f72017-10-25 19:17:18 +020082/*! Completely delete a subscriber record from the HLR database.
83 * Also remove authentication data.
84 * Future todo: also drop from all other database tables, which aren't used yet
85 * at the time of writing this.
86 * \param[in,out] dbc database context.
87 * \param[in] subscr_id ID of the subscriber in the HLR db.
88 * \returns if the subscriber was found and removed, -EIO on database error,
89 * -ENOENT if no such subscriber data exists.
90 */
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020091int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id)
92{
93 int rc;
Neels Hofmeyr1332a172017-10-10 02:25:00 +020094 struct sub_auth_data_str aud;
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020095 int ret = 0;
96
97 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_DEL_BY_ID];
98
99 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
100 return -EIO;
101
102 /* execute the statement */
103 rc = sqlite3_step(stmt);
104 if (rc != SQLITE_DONE) {
105 LOGP(DAUC, LOGL_ERROR,
Stefan Sperling705b61b2018-12-07 12:44:50 +0100106 "Cannot delete subscriber ID=%" PRId64 ": SQL error: (%d) %s\n",
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200107 subscr_id, rc, sqlite3_errmsg(dbc->db));
108 db_remove_reset(stmt);
109 return -EIO;
110 }
111
112 /* verify execution result */
113 rc = sqlite3_changes(dbc->db);
114 if (!rc) {
Stefan Sperling705b61b2018-12-07 12:44:50 +0100115 LOGP(DAUC, LOGL_ERROR, "Cannot delete: no such subscriber: ID=%" PRId64 "\n",
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200116 subscr_id);
117 ret = -ENOENT;
118 } else if (rc != 1) {
Stefan Sperling705b61b2018-12-07 12:44:50 +0100119 LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%" PRId64
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200120 ": SQL modified %d rows (expected 1)\n", subscr_id, rc);
121 ret = -EIO;
122 }
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200123 db_remove_reset(stmt);
Neels Hofmeyr1332a172017-10-10 02:25:00 +0200124
125 /* make sure to remove authentication data for this subscriber id, for
126 * both 2G and 3G. */
127
128 aud = (struct sub_auth_data_str){
129 .type = OSMO_AUTH_TYPE_GSM,
130 .algo = OSMO_AUTH_ALG_NONE,
131 };
132 rc = db_subscr_update_aud_by_id(dbc, subscr_id, &aud);
133 if (ret == -ENOENT && !rc)
134 ret = 0;
135
136 aud = (struct sub_auth_data_str){
137 .type = OSMO_AUTH_TYPE_UMTS,
138 .algo = OSMO_AUTH_ALG_NONE,
139 };
140 rc = db_subscr_update_aud_by_id(dbc, subscr_id, &aud);
141 if (ret == -ENOENT && !rc)
142 ret = 0;
143
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200144 return ret;
145}
146
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200147/*! Set a subscriber's MSISDN in the HLR database.
148 * \param[in,out] dbc database context.
Oliver Smith2dc7d962019-01-15 14:14:51 +0100149 * \param[in] imsi ASCII string of IMSI digits
150 * \param[in] msisdn ASCII string of MSISDN digits, or NULL to remove the MSISDN.
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200151 * \returns 0 on success, -EINVAL in case of invalid MSISDN string, -EIO on
152 * database failure, -ENOENT if no such subscriber exists.
153 */
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200154int db_subscr_update_msisdn_by_imsi(struct db_context *dbc, const char *imsi,
155 const char *msisdn)
156{
157 int rc;
158 int ret = 0;
159
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100160 if (msisdn && !osmo_msisdn_str_valid(msisdn)) {
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200161 LOGHLR(imsi, LOGL_ERROR,
162 "Cannot update subscriber: invalid MSISDN: '%s'\n",
163 msisdn);
164 return -EINVAL;
165 }
166
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100167 sqlite3_stmt *stmt = dbc->stmt[
168 msisdn ? DB_STMT_SET_MSISDN_BY_IMSI : DB_STMT_DELETE_MSISDN_BY_IMSI];
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200169
170 if (!db_bind_text(stmt, "$imsi", imsi))
171 return -EIO;
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100172 if (msisdn) {
173 if (!db_bind_text(stmt, "$msisdn", msisdn))
174 return -EIO;
175 }
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200176
177 /* execute the statement */
178 rc = sqlite3_step(stmt);
179 if (rc != SQLITE_DONE) {
180 LOGHLR(imsi, LOGL_ERROR,
181 "Cannot update subscriber's MSISDN: SQL error: (%d) %s\n",
182 rc, sqlite3_errmsg(dbc->db));
183 ret = -EIO;
184 goto out;
185 }
186
187 /* verify execution result */
188 rc = sqlite3_changes(dbc->db);
189 if (!rc) {
190 LOGP(DAUC, LOGL_ERROR, "Cannot update MSISDN: no such subscriber: IMSI='%s'\n",
191 imsi);
192 ret = -ENOENT;
193 goto out;
194 } else if (rc != 1) {
195 LOGHLR(imsi, LOGL_ERROR, "Update MSISDN: SQL modified %d rows (expected 1)\n", rc);
196 ret = -EIO;
197 }
198
199out:
200 db_remove_reset(stmt);
201 return ret;
202
203}
204
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200205/*! Insert or update 2G or 3G authentication tokens in the database.
Neels Hofmeyr1332a172017-10-10 02:25:00 +0200206 * If aud->type is OSMO_AUTH_TYPE_GSM, the auc_2g table entry for the
207 * subscriber will be added or modified; if aud->algo is OSMO_AUTH_ALG_NONE,
208 * however, the auc_2g entry for the subscriber is deleted. If aud->type is
209 * OSMO_AUTH_TYPE_UMTS, the auc_3g table is updated; again, if aud->algo is
210 * OSMO_AUTH_ALG_NONE, the auc_3g entry is deleted.
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200211 * \param[in,out] dbc database context.
212 * \param[in] subscr_id DB ID of the subscriber.
213 * \param[in] aud Pointer to new auth data (in ASCII string form).
214 * \returns 0 on success, -EINVAL for invalid aud, -ENOENT for unknown
215 * subscr_id, -EIO for database errors.
Neels Hofmeyr1332a172017-10-10 02:25:00 +0200216 */
217int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
218 const struct sub_auth_data_str *aud)
219{
220 sqlite3_stmt *stmt_del;
221 sqlite3_stmt *stmt_ins;
222 sqlite3_stmt *stmt;
223 const char *label;
224 int rc;
225 int ret = 0;
226
227 switch (aud->type) {
228 case OSMO_AUTH_TYPE_GSM:
229 label = "auc_2g";
230 stmt_del = dbc->stmt[DB_STMT_AUC_2G_DELETE];
231 stmt_ins = dbc->stmt[DB_STMT_AUC_2G_INSERT];
232
233 switch (aud->algo) {
234 case OSMO_AUTH_ALG_NONE:
235 case OSMO_AUTH_ALG_COMP128v1:
236 case OSMO_AUTH_ALG_COMP128v2:
237 case OSMO_AUTH_ALG_COMP128v3:
238 case OSMO_AUTH_ALG_XOR:
239 break;
240 case OSMO_AUTH_ALG_MILENAGE:
241 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
242 " auth algo not suited for 2G: %s\n",
243 osmo_auth_alg_name(aud->algo));
244 return -EINVAL;
245 default:
246 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
247 " Unknown auth algo: %d\n", aud->algo);
248 return -EINVAL;
249 }
250
251 if (aud->algo == OSMO_AUTH_ALG_NONE)
252 break;
253 if (!osmo_is_hexstr(aud->u.gsm.ki, 32, 32, true)) {
254 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
255 " Invalid KI: '%s'\n", aud->u.gsm.ki);
256 return -EINVAL;
257 }
258 break;
259
260 case OSMO_AUTH_TYPE_UMTS:
261 label = "auc_3g";
262 stmt_del = dbc->stmt[DB_STMT_AUC_3G_DELETE];
263 stmt_ins = dbc->stmt[DB_STMT_AUC_3G_INSERT];
264 switch (aud->algo) {
265 case OSMO_AUTH_ALG_NONE:
266 case OSMO_AUTH_ALG_MILENAGE:
Harald Welte6e237d32020-12-28 01:01:31 +0100267 case OSMO_AUTH_ALG_XOR:
Neels Hofmeyr1332a172017-10-10 02:25:00 +0200268 break;
269 case OSMO_AUTH_ALG_COMP128v1:
270 case OSMO_AUTH_ALG_COMP128v2:
271 case OSMO_AUTH_ALG_COMP128v3:
Neels Hofmeyr1332a172017-10-10 02:25:00 +0200272 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
273 " auth algo not suited for 3G: %s\n",
274 osmo_auth_alg_name(aud->algo));
275 return -EINVAL;
276 default:
277 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
278 " Unknown auth algo: %d\n", aud->algo);
279 return -EINVAL;
280 }
281
282 if (aud->algo == OSMO_AUTH_ALG_NONE)
283 break;
284 if (!osmo_is_hexstr(aud->u.umts.k, 32, 32, true)) {
285 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
286 " Invalid K: '%s'\n", aud->u.umts.k);
287 return -EINVAL;
288 }
289 if (!osmo_is_hexstr(aud->u.umts.opc, 32, 32, true)) {
290 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
291 " Invalid OP/OPC: '%s'\n", aud->u.umts.opc);
292 return -EINVAL;
293 }
294 if (aud->u.umts.ind_bitlen > OSMO_MILENAGE_IND_BITLEN_MAX) {
295 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
296 " Invalid ind_bitlen: %d\n", aud->u.umts.ind_bitlen);
297 return -EINVAL;
298 }
299 break;
300 default:
301 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
302 " unknown auth type: %d\n", aud->type);
303 return -EINVAL;
304 }
305
306 stmt = stmt_del;
307
308 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
309 return -EIO;
310
311 /* execute the statement */
312 rc = sqlite3_step(stmt);
313 if (rc != SQLITE_DONE) {
314 LOGP(DAUC, LOGL_ERROR,
315 "Cannot delete %s row: SQL error: (%d) %s\n",
316 label, rc, sqlite3_errmsg(dbc->db));
317 ret = -EIO;
318 goto out;
319 }
320
321 /* verify execution result */
322 rc = sqlite3_changes(dbc->db);
323 if (!rc)
324 /* Leave "no such entry" logging to the caller -- during
325 * db_subscr_delete_by_id(), we call this to make sure it is
326 * empty, and no entry is not an error then.*/
327 ret = -ENOENT;
328 else if (rc != 1) {
Stefan Sperling705b61b2018-12-07 12:44:50 +0100329 LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%" PRId64
Neels Hofmeyr1332a172017-10-10 02:25:00 +0200330 " from %s: SQL modified %d rows (expected 1)\n",
331 subscr_id, label, rc);
332 ret = -EIO;
333 }
334
335 db_remove_reset(stmt);
336
337 /* Error situation? Return now. */
338 if (ret && ret != -ENOENT)
339 return ret;
340
341 /* Just delete requested? */
342 if (aud->algo == OSMO_AUTH_ALG_NONE)
343 return ret;
344
345 /* Don't return -ENOENT if inserting new data. */
346 ret = 0;
347
348 /* Insert new row */
349 stmt = stmt_ins;
350
351 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
352 return -EIO;
353
354 switch (aud->type) {
355 case OSMO_AUTH_TYPE_GSM:
356 if (!db_bind_int(stmt, "$algo_id_2g", aud->algo))
357 return -EIO;
358 if (!db_bind_text(stmt, "$ki", aud->u.gsm.ki))
359 return -EIO;
360 break;
361 case OSMO_AUTH_TYPE_UMTS:
362 if (!db_bind_int(stmt, "$algo_id_3g", aud->algo))
363 return -EIO;
364 if (!db_bind_text(stmt, "$k", aud->u.umts.k))
365 return -EIO;
366 if (!db_bind_text(stmt, "$op",
367 aud->u.umts.opc_is_op ? aud->u.umts.opc : NULL))
368 return -EIO;
369 if (!db_bind_text(stmt, "$opc",
370 aud->u.umts.opc_is_op ? NULL : aud->u.umts.opc))
371 return -EIO;
372 if (!db_bind_int(stmt, "$ind_bitlen", aud->u.umts.ind_bitlen))
373 return -EIO;
374 break;
375 default:
376 OSMO_ASSERT(false);
377 }
378
379 /* execute the statement */
380 rc = sqlite3_step(stmt);
381 if (rc != SQLITE_DONE) {
382 LOGP(DAUC, LOGL_ERROR,
383 "Cannot insert %s row: SQL error: (%d) %s\n",
384 label, rc, sqlite3_errmsg(dbc->db));
385 ret = -EIO;
386 goto out;
387 }
388
389out:
390 db_remove_reset(stmt);
391 return ret;
392}
393
Oliver Smith81db3892019-01-09 12:03:51 +0100394/*! Set a subscriber's IMEI in the HLR database.
395 * \param[in,out] dbc database context.
396 * \param[in] imsi ASCII string of IMSI digits
397 * \param[in] imei ASCII string of identifier digits, or NULL to remove the IMEI.
398 * \returns 0 on success, -ENOENT when the given subscriber does not exist,
399 * -EIO on database errors.
400 */
401int db_subscr_update_imei_by_imsi(struct db_context *dbc, const char* imsi, const char *imei)
402{
403 int rc, ret = 0;
404 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_UPD_IMEI_BY_IMSI];
405
406 if (imei && !osmo_imei_str_valid(imei, false)) {
407 LOGP(DAUC, LOGL_ERROR, "Cannot update subscriber IMSI='%s': invalid IMEI: '%s'\n", imsi, imei);
408 return -EINVAL;
409 }
410
411 if (!db_bind_text(stmt, "$imsi", imsi))
412 return -EIO;
413 if (imei && !db_bind_text(stmt, "$imei", imei))
414 return -EIO;
415
416 /* execute the statement */
417 rc = sqlite3_step(stmt);
418 if (rc != SQLITE_DONE) {
419 LOGP(DAUC, LOGL_ERROR, "Update IMEI for subscriber IMSI='%s': SQL Error: %s\n", imsi,
420 sqlite3_errmsg(dbc->db));
421 ret = -EIO;
422 goto out;
423 }
424
425 /* verify execution result */
426 rc = sqlite3_changes(dbc->db);
427 if (!rc) {
428 LOGP(DAUC, LOGL_ERROR, "Cannot update IMEI for subscriber IMSI='%s': no such subscriber\n", imsi);
429 ret = -ENOENT;
430 } else if (rc != 1) {
431 LOGP(DAUC, LOGL_ERROR, "Update IMEI for subscriber IMSI='%s': SQL modified %d rows (expected 1)\n",
432 imsi, rc);
433 ret = -EIO;
434 }
435
436out:
437 db_remove_reset(stmt);
438 return ret;
439}
440
Neels Hofmeyr07e16022019-11-20 02:36:35 +0100441static void parse_last_lu_seen(time_t *dst, const char *last_lu_seen_str, const char *imsi, const char *label)
442{
443 struct tm tm = {0};
444 time_t val;
445 if (!last_lu_seen_str || last_lu_seen_str[0] == '\0')
446 return;
447
448 if (strptime(last_lu_seen_str, DB_LAST_LU_SEEN_FMT, &tm) == NULL) {
449 LOGP(DAUC, LOGL_ERROR, "IMSI-%s: Last LU Seen %s: Cannot parse timestamp '%s'\n",
450 imsi, label, last_lu_seen_str);
451 return;
452 }
453
454 errno = 0;
455 val = mktime(&tm);
456 if (val == -1) {
457 LOGP(DAUC, LOGL_ERROR, "IMSI-%s: Last LU Seen %s: Cannot convert timestamp '%s' to time_t: %s\n",
458 imsi, label, last_lu_seen_str, strerror(errno));
459 val = 0;
460 }
461
462 *dst = val;
463}
464
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200465/* Common code for db_subscr_get_by_*() functions. */
466static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscriber *subscr,
467 const char **err)
Harald Weltee687be52016-05-03 18:49:27 +0200468{
Maxadc66482017-02-20 11:23:20 +0100469 int rc;
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200470 int ret = 0;
Harald Weltee687be52016-05-03 18:49:27 +0200471
472 /* execute the statement */
473 rc = sqlite3_step(stmt);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200474 if (rc == SQLITE_DONE) {
475 ret = -ENOENT;
476 goto out;
477 }
Harald Weltee687be52016-05-03 18:49:27 +0200478 if (rc != SQLITE_ROW) {
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200479 ret = -EIO;
480 goto out;
Maxadc66482017-02-20 11:23:20 +0100481 }
482
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200483 if (!subscr)
484 goto out;
Harald Weltee687be52016-05-03 18:49:27 +0200485
Neels Hofmeyrb6837e32017-10-10 23:20:26 +0200486 *subscr = (struct hlr_subscriber){};
487
Harald Weltee687be52016-05-03 18:49:27 +0200488 /* obtain the various columns */
489 subscr->id = sqlite3_column_int64(stmt, 0);
Neels Hofmeyrdbced932017-10-27 02:57:51 +0200490 copy_sqlite3_text_to_buf(subscr->imsi, stmt, 1);
491 copy_sqlite3_text_to_buf(subscr->msisdn, stmt, 2);
Oliver Smith81db3892019-01-09 12:03:51 +0100492 copy_sqlite3_text_to_buf(subscr->imei, stmt, 3);
Harald Welte99909272016-05-05 18:24:15 +0200493 /* FIXME: These should all be BLOBs as they might contain NUL */
Oliver Smith81db3892019-01-09 12:03:51 +0100494 copy_sqlite3_text_to_buf(subscr->vlr_number, stmt, 4);
495 copy_sqlite3_text_to_buf(subscr->sgsn_number, stmt, 5);
496 copy_sqlite3_text_to_buf(subscr->sgsn_address, stmt, 6);
497 subscr->periodic_lu_timer = sqlite3_column_int(stmt, 7);
498 subscr->periodic_rau_tau_timer = sqlite3_column_int(stmt, 8);
499 subscr->nam_cs = sqlite3_column_int(stmt, 9);
500 subscr->nam_ps = sqlite3_column_int(stmt, 10);
501 subscr->lmsi = sqlite3_column_int(stmt, 11);
502 subscr->ms_purged_cs = sqlite3_column_int(stmt, 12);
503 subscr->ms_purged_ps = sqlite3_column_int(stmt, 13);
Neels Hofmeyr07e16022019-11-20 02:36:35 +0100504 parse_last_lu_seen(&subscr->last_lu_seen, (const char *)sqlite3_column_text(stmt, 14),
505 subscr->imsi, "CS");
506 parse_last_lu_seen(&subscr->last_lu_seen_ps, (const char *)sqlite3_column_text(stmt, 15),
507 subscr->imsi, "PS");
Neels Hofmeyr04c23752019-11-25 03:59:50 +0100508 copy_sqlite3_text_to_ipa_name(&subscr->vlr_via_proxy, stmt, 16);
509 copy_sqlite3_text_to_ipa_name(&subscr->sgsn_via_proxy, stmt, 17);
Harald Weltee687be52016-05-03 18:49:27 +0200510
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200511out:
Max00b37152017-02-20 11:09:27 +0100512 db_remove_reset(stmt);
Harald Weltee687be52016-05-03 18:49:27 +0200513
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200514 switch (ret) {
515 case 0:
516 *err = NULL;
517 break;
518 case -ENOENT:
519 *err = "No such subscriber";
520 break;
521 default:
522 *err = sqlite3_errmsg(dbc->db);
523 break;
524 }
525 return ret;
526}
527
Oliver Smith6b73fd92019-03-06 13:49:05 +0100528/*! Check if a subscriber exists in the HLR database.
529 * \param[in, out] dbc database context.
530 * \param[in] imsi ASCII string of IMSI digits.
531 * \returns 0 if it exists, -ENOENT if it does not exist, -EIO on database error.
532 */
533int db_subscr_exists_by_imsi(struct db_context *dbc, const char *imsi) {
534 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_EXISTS_BY_IMSI];
535 const char *err;
536 int rc;
537
538 if (!db_bind_text(stmt, NULL, imsi))
539 return -EIO;
540
541 rc = sqlite3_step(stmt);
542 db_remove_reset(stmt);
543 if (rc == SQLITE_ROW)
544 return 0; /* exists */
545 if (rc == SQLITE_DONE)
546 return -ENOENT; /* does not exist */
547
548 err = sqlite3_errmsg(dbc->db);
549 LOGP(DAUC, LOGL_ERROR, "Failed to check if subscriber exists by IMSI='%s': %s\n", imsi, err);
550 return rc;
551}
552
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200553/*! Retrieve subscriber data from the HLR database.
554 * \param[in,out] dbc database context.
555 * \param[in] imsi ASCII string of IMSI digits.
556 * \param[out] subscr place retrieved data in this struct.
557 * \returns 0 on success, -ENOENT if no such subscriber was found, -EIO on
558 * database error.
559 */
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200560int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
561 struct hlr_subscriber *subscr)
562{
563 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_IMSI];
564 const char *err;
565 int rc;
566
567 if (!db_bind_text(stmt, NULL, imsi))
568 return -EIO;
569
570 rc = db_sel(dbc, stmt, subscr, &err);
Neels Hofmeyr0d82a872019-11-25 05:37:53 +0100571 if (rc && rc != -ENOENT)
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200572 LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: IMSI='%s': %s\n",
573 imsi, err);
574 return rc;
575}
576
Vadim Yanitskiyc13599d2019-03-30 17:03:42 +0700577/*! Check if a subscriber exists in the HLR database.
578 * \param[in, out] dbc database context.
579 * \param[in] msisdn ASCII string of MSISDN digits.
580 * \returns 0 if it exists, -ENOENT if it does not exist, -EIO on database error.
581 */
582int db_subscr_exists_by_msisdn(struct db_context *dbc, const char *msisdn)
583{
584 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_EXISTS_BY_MSISDN];
585 const char *err;
586 int rc;
587
588 if (!db_bind_text(stmt, NULL, msisdn))
589 return -EIO;
590
591 rc = sqlite3_step(stmt);
592 db_remove_reset(stmt);
593 if (rc == SQLITE_ROW)
594 return 0; /* exists */
595 if (rc == SQLITE_DONE)
596 return -ENOENT; /* does not exist */
597
598 err = sqlite3_errmsg(dbc->db);
599 LOGP(DAUC, LOGL_ERROR, "Failed to check if subscriber exists "
600 "by MSISDN='%s': %s\n", msisdn, err);
601 return rc;
602}
603
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200604/*! Retrieve subscriber data from the HLR database.
605 * \param[in,out] dbc database context.
606 * \param[in] msisdn ASCII string of MSISDN digits.
607 * \param[out] subscr place retrieved data in this struct.
608 * \returns 0 on success, -ENOENT if no such subscriber was found, -EIO on
609 * database error.
610 */
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200611int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
612 struct hlr_subscriber *subscr)
613{
614 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_MSISDN];
615 const char *err;
616 int rc;
617
618 if (!db_bind_text(stmt, NULL, msisdn))
619 return -EIO;
620
621 rc = db_sel(dbc, stmt, subscr, &err);
Neels Hofmeyr0d82a872019-11-25 05:37:53 +0100622 if (rc && rc != -ENOENT)
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200623 LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: MSISDN='%s': %s\n",
624 msisdn, err);
625 return rc;
626}
627
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200628/*! Retrieve subscriber data from the HLR database.
629 * \param[in,out] dbc database context.
Keith89fda302021-01-19 07:01:33 +0100630 * \param[in] filter_type ASCII string of identifier type to search.
631 * \param[in] filter ASCII string to search.
632 * \param[in] get_cb pointer to call back function for data.
633 * \param[in,out] data pointer to pass to callback function.
634 * \param[in,out] count counter for number of matched subscribers.
635 * \param[in,our] err
636 * \returns 0 on success, -ENOENT if no subscriber was found, -EIO on
637 * database error.
638 */
639int db_subscrs_get(struct db_context *dbc, const char *filter_type, const char *filter,
640 void (*get_cb)(struct hlr_subscriber *subscr, void *data), void *data,
641 int *count, const char **err)
642{
643 sqlite3_stmt *stmt;
644 char search[256];
645 int rc;
646 struct hlr_subscriber subscr;
647 bool show_ls = false;
648
649 if (!filter_type) {
650 stmt = dbc->stmt[DB_STMT_SEL_ALL];
Keithca8e6ef2021-05-07 05:59:21 +0200651 } else if (strcmp(filter_type, "imei") == 0) {
652 stmt = dbc->stmt[DB_STMT_SEL_FILTER_IMEI];
Keith89fda302021-01-19 07:01:33 +0100653 } else if (strcmp(filter_type, "imsi") == 0) {
654 stmt = dbc->stmt[DB_STMT_SEL_FILTER_IMSI];
655 } else if (strcmp(filter_type, "msisdn") == 0) {
656 stmt = dbc->stmt[DB_STMT_SEL_FILTER_MSISDN];
657 } else if (strcmp(filter_type, "cs") == 0) {
658 stmt = dbc->stmt[DB_STMT_SEL_FILTER_CS];
659 } else if (strcmp(filter_type, "ps") == 0) {
660 stmt = dbc->stmt[DB_STMT_SEL_FILTER_PS];
661 } else if (strcmp(filter_type, "last_lu_seen") == 0) {
662 show_ls = true;
663 stmt = dbc->stmt[DB_STMT_SEL_ALL_ORDER_LAST_SEEN];
664 } else {
665 return -EIO;
666 }
667
Keithb5a56762021-01-30 13:49:15 -0600668 if (filter_type && filter && strcmp(filter_type, "last_lu_seen") != 0) {
Keith89fda302021-01-19 07:01:33 +0100669 if (strcmp(filter, "on") == 0) {
670 sprintf(search, "%s", "1");
671 } else if (strcmp(filter, "off") == 0) {
672 sprintf(search, "%s", "0");
673 } else {
674 sprintf(search, "%%%s%%", filter);
675 }
676 if (!db_bind_text(stmt, "$search", search)) {
677 *err = sqlite3_errmsg(dbc->db);
678 return -EIO;
679 }
680 }
681
682 rc = sqlite3_step(stmt);
683
684 if (rc == SQLITE_DONE) {
685 db_remove_reset(stmt);
686 *err = "No matching subscriber(s)";
687 return -ENOENT;
688 }
689
690 while (rc == SQLITE_ROW) {
691 subscr = (struct hlr_subscriber){
692 .id = sqlite3_column_int64(stmt, 0),};
693 copy_sqlite3_text_to_buf(subscr.imsi, stmt, 1);
694 copy_sqlite3_text_to_buf(subscr.msisdn, stmt, 2);
695 copy_sqlite3_text_to_buf(subscr.imei, stmt, 3);
696 subscr.nam_cs = sqlite3_column_int(stmt, 9);
697 subscr.nam_ps = sqlite3_column_int(stmt, 10);
698 if (show_ls)
699 parse_last_lu_seen(&subscr.last_lu_seen, (const char *)sqlite3_column_text(stmt, 14),
700 subscr.imsi, "CS");
701 get_cb(&subscr, data);
702 rc = sqlite3_step(stmt);
703 (*count)++;
704 }
705
706 db_remove_reset(stmt);
707 if (rc != SQLITE_DONE) {
708 *err = sqlite3_errmsg(dbc->db);
Keith89fda302021-01-19 07:01:33 +0100709 LOGP(DAUC, LOGL_ERROR, "Cannot read subscribers from db:: %s\n", *err);
710 return rc;
711 }
Keithb5a56762021-01-30 13:49:15 -0600712 *err = NULL;
713 return 0;
Keith89fda302021-01-19 07:01:33 +0100714}
715
716/*! Retrieve subscriber data from the HLR database.
717 * \param[in,out] dbc database context.
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200718 * \param[in] id ID of the subscriber in the HLR db.
719 * \param[out] subscr place retrieved data in this struct.
720 * \returns 0 on success, -ENOENT if no such subscriber was found, -EIO on
721 * database error.
722 */
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200723int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
724 struct hlr_subscriber *subscr)
725{
726 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_ID];
727 const char *err;
728 int rc;
729
730 if (!db_bind_int64(stmt, NULL, id))
731 return -EIO;
732
733 rc = db_sel(dbc, stmt, subscr, &err);
Neels Hofmeyr0d82a872019-11-25 05:37:53 +0100734 if (rc && rc != -ENOENT)
Stefan Sperling705b61b2018-12-07 12:44:50 +0100735 LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: ID=%" PRId64 ": %s\n",
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200736 id, err);
737 return rc;
Harald Weltee687be52016-05-03 18:49:27 +0200738}
739
Oliver Smith81db3892019-01-09 12:03:51 +0100740/*! Retrieve subscriber data from the HLR database.
741 * \param[in,out] dbc database context.
742 * \param[in] imei ASCII string of identifier digits
743 * \param[out] subscr place retrieved data in this struct.
744 * \returns 0 on success, -ENOENT if no such subscriber was found, -EIO on
745 * database error.
746 */
747int db_subscr_get_by_imei(struct db_context *dbc, const char *imei, struct hlr_subscriber *subscr)
748{
749 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_IMEI];
750 const char *err;
751 int rc;
752
753 if (!db_bind_text(stmt, NULL, imei))
754 return -EIO;
755
756 rc = db_sel(dbc, stmt, subscr, &err);
Neels Hofmeyr0d82a872019-11-25 05:37:53 +0100757 if (rc && rc != -ENOENT)
Oliver Smith81db3892019-01-09 12:03:51 +0100758 LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: IMEI=%s: %s\n", imei, err);
759 return rc;
760}
761
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200762/*! You should use hlr_subscr_nam() instead; enable or disable PS or CS for a
763 * subscriber without notifying GSUP clients.
764 * \param[in,out] dbc database context.
765 * \param[in] imsi ASCII string of IMSI digits.
766 * \param[in] nam_val True to enable CS/PS, false to disable.
767 * \param[in] is_ps when true, set nam_ps, else set nam_cs.
768 * \returns 0 on success, -ENOENT when the given IMSI does not exist, -EIO on
769 * database errors.
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200770 */
771int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps)
Max3ce36862017-02-20 11:18:04 +0100772{
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200773 sqlite3_stmt *stmt;
Max3ce36862017-02-20 11:18:04 +0100774 int rc;
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200775 int ret = 0;
Max3ce36862017-02-20 11:18:04 +0100776
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200777 stmt = dbc->stmt[is_ps ? DB_STMT_UPD_NAM_PS_BY_IMSI
778 : DB_STMT_UPD_NAM_CS_BY_IMSI];
Max3ce36862017-02-20 11:18:04 +0100779
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200780 if (!db_bind_text(stmt, "$imsi", imsi))
781 return -EIO;
782 if (!db_bind_int(stmt, "$val", nam_val ? 1 : 0))
783 return -EIO;
784
785 /* execute the statement */
786 rc = sqlite3_step(stmt);
Max3ce36862017-02-20 11:18:04 +0100787 if (rc != SQLITE_DONE) {
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200788 LOGHLR(imsi, LOGL_ERROR, "%s %s: SQL error: %s\n",
789 nam_val ? "enable" : "disable",
790 is_ps ? "PS" : "CS",
791 sqlite3_errmsg(dbc->db));
792 ret = -EIO;
793 goto out;
Max3ce36862017-02-20 11:18:04 +0100794 }
795
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200796 /* verify execution result */
797 rc = sqlite3_changes(dbc->db);
798 if (!rc) {
799 LOGP(DAUC, LOGL_ERROR, "Cannot %s %s: no such subscriber: IMSI='%s'\n",
800 nam_val ? "enable" : "disable",
801 is_ps ? "PS" : "CS",
802 imsi);
803 ret = -ENOENT;
804 goto out;
805 } else if (rc != 1) {
806 LOGHLR(imsi, LOGL_ERROR, "%s %s: SQL modified %d rows (expected 1)\n",
807 nam_val ? "enable" : "disable",
808 is_ps ? "PS" : "CS",
Max3ce36862017-02-20 11:18:04 +0100809 rc);
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200810 ret = -EIO;
Max3ce36862017-02-20 11:18:04 +0100811 }
812
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200813out:
Max3ce36862017-02-20 11:18:04 +0100814 db_remove_reset(stmt);
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200815 return ret;
Max3ce36862017-02-20 11:18:04 +0100816}
817
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200818/*! Record a Location Updating in the database.
819 * \param[in,out] dbc database context.
820 * \param[in] subscr_id ID of the subscriber in the HLR db.
821 * \param[in] vlr_or_sgsn_number ASCII string of identifier digits.
822 * \param[in] is_ps when true, set sgsn_number, else set vlr_number.
823 * \returns 0 on success, -ENOENT when the given subscriber does not exist,
824 * -EIO on database errors.
825 */
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200826int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
Neels Hofmeyrad868e22019-11-20 02:36:45 +0100827 const struct osmo_ipa_name *vlr_name, bool is_ps,
828 const struct osmo_ipa_name *via_proxy)
Harald Weltee687be52016-05-03 18:49:27 +0200829{
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200830 sqlite3_stmt *stmt;
Harald Weltee687be52016-05-03 18:49:27 +0200831 int rc, ret = 0;
Stefan Sperling638ba8c2018-12-04 15:07:29 +0100832 struct timespec localtime;
Harald Weltee687be52016-05-03 18:49:27 +0200833
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200834 stmt = dbc->stmt[is_ps ? DB_STMT_UPD_SGSN_BY_ID
835 : DB_STMT_UPD_VLR_BY_ID];
Harald Weltee687be52016-05-03 18:49:27 +0200836
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200837 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
838 return -EIO;
Harald Weltee687be52016-05-03 18:49:27 +0200839
Neels Hofmeyrad868e22019-11-20 02:36:45 +0100840 if (!db_bind_text(stmt, "$number", (char*)vlr_name->val))
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200841 return -EIO;
Harald Weltee687be52016-05-03 18:49:27 +0200842
Neels Hofmeyr04c23752019-11-25 03:59:50 +0100843 if (via_proxy && via_proxy->len) {
844 if (!db_bind_text(stmt, "$proxy", (char*)via_proxy->val))
845 return -EIO;
846 } else {
847 if (!db_bind_null(stmt, "$proxy"))
848 return -EIO;
849 }
850
Harald Weltee687be52016-05-03 18:49:27 +0200851 /* execute the statement */
852 rc = sqlite3_step(stmt);
853 if (rc != SQLITE_DONE) {
Stefan Sperling705b61b2018-12-07 12:44:50 +0100854 LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%" PRId64 ": SQL Error: %s\n",
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200855 is_ps? "SGSN" : "VLR", subscr_id, sqlite3_errmsg(dbc->db));
856 ret = -EIO;
857 goto out;
Harald Weltee687be52016-05-03 18:49:27 +0200858 }
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200859
860 /* verify execution result */
861 rc = sqlite3_changes(dbc->db);
862 if (!rc) {
Stefan Sperling705b61b2018-12-07 12:44:50 +0100863 LOGP(DAUC, LOGL_ERROR, "Cannot update %s number for subscriber ID=%" PRId64
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200864 ": no such subscriber\n",
865 is_ps? "SGSN" : "VLR", subscr_id);
866 ret = -ENOENT;
Stefan Sperling638ba8c2018-12-04 15:07:29 +0100867 goto out;
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200868 } else if (rc != 1) {
Stefan Sperling705b61b2018-12-07 12:44:50 +0100869 LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%" PRId64
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200870 ": SQL modified %d rows (expected 1)\n",
871 is_ps? "SGSN" : "VLR", subscr_id, rc);
872 ret = -EIO;
Stefan Sperling638ba8c2018-12-04 15:07:29 +0100873 goto out;
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200874 }
875
Stefan Sperling638ba8c2018-12-04 15:07:29 +0100876 db_remove_reset(stmt);
877
878 if (osmo_clock_gettime(CLOCK_REALTIME, &localtime) != 0) {
879 LOGP(DAUC, LOGL_ERROR, "Cannot get the current time: (%d) %s\n", errno, strerror(errno));
880 ret = -errno;
881 goto out;
882 }
883
Neels Hofmeyr07e16022019-11-20 02:36:35 +0100884 stmt = dbc->stmt[is_ps? DB_STMT_SET_LAST_LU_SEEN_PS : DB_STMT_SET_LAST_LU_SEEN];
Stefan Sperling638ba8c2018-12-04 15:07:29 +0100885
886 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
887 return -EIO;
888 /* The timestamp will be converted to UTC by SQLite. */
889 if (!db_bind_int64(stmt, "$val", (int64_t)localtime.tv_sec)) {
890 ret = -EIO;
891 goto out;
892 }
893
894 rc = sqlite3_step(stmt);
895 if (rc != SQLITE_DONE) {
896 LOGP(DAUC, LOGL_ERROR,
Stefan Sperling705b61b2018-12-07 12:44:50 +0100897 "Cannot update LU timestamp for subscriber ID=%" PRId64 ": SQL error: (%d) %s\n",
Stefan Sperling638ba8c2018-12-04 15:07:29 +0100898 subscr_id, rc, sqlite3_errmsg(dbc->db));
899 ret = -EIO;
900 goto out;
901 }
902
903 /* verify execution result */
904 rc = sqlite3_changes(dbc->db);
905 if (!rc) {
Stefan Sperling705b61b2018-12-07 12:44:50 +0100906 LOGP(DAUC, LOGL_ERROR, "Cannot update LU timestamp for subscriber ID=%" PRId64
Stefan Sperling638ba8c2018-12-04 15:07:29 +0100907 ": no such subscriber\n", subscr_id);
908 ret = -ENOENT;
909 goto out;
910 } else if (rc != 1) {
Stefan Sperling705b61b2018-12-07 12:44:50 +0100911 LOGP(DAUC, LOGL_ERROR, "Update LU timestamp for subscriber ID=%" PRId64
Stefan Sperling638ba8c2018-12-04 15:07:29 +0100912 ": SQL modified %d rows (expected 1)\n", subscr_id, rc);
913 ret = -EIO;
914 }
Harald Weltee687be52016-05-03 18:49:27 +0200915out:
Max00b37152017-02-20 11:09:27 +0100916 db_remove_reset(stmt);
Harald Weltee687be52016-05-03 18:49:27 +0200917 return ret;
918}
Harald Welteb18f0e02016-05-05 21:03:03 +0200919
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200920/*! Set the ms_purged_cs or ms_purged_ps values in the database.
921 * \param[in,out] dbc database context.
922 * \param[in] by_imsi ASCII string of IMSI digits.
923 * \param[in] purge_val true to purge, false to un-purge.
924 * \param[in] is_ps when true, set ms_purged_ps, else set ms_purged_cs.
925 * \returns 0 on success, -ENOENT when the given IMSI does not exist, -EIO on
926 * database errors.
927 */
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200928int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
929 bool purge_val, bool is_ps)
Harald Welteb18f0e02016-05-05 21:03:03 +0200930{
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200931 sqlite3_stmt *stmt;
932 int rc, ret = 0;
Harald Welteb18f0e02016-05-05 21:03:03 +0200933
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200934 stmt = dbc->stmt[is_ps ? DB_STMT_UPD_PURGE_PS_BY_IMSI
935 : DB_STMT_UPD_PURGE_CS_BY_IMSI];
Harald Welteb18f0e02016-05-05 21:03:03 +0200936
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200937 if (!db_bind_text(stmt, "$imsi", by_imsi))
938 return -EIO;
939 if (!db_bind_int(stmt, "$val", purge_val ? 1 : 0))
940 return -EIO;
Harald Welteb18f0e02016-05-05 21:03:03 +0200941
942 /* execute the statement */
943 rc = sqlite3_step(stmt);
944 if (rc != SQLITE_DONE) {
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200945 LOGP(DAUC, LOGL_ERROR, "%s %s: SQL error: %s\n",
946 purge_val ? "purge" : "un-purge",
947 is_ps ? "PS" : "CS",
948 sqlite3_errmsg(dbc->db));
949 ret = -EIO;
950 goto out;
Harald Welteb18f0e02016-05-05 21:03:03 +0200951 }
Max00b37152017-02-20 11:09:27 +0100952
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200953 /* verify execution result */
954 rc = sqlite3_changes(dbc->db);
955 if (!rc) {
956 LOGP(DAUC, LOGL_ERROR, "Cannot %s %s: no such subscriber: IMSI='%s'\n",
957 purge_val ? "purge" : "un-purge",
958 is_ps ? "PS" : "CS",
959 by_imsi);
960 ret = -ENOENT;
961 goto out;
962 } else if (rc != 1) {
963 LOGHLR(by_imsi, LOGL_ERROR, "%s %s: SQL modified %d rows (expected 1)\n",
964 purge_val ? "purge" : "un-purge",
965 is_ps ? "PS" : "CS",
966 rc);
967 ret = -EIO;
968 }
969
970out:
Max00b37152017-02-20 11:09:27 +0100971 db_remove_reset(stmt);
Harald Welteb18f0e02016-05-05 21:03:03 +0200972
973 return ret;
974}
Neels Hofmeyr3f9d1972019-12-12 04:04:53 +0100975
976static int _db_ind_run(struct db_context *dbc, sqlite3_stmt *stmt, const char *vlr, bool reset)
977{
978 int rc;
979
980 if (!db_bind_text(stmt, "$vlr", vlr))
981 return -EIO;
982
983 /* execute the statement */
984 rc = sqlite3_step(stmt);
985 if (reset)
986 db_remove_reset(stmt);
987 return rc;
988}
989
990static int _db_ind_add(struct db_context *dbc, const char *vlr)
991{
992 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_IND_ADD];
993 if (_db_ind_run(dbc, stmt, vlr, true) != SQLITE_DONE) {
994 LOGP(DDB, LOGL_ERROR, "Cannot create IND entry for %s\n", osmo_quote_str_c(OTC_SELECT, vlr, -1));
995 return -EIO;
996 }
997 return 0;
998}
999
1000static int _db_ind_del(struct db_context *dbc, const char *vlr)
1001{
1002 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_IND_DEL];
1003 _db_ind_run(dbc, stmt, vlr, true);
1004 /* We don't really care about the result. If it didn't exist, then that was the goal anyway. */
1005 return 0;
1006}
1007
1008static int _db_ind_get(struct db_context *dbc, const char *vlr, unsigned int *ind)
1009{
1010 int ret = 0;
1011 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_IND_SELECT];
1012 int rc = _db_ind_run(dbc, stmt, vlr, false);
1013 if (rc == SQLITE_DONE) {
1014 /* Does not exist yet */
1015 ret = -ENOENT;
1016 goto out;
1017 } else if (rc != SQLITE_ROW) {
1018 LOGP(DDB, LOGL_ERROR, "Error executing SQL: %d\n", rc);
1019 ret = -EIO;
1020 goto out;
1021 }
1022
1023 OSMO_ASSERT(ind);
1024 *ind = sqlite3_column_int64(stmt, 0);
1025out:
1026 db_remove_reset(stmt);
1027 return ret;
1028}
1029
1030int _db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr,
1031 unsigned int *ind, bool del)
1032{
1033 const char *vlr_name = NULL;
1034 int rc;
1035
1036 switch (vlr->type) {
1037 case OSMO_CNI_PEER_ID_IPA_NAME:
1038 if (vlr->ipa_name.len < 2 || vlr->ipa_name.val[vlr->ipa_name.len - 1] != '\0') {
1039 LOGP(DDB, LOGL_ERROR, "Expecting VLR ipa_name to be zero terminated; found %s\n",
1040 osmo_ipa_name_to_str(&vlr->ipa_name));
1041 return -ENOTSUP;
1042 }
1043 vlr_name = (const char*)vlr->ipa_name.val;
1044 break;
1045 default:
1046 LOGP(DDB, LOGL_ERROR, "Unsupported osmo_cni_peer_id type: %s\n",
1047 osmo_cni_peer_id_type_name(vlr->type));
1048 return -ENOTSUP;
1049 }
1050
1051 if (del)
1052 return _db_ind_del(dbc, vlr_name);
1053
1054 rc = _db_ind_get(dbc, vlr_name, ind);
1055 if (!rc)
1056 return 0;
1057
1058 /* Does not exist yet, create. */
1059 rc = _db_ind_add(dbc, vlr_name);
1060 if (rc) {
1061 LOGP(DDB, LOGL_ERROR, "Error creating IND entry for %s\n", osmo_quote_str_c(OTC_SELECT, vlr_name, -1));
1062 return rc;
1063 }
1064
1065 /* To be sure, query again from scratch. */
1066 return _db_ind_get(dbc, vlr_name, ind);
1067}
1068
1069int db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr, unsigned int *ind)
1070{
1071 return _db_ind(dbc, vlr, ind, false);
1072}
1073
1074int db_ind_del(struct db_context *dbc, const struct osmo_cni_peer_id *vlr)
1075{
1076 return _db_ind(dbc, vlr, NULL, true);
1077}