blob: ef01428d2d6967d9b7b2d7a2128734151c439414 [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
20#include <string.h>
Max00b37152017-02-20 11:09:27 +010021#include <errno.h>
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020022#include <inttypes.h>
Harald Weltee687be52016-05-03 18:49:27 +020023
24#include <osmocom/core/utils.h>
25#include <osmocom/crypt/auth.h>
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020026#include <osmocom/gsm/gsm23003.h>
Harald Weltee687be52016-05-03 18:49:27 +020027
28#include <sqlite3.h>
29
30#include "logging.h"
Neels Hofmeyr00b1d432017-10-17 01:43:48 +020031#include "hlr.h"
Harald Weltee687be52016-05-03 18:49:27 +020032#include "db.h"
Neels Hofmeyr00b1d432017-10-17 01:43:48 +020033#include "gsup_server.h"
34#include "luop.h"
Harald Weltee687be52016-05-03 18:49:27 +020035
Neels Hofmeyr40aa61c2017-10-09 17:56:04 +020036#define LOGHLR(imsi, level, fmt, args ...) LOGP(DAUC, level, "IMSI='%s': " fmt, imsi, ## args)
Harald Weltee687be52016-05-03 18:49:27 +020037
Neels Hofmeyr1e31d182017-10-10 23:20:09 +020038#define SL3_TXT(x, stmt, idx) \
39 do { \
40 const char *_txt = (const char *) sqlite3_column_text(stmt, idx);\
41 if (_txt) \
42 strncpy(x, _txt, sizeof(x)); \
43 x[sizeof(x)-1] = '\0'; \
44 } while (0)
Harald Weltee687be52016-05-03 18:49:27 +020045
Neels Hofmeyr16140f72017-10-25 19:17:18 +020046/*! Add new subscriber record to the HLR database.
47 * \param[in,out] dbc database context.
48 * \param[in] imsi ASCII string of IMSI digits, is validated.
49 * \returns 0 on success, -EINVAL on invalid IMSI, -EIO on database error.
50 */
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020051int db_subscr_create(struct db_context *dbc, const char *imsi)
52{
53 sqlite3_stmt *stmt;
54 int rc;
55
56 if (!osmo_imsi_str_valid(imsi)) {
57 LOGP(DAUC, LOGL_ERROR, "Cannot create subscriber: invalid IMSI: '%s'\n",
58 imsi);
59 return -EINVAL;
60 }
61
62 stmt = dbc->stmt[DB_STMT_SUBSCR_CREATE];
63
64 if (!db_bind_text(stmt, "$imsi", imsi))
65 return -EIO;
66
67 /* execute the statement */
68 rc = sqlite3_step(stmt);
69 db_remove_reset(stmt);
70 if (rc != SQLITE_DONE) {
71 LOGHLR(imsi, LOGL_ERROR, "Cannot create subscriber: SQL error: (%d) %s\n",
72 rc, sqlite3_errmsg(dbc->db));
73 return -EIO;
74 }
75
76 return 0;
77}
78
Neels Hofmeyr16140f72017-10-25 19:17:18 +020079/*! Completely delete a subscriber record from the HLR database.
80 * Also remove authentication data.
81 * Future todo: also drop from all other database tables, which aren't used yet
82 * at the time of writing this.
83 * \param[in,out] dbc database context.
84 * \param[in] subscr_id ID of the subscriber in the HLR db.
85 * \returns if the subscriber was found and removed, -EIO on database error,
86 * -ENOENT if no such subscriber data exists.
87 */
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020088int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id)
89{
90 int rc;
Neels Hofmeyr1332a172017-10-10 02:25:00 +020091 struct sub_auth_data_str aud;
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020092 int ret = 0;
93
94 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_DEL_BY_ID];
95
96 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
97 return -EIO;
98
99 /* execute the statement */
100 rc = sqlite3_step(stmt);
101 if (rc != SQLITE_DONE) {
102 LOGP(DAUC, LOGL_ERROR,
103 "Cannot delete subscriber ID=%"PRId64": SQL error: (%d) %s\n",
104 subscr_id, rc, sqlite3_errmsg(dbc->db));
105 db_remove_reset(stmt);
106 return -EIO;
107 }
108
109 /* verify execution result */
110 rc = sqlite3_changes(dbc->db);
111 if (!rc) {
112 LOGP(DAUC, LOGL_ERROR, "Cannot delete: no such subscriber: ID=%"PRId64"\n",
113 subscr_id);
114 ret = -ENOENT;
115 } else if (rc != 1) {
116 LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%"PRId64
117 ": SQL modified %d rows (expected 1)\n", subscr_id, rc);
118 ret = -EIO;
119 }
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200120 db_remove_reset(stmt);
Neels Hofmeyr1332a172017-10-10 02:25:00 +0200121
122 /* make sure to remove authentication data for this subscriber id, for
123 * both 2G and 3G. */
124
125 aud = (struct sub_auth_data_str){
126 .type = OSMO_AUTH_TYPE_GSM,
127 .algo = OSMO_AUTH_ALG_NONE,
128 };
129 rc = db_subscr_update_aud_by_id(dbc, subscr_id, &aud);
130 if (ret == -ENOENT && !rc)
131 ret = 0;
132
133 aud = (struct sub_auth_data_str){
134 .type = OSMO_AUTH_TYPE_UMTS,
135 .algo = OSMO_AUTH_ALG_NONE,
136 };
137 rc = db_subscr_update_aud_by_id(dbc, subscr_id, &aud);
138 if (ret == -ENOENT && !rc)
139 ret = 0;
140
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200141 return ret;
142}
143
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200144/*! Set a subscriber's MSISDN in the HLR database.
145 * \param[in,out] dbc database context.
146 * \param[in] imsi ASCII string of IMSI digits.
147 * \param[in] msisdn ASCII string of MSISDN digits.
148 * \returns 0 on success, -EINVAL in case of invalid MSISDN string, -EIO on
149 * database failure, -ENOENT if no such subscriber exists.
150 */
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200151int db_subscr_update_msisdn_by_imsi(struct db_context *dbc, const char *imsi,
152 const char *msisdn)
153{
154 int rc;
155 int ret = 0;
156
157 if (!osmo_msisdn_str_valid(msisdn)) {
158 LOGHLR(imsi, LOGL_ERROR,
159 "Cannot update subscriber: invalid MSISDN: '%s'\n",
160 msisdn);
161 return -EINVAL;
162 }
163
164 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SET_MSISDN_BY_IMSI];
165
166 if (!db_bind_text(stmt, "$imsi", imsi))
167 return -EIO;
168 if (!db_bind_text(stmt, "$msisdn", msisdn))
169 return -EIO;
170
171 /* execute the statement */
172 rc = sqlite3_step(stmt);
173 if (rc != SQLITE_DONE) {
174 LOGHLR(imsi, LOGL_ERROR,
175 "Cannot update subscriber's MSISDN: SQL error: (%d) %s\n",
176 rc, sqlite3_errmsg(dbc->db));
177 ret = -EIO;
178 goto out;
179 }
180
181 /* verify execution result */
182 rc = sqlite3_changes(dbc->db);
183 if (!rc) {
184 LOGP(DAUC, LOGL_ERROR, "Cannot update MSISDN: no such subscriber: IMSI='%s'\n",
185 imsi);
186 ret = -ENOENT;
187 goto out;
188 } else if (rc != 1) {
189 LOGHLR(imsi, LOGL_ERROR, "Update MSISDN: SQL modified %d rows (expected 1)\n", rc);
190 ret = -EIO;
191 }
192
193out:
194 db_remove_reset(stmt);
195 return ret;
196
197}
198
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200199/*! Insert or update 2G or 3G authentication tokens in the database.
Neels Hofmeyr1332a172017-10-10 02:25:00 +0200200 * If aud->type is OSMO_AUTH_TYPE_GSM, the auc_2g table entry for the
201 * subscriber will be added or modified; if aud->algo is OSMO_AUTH_ALG_NONE,
202 * however, the auc_2g entry for the subscriber is deleted. If aud->type is
203 * OSMO_AUTH_TYPE_UMTS, the auc_3g table is updated; again, if aud->algo is
204 * OSMO_AUTH_ALG_NONE, the auc_3g entry is deleted.
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200205 * \param[in,out] dbc database context.
206 * \param[in] subscr_id DB ID of the subscriber.
207 * \param[in] aud Pointer to new auth data (in ASCII string form).
208 * \returns 0 on success, -EINVAL for invalid aud, -ENOENT for unknown
209 * subscr_id, -EIO for database errors.
Neels Hofmeyr1332a172017-10-10 02:25:00 +0200210 */
211int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
212 const struct sub_auth_data_str *aud)
213{
214 sqlite3_stmt *stmt_del;
215 sqlite3_stmt *stmt_ins;
216 sqlite3_stmt *stmt;
217 const char *label;
218 int rc;
219 int ret = 0;
220
221 switch (aud->type) {
222 case OSMO_AUTH_TYPE_GSM:
223 label = "auc_2g";
224 stmt_del = dbc->stmt[DB_STMT_AUC_2G_DELETE];
225 stmt_ins = dbc->stmt[DB_STMT_AUC_2G_INSERT];
226
227 switch (aud->algo) {
228 case OSMO_AUTH_ALG_NONE:
229 case OSMO_AUTH_ALG_COMP128v1:
230 case OSMO_AUTH_ALG_COMP128v2:
231 case OSMO_AUTH_ALG_COMP128v3:
232 case OSMO_AUTH_ALG_XOR:
233 break;
234 case OSMO_AUTH_ALG_MILENAGE:
235 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
236 " auth algo not suited for 2G: %s\n",
237 osmo_auth_alg_name(aud->algo));
238 return -EINVAL;
239 default:
240 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
241 " Unknown auth algo: %d\n", aud->algo);
242 return -EINVAL;
243 }
244
245 if (aud->algo == OSMO_AUTH_ALG_NONE)
246 break;
247 if (!osmo_is_hexstr(aud->u.gsm.ki, 32, 32, true)) {
248 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
249 " Invalid KI: '%s'\n", aud->u.gsm.ki);
250 return -EINVAL;
251 }
252 break;
253
254 case OSMO_AUTH_TYPE_UMTS:
255 label = "auc_3g";
256 stmt_del = dbc->stmt[DB_STMT_AUC_3G_DELETE];
257 stmt_ins = dbc->stmt[DB_STMT_AUC_3G_INSERT];
258 switch (aud->algo) {
259 case OSMO_AUTH_ALG_NONE:
260 case OSMO_AUTH_ALG_MILENAGE:
261 break;
262 case OSMO_AUTH_ALG_COMP128v1:
263 case OSMO_AUTH_ALG_COMP128v2:
264 case OSMO_AUTH_ALG_COMP128v3:
265 case OSMO_AUTH_ALG_XOR:
266 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
267 " auth algo not suited for 3G: %s\n",
268 osmo_auth_alg_name(aud->algo));
269 return -EINVAL;
270 default:
271 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
272 " Unknown auth algo: %d\n", aud->algo);
273 return -EINVAL;
274 }
275
276 if (aud->algo == OSMO_AUTH_ALG_NONE)
277 break;
278 if (!osmo_is_hexstr(aud->u.umts.k, 32, 32, true)) {
279 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
280 " Invalid K: '%s'\n", aud->u.umts.k);
281 return -EINVAL;
282 }
283 if (!osmo_is_hexstr(aud->u.umts.opc, 32, 32, true)) {
284 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
285 " Invalid OP/OPC: '%s'\n", aud->u.umts.opc);
286 return -EINVAL;
287 }
288 if (aud->u.umts.ind_bitlen > OSMO_MILENAGE_IND_BITLEN_MAX) {
289 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
290 " Invalid ind_bitlen: %d\n", aud->u.umts.ind_bitlen);
291 return -EINVAL;
292 }
293 break;
294 default:
295 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
296 " unknown auth type: %d\n", aud->type);
297 return -EINVAL;
298 }
299
300 stmt = stmt_del;
301
302 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
303 return -EIO;
304
305 /* execute the statement */
306 rc = sqlite3_step(stmt);
307 if (rc != SQLITE_DONE) {
308 LOGP(DAUC, LOGL_ERROR,
309 "Cannot delete %s row: SQL error: (%d) %s\n",
310 label, rc, sqlite3_errmsg(dbc->db));
311 ret = -EIO;
312 goto out;
313 }
314
315 /* verify execution result */
316 rc = sqlite3_changes(dbc->db);
317 if (!rc)
318 /* Leave "no such entry" logging to the caller -- during
319 * db_subscr_delete_by_id(), we call this to make sure it is
320 * empty, and no entry is not an error then.*/
321 ret = -ENOENT;
322 else if (rc != 1) {
323 LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%"PRId64
324 " from %s: SQL modified %d rows (expected 1)\n",
325 subscr_id, label, rc);
326 ret = -EIO;
327 }
328
329 db_remove_reset(stmt);
330
331 /* Error situation? Return now. */
332 if (ret && ret != -ENOENT)
333 return ret;
334
335 /* Just delete requested? */
336 if (aud->algo == OSMO_AUTH_ALG_NONE)
337 return ret;
338
339 /* Don't return -ENOENT if inserting new data. */
340 ret = 0;
341
342 /* Insert new row */
343 stmt = stmt_ins;
344
345 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
346 return -EIO;
347
348 switch (aud->type) {
349 case OSMO_AUTH_TYPE_GSM:
350 if (!db_bind_int(stmt, "$algo_id_2g", aud->algo))
351 return -EIO;
352 if (!db_bind_text(stmt, "$ki", aud->u.gsm.ki))
353 return -EIO;
354 break;
355 case OSMO_AUTH_TYPE_UMTS:
356 if (!db_bind_int(stmt, "$algo_id_3g", aud->algo))
357 return -EIO;
358 if (!db_bind_text(stmt, "$k", aud->u.umts.k))
359 return -EIO;
360 if (!db_bind_text(stmt, "$op",
361 aud->u.umts.opc_is_op ? aud->u.umts.opc : NULL))
362 return -EIO;
363 if (!db_bind_text(stmt, "$opc",
364 aud->u.umts.opc_is_op ? NULL : aud->u.umts.opc))
365 return -EIO;
366 if (!db_bind_int(stmt, "$ind_bitlen", aud->u.umts.ind_bitlen))
367 return -EIO;
368 break;
369 default:
370 OSMO_ASSERT(false);
371 }
372
373 /* execute the statement */
374 rc = sqlite3_step(stmt);
375 if (rc != SQLITE_DONE) {
376 LOGP(DAUC, LOGL_ERROR,
377 "Cannot insert %s row: SQL error: (%d) %s\n",
378 label, rc, sqlite3_errmsg(dbc->db));
379 ret = -EIO;
380 goto out;
381 }
382
383out:
384 db_remove_reset(stmt);
385 return ret;
386}
387
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200388/* Common code for db_subscr_get_by_*() functions. */
389static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscriber *subscr,
390 const char **err)
Harald Weltee687be52016-05-03 18:49:27 +0200391{
Maxadc66482017-02-20 11:23:20 +0100392 int rc;
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200393 int ret = 0;
Harald Weltee687be52016-05-03 18:49:27 +0200394
395 /* execute the statement */
396 rc = sqlite3_step(stmt);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200397 if (rc == SQLITE_DONE) {
398 ret = -ENOENT;
399 goto out;
400 }
Harald Weltee687be52016-05-03 18:49:27 +0200401 if (rc != SQLITE_ROW) {
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200402 ret = -EIO;
403 goto out;
Maxadc66482017-02-20 11:23:20 +0100404 }
405
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200406 if (!subscr)
407 goto out;
Harald Weltee687be52016-05-03 18:49:27 +0200408
Neels Hofmeyrb6837e32017-10-10 23:20:26 +0200409 *subscr = (struct hlr_subscriber){};
410
Harald Weltee687be52016-05-03 18:49:27 +0200411 /* obtain the various columns */
412 subscr->id = sqlite3_column_int64(stmt, 0);
413 SL3_TXT(subscr->imsi, stmt, 1);
414 SL3_TXT(subscr->msisdn, stmt, 2);
Harald Welte99909272016-05-05 18:24:15 +0200415 /* FIXME: These should all be BLOBs as they might contain NUL */
Harald Weltee687be52016-05-03 18:49:27 +0200416 SL3_TXT(subscr->vlr_number, stmt, 3);
417 SL3_TXT(subscr->sgsn_number, stmt, 4);
418 SL3_TXT(subscr->sgsn_address, stmt, 5);
419 subscr->periodic_lu_timer = sqlite3_column_int(stmt, 6);
420 subscr->periodic_rau_tau_timer = sqlite3_column_int(stmt, 7);
421 subscr->nam_cs = sqlite3_column_int(stmt, 8);
422 subscr->nam_ps = sqlite3_column_int(stmt, 9);
423 subscr->lmsi = sqlite3_column_int(stmt, 10);
424 subscr->ms_purged_cs = sqlite3_column_int(stmt, 11);
425 subscr->ms_purged_ps = sqlite3_column_int(stmt, 12);
426
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200427out:
Max00b37152017-02-20 11:09:27 +0100428 db_remove_reset(stmt);
Harald Weltee687be52016-05-03 18:49:27 +0200429
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200430 switch (ret) {
431 case 0:
432 *err = NULL;
433 break;
434 case -ENOENT:
435 *err = "No such subscriber";
436 break;
437 default:
438 *err = sqlite3_errmsg(dbc->db);
439 break;
440 }
441 return ret;
442}
443
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200444/*! Retrieve subscriber data from the HLR database.
445 * \param[in,out] dbc database context.
446 * \param[in] imsi ASCII string of IMSI digits.
447 * \param[out] subscr place retrieved data in this struct.
448 * \returns 0 on success, -ENOENT if no such subscriber was found, -EIO on
449 * database error.
450 */
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200451int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
452 struct hlr_subscriber *subscr)
453{
454 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_IMSI];
455 const char *err;
456 int rc;
457
458 if (!db_bind_text(stmt, NULL, imsi))
459 return -EIO;
460
461 rc = db_sel(dbc, stmt, subscr, &err);
462 if (rc)
463 LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: IMSI='%s': %s\n",
464 imsi, err);
465 return rc;
466}
467
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200468/*! Retrieve subscriber data from the HLR database.
469 * \param[in,out] dbc database context.
470 * \param[in] msisdn ASCII string of MSISDN digits.
471 * \param[out] subscr place retrieved data in this struct.
472 * \returns 0 on success, -ENOENT if no such subscriber was found, -EIO on
473 * database error.
474 */
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200475int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
476 struct hlr_subscriber *subscr)
477{
478 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_MSISDN];
479 const char *err;
480 int rc;
481
482 if (!db_bind_text(stmt, NULL, msisdn))
483 return -EIO;
484
485 rc = db_sel(dbc, stmt, subscr, &err);
486 if (rc)
487 LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: MSISDN='%s': %s\n",
488 msisdn, err);
489 return rc;
490}
491
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200492/*! Retrieve subscriber data from the HLR database.
493 * \param[in,out] dbc database context.
494 * \param[in] id ID of the subscriber in the HLR db.
495 * \param[out] subscr place retrieved data in this struct.
496 * \returns 0 on success, -ENOENT if no such subscriber was found, -EIO on
497 * database error.
498 */
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200499int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
500 struct hlr_subscriber *subscr)
501{
502 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_ID];
503 const char *err;
504 int rc;
505
506 if (!db_bind_int64(stmt, NULL, id))
507 return -EIO;
508
509 rc = db_sel(dbc, stmt, subscr, &err);
510 if (rc)
511 LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: ID=%"PRId64": %s\n",
512 id, err);
513 return rc;
Harald Weltee687be52016-05-03 18:49:27 +0200514}
515
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200516/*! You should use hlr_subscr_nam() instead; enable or disable PS or CS for a
517 * subscriber without notifying GSUP clients.
518 * \param[in,out] dbc database context.
519 * \param[in] imsi ASCII string of IMSI digits.
520 * \param[in] nam_val True to enable CS/PS, false to disable.
521 * \param[in] is_ps when true, set nam_ps, else set nam_cs.
522 * \returns 0 on success, -ENOENT when the given IMSI does not exist, -EIO on
523 * database errors.
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200524 */
525int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps)
Max3ce36862017-02-20 11:18:04 +0100526{
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200527 sqlite3_stmt *stmt;
Max3ce36862017-02-20 11:18:04 +0100528 int rc;
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200529 int ret = 0;
Max3ce36862017-02-20 11:18:04 +0100530
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200531 stmt = dbc->stmt[is_ps ? DB_STMT_UPD_NAM_PS_BY_IMSI
532 : DB_STMT_UPD_NAM_CS_BY_IMSI];
Max3ce36862017-02-20 11:18:04 +0100533
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200534 if (!db_bind_text(stmt, "$imsi", imsi))
535 return -EIO;
536 if (!db_bind_int(stmt, "$val", nam_val ? 1 : 0))
537 return -EIO;
538
539 /* execute the statement */
540 rc = sqlite3_step(stmt);
Max3ce36862017-02-20 11:18:04 +0100541 if (rc != SQLITE_DONE) {
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200542 LOGHLR(imsi, LOGL_ERROR, "%s %s: SQL error: %s\n",
543 nam_val ? "enable" : "disable",
544 is_ps ? "PS" : "CS",
545 sqlite3_errmsg(dbc->db));
546 ret = -EIO;
547 goto out;
Max3ce36862017-02-20 11:18:04 +0100548 }
549
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200550 /* verify execution result */
551 rc = sqlite3_changes(dbc->db);
552 if (!rc) {
553 LOGP(DAUC, LOGL_ERROR, "Cannot %s %s: no such subscriber: IMSI='%s'\n",
554 nam_val ? "enable" : "disable",
555 is_ps ? "PS" : "CS",
556 imsi);
557 ret = -ENOENT;
558 goto out;
559 } else if (rc != 1) {
560 LOGHLR(imsi, LOGL_ERROR, "%s %s: SQL modified %d rows (expected 1)\n",
561 nam_val ? "enable" : "disable",
562 is_ps ? "PS" : "CS",
Max3ce36862017-02-20 11:18:04 +0100563 rc);
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200564 ret = -EIO;
Max3ce36862017-02-20 11:18:04 +0100565 }
566
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200567out:
Max3ce36862017-02-20 11:18:04 +0100568 db_remove_reset(stmt);
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200569 return ret;
Max3ce36862017-02-20 11:18:04 +0100570}
571
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200572/*! Record a Location Updating in the database.
573 * \param[in,out] dbc database context.
574 * \param[in] subscr_id ID of the subscriber in the HLR db.
575 * \param[in] vlr_or_sgsn_number ASCII string of identifier digits.
576 * \param[in] is_ps when true, set sgsn_number, else set vlr_number.
577 * \returns 0 on success, -ENOENT when the given subscriber does not exist,
578 * -EIO on database errors.
579 */
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200580int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
581 const char *vlr_or_sgsn_number, bool is_ps)
Harald Weltee687be52016-05-03 18:49:27 +0200582{
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200583 sqlite3_stmt *stmt;
Harald Weltee687be52016-05-03 18:49:27 +0200584 int rc, ret = 0;
585
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200586 stmt = dbc->stmt[is_ps ? DB_STMT_UPD_SGSN_BY_ID
587 : DB_STMT_UPD_VLR_BY_ID];
Harald Weltee687be52016-05-03 18:49:27 +0200588
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200589 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
590 return -EIO;
Harald Weltee687be52016-05-03 18:49:27 +0200591
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200592 if (!db_bind_text(stmt, "$number", vlr_or_sgsn_number))
593 return -EIO;
Harald Weltee687be52016-05-03 18:49:27 +0200594
595 /* execute the statement */
596 rc = sqlite3_step(stmt);
597 if (rc != SQLITE_DONE) {
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200598 LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%"PRId64": SQL Error: %s\n",
599 is_ps? "SGSN" : "VLR", subscr_id, sqlite3_errmsg(dbc->db));
600 ret = -EIO;
601 goto out;
Harald Weltee687be52016-05-03 18:49:27 +0200602 }
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200603
604 /* verify execution result */
605 rc = sqlite3_changes(dbc->db);
606 if (!rc) {
607 LOGP(DAUC, LOGL_ERROR, "Cannot update %s number for subscriber ID=%"PRId64
608 ": no such subscriber\n",
609 is_ps? "SGSN" : "VLR", subscr_id);
610 ret = -ENOENT;
611 } else if (rc != 1) {
612 LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%"PRId64
613 ": SQL modified %d rows (expected 1)\n",
614 is_ps? "SGSN" : "VLR", subscr_id, rc);
615 ret = -EIO;
616 }
617
Harald Weltee687be52016-05-03 18:49:27 +0200618out:
Max00b37152017-02-20 11:09:27 +0100619 db_remove_reset(stmt);
Harald Weltee687be52016-05-03 18:49:27 +0200620 return ret;
621}
Harald Welteb18f0e02016-05-05 21:03:03 +0200622
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200623/*! Set the ms_purged_cs or ms_purged_ps values in the database.
624 * \param[in,out] dbc database context.
625 * \param[in] by_imsi ASCII string of IMSI digits.
626 * \param[in] purge_val true to purge, false to un-purge.
627 * \param[in] is_ps when true, set ms_purged_ps, else set ms_purged_cs.
628 * \returns 0 on success, -ENOENT when the given IMSI does not exist, -EIO on
629 * database errors.
630 */
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200631int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
632 bool purge_val, bool is_ps)
Harald Welteb18f0e02016-05-05 21:03:03 +0200633{
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200634 sqlite3_stmt *stmt;
635 int rc, ret = 0;
Harald Welteb18f0e02016-05-05 21:03:03 +0200636
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200637 stmt = dbc->stmt[is_ps ? DB_STMT_UPD_PURGE_PS_BY_IMSI
638 : DB_STMT_UPD_PURGE_CS_BY_IMSI];
Harald Welteb18f0e02016-05-05 21:03:03 +0200639
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200640 if (!db_bind_text(stmt, "$imsi", by_imsi))
641 return -EIO;
642 if (!db_bind_int(stmt, "$val", purge_val ? 1 : 0))
643 return -EIO;
Harald Welteb18f0e02016-05-05 21:03:03 +0200644
645 /* execute the statement */
646 rc = sqlite3_step(stmt);
647 if (rc != SQLITE_DONE) {
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200648 LOGP(DAUC, LOGL_ERROR, "%s %s: SQL error: %s\n",
649 purge_val ? "purge" : "un-purge",
650 is_ps ? "PS" : "CS",
651 sqlite3_errmsg(dbc->db));
652 ret = -EIO;
653 goto out;
Harald Welteb18f0e02016-05-05 21:03:03 +0200654 }
Max00b37152017-02-20 11:09:27 +0100655
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200656 /* verify execution result */
657 rc = sqlite3_changes(dbc->db);
658 if (!rc) {
659 LOGP(DAUC, LOGL_ERROR, "Cannot %s %s: no such subscriber: IMSI='%s'\n",
660 purge_val ? "purge" : "un-purge",
661 is_ps ? "PS" : "CS",
662 by_imsi);
663 ret = -ENOENT;
664 goto out;
665 } else if (rc != 1) {
666 LOGHLR(by_imsi, LOGL_ERROR, "%s %s: SQL modified %d rows (expected 1)\n",
667 purge_val ? "purge" : "un-purge",
668 is_ps ? "PS" : "CS",
669 rc);
670 ret = -EIO;
671 }
672
673out:
Max00b37152017-02-20 11:09:27 +0100674 db_remove_reset(stmt);
Harald Welteb18f0e02016-05-05 21:03:03 +0200675
676 return ret;
677}
Neels Hofmeyr00b1d432017-10-17 01:43:48 +0200678
679/*! Update nam_cs/nam_ps in the db and trigger notifications to GSUP clients.
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200680 * \param[in,out] hlr Global hlr context.
681 * \param[in] subscr Subscriber from a fresh db_subscr_get_by_*() call.
682 * \param[in] nam_val True to enable CS/PS, false to disable.
683 * \param[in] is_ps True to enable/disable PS, false for CS.
Neels Hofmeyr00b1d432017-10-17 01:43:48 +0200684 * \returns 0 on success, ENOEXEC if there is no need to change, a negative
685 * value on error.
686 */
687int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps)
688{
689 int rc;
690 struct lu_operation *luop;
691 struct osmo_gsup_conn *co;
692 bool is_val = is_ps? subscr->nam_ps : subscr->nam_cs;
693
694 if (is_val == nam_val) {
695 LOGHLR(subscr->imsi, LOGL_DEBUG, "Already has the requested value when asked to %s %s\n",
696 nam_val ? "enable" : "disable", is_ps ? "PS" : "CS");
697 return ENOEXEC;
698 }
699
700 rc = db_subscr_nam(hlr->dbc, subscr->imsi, nam_val, is_ps);
701 if (rc)
702 return rc > 0? -rc : rc;
703
704 /* If we're disabling, send a notice out to the GSUP client that is
705 * responsible. Otherwise no need. */
706 if (nam_val)
707 return 0;
708
709 /* FIXME: only send to single SGSN where latest update for IMSI came from */
710 llist_for_each_entry(co, &hlr->gs->clients, list) {
711 luop = lu_op_alloc_conn(co);
712 if (!luop) {
713 LOGHLR(subscr->imsi, LOGL_ERROR,
714 "Cannot notify GSUP client, cannot allocate lu_operation,"
715 " for %s:%u\n",
716 co && co->conn && co->conn->server? co->conn->server->addr : "unset",
717 co && co->conn && co->conn->server? co->conn->server->port : 0);
718 continue;
719 }
720 luop->subscr = *subscr;
721 lu_op_tx_del_subscr_data(luop);
722 lu_op_free(luop);
723 }
724 return 0;
725}