blob: e8db7d25c92b040f1a5addb373425d818348cbdc [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 Hofmeyrf7c3e6e2017-10-09 17:55:16 +020046int db_subscr_create(struct db_context *dbc, const char *imsi)
47{
48 sqlite3_stmt *stmt;
49 int rc;
50
51 if (!osmo_imsi_str_valid(imsi)) {
52 LOGP(DAUC, LOGL_ERROR, "Cannot create subscriber: invalid IMSI: '%s'\n",
53 imsi);
54 return -EINVAL;
55 }
56
57 stmt = dbc->stmt[DB_STMT_SUBSCR_CREATE];
58
59 if (!db_bind_text(stmt, "$imsi", imsi))
60 return -EIO;
61
62 /* execute the statement */
63 rc = sqlite3_step(stmt);
64 db_remove_reset(stmt);
65 if (rc != SQLITE_DONE) {
66 LOGHLR(imsi, LOGL_ERROR, "Cannot create subscriber: SQL error: (%d) %s\n",
67 rc, sqlite3_errmsg(dbc->db));
68 return -EIO;
69 }
70
71 return 0;
72}
73
74int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id)
75{
76 int rc;
Neels Hofmeyr1332a172017-10-10 02:25:00 +020077 struct sub_auth_data_str aud;
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020078 int ret = 0;
79
80 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_DEL_BY_ID];
81
82 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
83 return -EIO;
84
85 /* execute the statement */
86 rc = sqlite3_step(stmt);
87 if (rc != SQLITE_DONE) {
88 LOGP(DAUC, LOGL_ERROR,
89 "Cannot delete subscriber ID=%"PRId64": SQL error: (%d) %s\n",
90 subscr_id, rc, sqlite3_errmsg(dbc->db));
91 db_remove_reset(stmt);
92 return -EIO;
93 }
94
95 /* verify execution result */
96 rc = sqlite3_changes(dbc->db);
97 if (!rc) {
98 LOGP(DAUC, LOGL_ERROR, "Cannot delete: no such subscriber: ID=%"PRId64"\n",
99 subscr_id);
100 ret = -ENOENT;
101 } else if (rc != 1) {
102 LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%"PRId64
103 ": SQL modified %d rows (expected 1)\n", subscr_id, rc);
104 ret = -EIO;
105 }
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200106 db_remove_reset(stmt);
Neels Hofmeyr1332a172017-10-10 02:25:00 +0200107
108 /* make sure to remove authentication data for this subscriber id, for
109 * both 2G and 3G. */
110
111 aud = (struct sub_auth_data_str){
112 .type = OSMO_AUTH_TYPE_GSM,
113 .algo = OSMO_AUTH_ALG_NONE,
114 };
115 rc = db_subscr_update_aud_by_id(dbc, subscr_id, &aud);
116 if (ret == -ENOENT && !rc)
117 ret = 0;
118
119 aud = (struct sub_auth_data_str){
120 .type = OSMO_AUTH_TYPE_UMTS,
121 .algo = OSMO_AUTH_ALG_NONE,
122 };
123 rc = db_subscr_update_aud_by_id(dbc, subscr_id, &aud);
124 if (ret == -ENOENT && !rc)
125 ret = 0;
126
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200127 return ret;
128}
129
130int db_subscr_update_msisdn_by_imsi(struct db_context *dbc, const char *imsi,
131 const char *msisdn)
132{
133 int rc;
134 int ret = 0;
135
136 if (!osmo_msisdn_str_valid(msisdn)) {
137 LOGHLR(imsi, LOGL_ERROR,
138 "Cannot update subscriber: invalid MSISDN: '%s'\n",
139 msisdn);
140 return -EINVAL;
141 }
142
143 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SET_MSISDN_BY_IMSI];
144
145 if (!db_bind_text(stmt, "$imsi", imsi))
146 return -EIO;
147 if (!db_bind_text(stmt, "$msisdn", msisdn))
148 return -EIO;
149
150 /* execute the statement */
151 rc = sqlite3_step(stmt);
152 if (rc != SQLITE_DONE) {
153 LOGHLR(imsi, LOGL_ERROR,
154 "Cannot update subscriber's MSISDN: SQL error: (%d) %s\n",
155 rc, sqlite3_errmsg(dbc->db));
156 ret = -EIO;
157 goto out;
158 }
159
160 /* verify execution result */
161 rc = sqlite3_changes(dbc->db);
162 if (!rc) {
163 LOGP(DAUC, LOGL_ERROR, "Cannot update MSISDN: no such subscriber: IMSI='%s'\n",
164 imsi);
165 ret = -ENOENT;
166 goto out;
167 } else if (rc != 1) {
168 LOGHLR(imsi, LOGL_ERROR, "Update MSISDN: SQL modified %d rows (expected 1)\n", rc);
169 ret = -EIO;
170 }
171
172out:
173 db_remove_reset(stmt);
174 return ret;
175
176}
177
Neels Hofmeyr1332a172017-10-10 02:25:00 +0200178/* Insert or update 2G or 3G authentication tokens in the database.
179 * If aud->type is OSMO_AUTH_TYPE_GSM, the auc_2g table entry for the
180 * subscriber will be added or modified; if aud->algo is OSMO_AUTH_ALG_NONE,
181 * however, the auc_2g entry for the subscriber is deleted. If aud->type is
182 * OSMO_AUTH_TYPE_UMTS, the auc_3g table is updated; again, if aud->algo is
183 * OSMO_AUTH_ALG_NONE, the auc_3g entry is deleted.
184 * Returns 0 if successful, -EINVAL for unknown aud->type, -ENOENT for unknown
185 * subscr_id, -EIO for SQL errors.
186 */
187int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
188 const struct sub_auth_data_str *aud)
189{
190 sqlite3_stmt *stmt_del;
191 sqlite3_stmt *stmt_ins;
192 sqlite3_stmt *stmt;
193 const char *label;
194 int rc;
195 int ret = 0;
196
197 switch (aud->type) {
198 case OSMO_AUTH_TYPE_GSM:
199 label = "auc_2g";
200 stmt_del = dbc->stmt[DB_STMT_AUC_2G_DELETE];
201 stmt_ins = dbc->stmt[DB_STMT_AUC_2G_INSERT];
202
203 switch (aud->algo) {
204 case OSMO_AUTH_ALG_NONE:
205 case OSMO_AUTH_ALG_COMP128v1:
206 case OSMO_AUTH_ALG_COMP128v2:
207 case OSMO_AUTH_ALG_COMP128v3:
208 case OSMO_AUTH_ALG_XOR:
209 break;
210 case OSMO_AUTH_ALG_MILENAGE:
211 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
212 " auth algo not suited for 2G: %s\n",
213 osmo_auth_alg_name(aud->algo));
214 return -EINVAL;
215 default:
216 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
217 " Unknown auth algo: %d\n", aud->algo);
218 return -EINVAL;
219 }
220
221 if (aud->algo == OSMO_AUTH_ALG_NONE)
222 break;
223 if (!osmo_is_hexstr(aud->u.gsm.ki, 32, 32, true)) {
224 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
225 " Invalid KI: '%s'\n", aud->u.gsm.ki);
226 return -EINVAL;
227 }
228 break;
229
230 case OSMO_AUTH_TYPE_UMTS:
231 label = "auc_3g";
232 stmt_del = dbc->stmt[DB_STMT_AUC_3G_DELETE];
233 stmt_ins = dbc->stmt[DB_STMT_AUC_3G_INSERT];
234 switch (aud->algo) {
235 case OSMO_AUTH_ALG_NONE:
236 case OSMO_AUTH_ALG_MILENAGE:
237 break;
238 case OSMO_AUTH_ALG_COMP128v1:
239 case OSMO_AUTH_ALG_COMP128v2:
240 case OSMO_AUTH_ALG_COMP128v3:
241 case OSMO_AUTH_ALG_XOR:
242 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
243 " auth algo not suited for 3G: %s\n",
244 osmo_auth_alg_name(aud->algo));
245 return -EINVAL;
246 default:
247 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
248 " Unknown auth algo: %d\n", aud->algo);
249 return -EINVAL;
250 }
251
252 if (aud->algo == OSMO_AUTH_ALG_NONE)
253 break;
254 if (!osmo_is_hexstr(aud->u.umts.k, 32, 32, true)) {
255 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
256 " Invalid K: '%s'\n", aud->u.umts.k);
257 return -EINVAL;
258 }
259 if (!osmo_is_hexstr(aud->u.umts.opc, 32, 32, true)) {
260 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
261 " Invalid OP/OPC: '%s'\n", aud->u.umts.opc);
262 return -EINVAL;
263 }
264 if (aud->u.umts.ind_bitlen > OSMO_MILENAGE_IND_BITLEN_MAX) {
265 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
266 " Invalid ind_bitlen: %d\n", aud->u.umts.ind_bitlen);
267 return -EINVAL;
268 }
269 break;
270 default:
271 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
272 " unknown auth type: %d\n", aud->type);
273 return -EINVAL;
274 }
275
276 stmt = stmt_del;
277
278 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
279 return -EIO;
280
281 /* execute the statement */
282 rc = sqlite3_step(stmt);
283 if (rc != SQLITE_DONE) {
284 LOGP(DAUC, LOGL_ERROR,
285 "Cannot delete %s row: SQL error: (%d) %s\n",
286 label, rc, sqlite3_errmsg(dbc->db));
287 ret = -EIO;
288 goto out;
289 }
290
291 /* verify execution result */
292 rc = sqlite3_changes(dbc->db);
293 if (!rc)
294 /* Leave "no such entry" logging to the caller -- during
295 * db_subscr_delete_by_id(), we call this to make sure it is
296 * empty, and no entry is not an error then.*/
297 ret = -ENOENT;
298 else if (rc != 1) {
299 LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%"PRId64
300 " from %s: SQL modified %d rows (expected 1)\n",
301 subscr_id, label, rc);
302 ret = -EIO;
303 }
304
305 db_remove_reset(stmt);
306
307 /* Error situation? Return now. */
308 if (ret && ret != -ENOENT)
309 return ret;
310
311 /* Just delete requested? */
312 if (aud->algo == OSMO_AUTH_ALG_NONE)
313 return ret;
314
315 /* Don't return -ENOENT if inserting new data. */
316 ret = 0;
317
318 /* Insert new row */
319 stmt = stmt_ins;
320
321 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
322 return -EIO;
323
324 switch (aud->type) {
325 case OSMO_AUTH_TYPE_GSM:
326 if (!db_bind_int(stmt, "$algo_id_2g", aud->algo))
327 return -EIO;
328 if (!db_bind_text(stmt, "$ki", aud->u.gsm.ki))
329 return -EIO;
330 break;
331 case OSMO_AUTH_TYPE_UMTS:
332 if (!db_bind_int(stmt, "$algo_id_3g", aud->algo))
333 return -EIO;
334 if (!db_bind_text(stmt, "$k", aud->u.umts.k))
335 return -EIO;
336 if (!db_bind_text(stmt, "$op",
337 aud->u.umts.opc_is_op ? aud->u.umts.opc : NULL))
338 return -EIO;
339 if (!db_bind_text(stmt, "$opc",
340 aud->u.umts.opc_is_op ? NULL : aud->u.umts.opc))
341 return -EIO;
342 if (!db_bind_int(stmt, "$ind_bitlen", aud->u.umts.ind_bitlen))
343 return -EIO;
344 break;
345 default:
346 OSMO_ASSERT(false);
347 }
348
349 /* execute the statement */
350 rc = sqlite3_step(stmt);
351 if (rc != SQLITE_DONE) {
352 LOGP(DAUC, LOGL_ERROR,
353 "Cannot insert %s row: SQL error: (%d) %s\n",
354 label, rc, sqlite3_errmsg(dbc->db));
355 ret = -EIO;
356 goto out;
357 }
358
359out:
360 db_remove_reset(stmt);
361 return ret;
362}
363
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200364/* Common code for db_subscr_get_by_*() functions. */
365static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscriber *subscr,
366 const char **err)
Harald Weltee687be52016-05-03 18:49:27 +0200367{
Maxadc66482017-02-20 11:23:20 +0100368 int rc;
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200369 int ret = 0;
Harald Weltee687be52016-05-03 18:49:27 +0200370
371 /* execute the statement */
372 rc = sqlite3_step(stmt);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200373 if (rc == SQLITE_DONE) {
374 ret = -ENOENT;
375 goto out;
376 }
Harald Weltee687be52016-05-03 18:49:27 +0200377 if (rc != SQLITE_ROW) {
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200378 ret = -EIO;
379 goto out;
Maxadc66482017-02-20 11:23:20 +0100380 }
381
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200382 if (!subscr)
383 goto out;
Harald Weltee687be52016-05-03 18:49:27 +0200384
Neels Hofmeyrb6837e32017-10-10 23:20:26 +0200385 *subscr = (struct hlr_subscriber){};
386
Harald Weltee687be52016-05-03 18:49:27 +0200387 /* obtain the various columns */
388 subscr->id = sqlite3_column_int64(stmt, 0);
389 SL3_TXT(subscr->imsi, stmt, 1);
390 SL3_TXT(subscr->msisdn, stmt, 2);
Harald Welte99909272016-05-05 18:24:15 +0200391 /* FIXME: These should all be BLOBs as they might contain NUL */
Harald Weltee687be52016-05-03 18:49:27 +0200392 SL3_TXT(subscr->vlr_number, stmt, 3);
393 SL3_TXT(subscr->sgsn_number, stmt, 4);
394 SL3_TXT(subscr->sgsn_address, stmt, 5);
395 subscr->periodic_lu_timer = sqlite3_column_int(stmt, 6);
396 subscr->periodic_rau_tau_timer = sqlite3_column_int(stmt, 7);
397 subscr->nam_cs = sqlite3_column_int(stmt, 8);
398 subscr->nam_ps = sqlite3_column_int(stmt, 9);
399 subscr->lmsi = sqlite3_column_int(stmt, 10);
400 subscr->ms_purged_cs = sqlite3_column_int(stmt, 11);
401 subscr->ms_purged_ps = sqlite3_column_int(stmt, 12);
402
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200403out:
Max00b37152017-02-20 11:09:27 +0100404 db_remove_reset(stmt);
Harald Weltee687be52016-05-03 18:49:27 +0200405
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200406 switch (ret) {
407 case 0:
408 *err = NULL;
409 break;
410 case -ENOENT:
411 *err = "No such subscriber";
412 break;
413 default:
414 *err = sqlite3_errmsg(dbc->db);
415 break;
416 }
417 return ret;
418}
419
420int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
421 struct hlr_subscriber *subscr)
422{
423 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_IMSI];
424 const char *err;
425 int rc;
426
427 if (!db_bind_text(stmt, NULL, imsi))
428 return -EIO;
429
430 rc = db_sel(dbc, stmt, subscr, &err);
431 if (rc)
432 LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: IMSI='%s': %s\n",
433 imsi, err);
434 return rc;
435}
436
437int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
438 struct hlr_subscriber *subscr)
439{
440 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_MSISDN];
441 const char *err;
442 int rc;
443
444 if (!db_bind_text(stmt, NULL, msisdn))
445 return -EIO;
446
447 rc = db_sel(dbc, stmt, subscr, &err);
448 if (rc)
449 LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: MSISDN='%s': %s\n",
450 msisdn, err);
451 return rc;
452}
453
454int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
455 struct hlr_subscriber *subscr)
456{
457 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_ID];
458 const char *err;
459 int rc;
460
461 if (!db_bind_int64(stmt, NULL, id))
462 return -EIO;
463
464 rc = db_sel(dbc, stmt, subscr, &err);
465 if (rc)
466 LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: ID=%"PRId64": %s\n",
467 id, err);
468 return rc;
Harald Weltee687be52016-05-03 18:49:27 +0200469}
470
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200471/* Enable or disable PS or CS for a subscriber.
472 * For the subscriber with the given imsi, set nam_ps (when is_ps == true) or
473 * nam_cs (when is_ps == false) to nam_val in the database.
474 * Returns 0 on success, -ENOENT when the given IMSI does not exist, -EINVAL if
475 * the SQL statement could not be composed, -ENOEXEC if running the SQL
476 * statement failed, -EIO if the amount of rows modified is unexpected.
477 */
478int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps)
Max3ce36862017-02-20 11:18:04 +0100479{
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200480 sqlite3_stmt *stmt;
Max3ce36862017-02-20 11:18:04 +0100481 int rc;
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200482 int ret = 0;
Max3ce36862017-02-20 11:18:04 +0100483
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200484 stmt = dbc->stmt[is_ps ? DB_STMT_UPD_NAM_PS_BY_IMSI
485 : DB_STMT_UPD_NAM_CS_BY_IMSI];
Max3ce36862017-02-20 11:18:04 +0100486
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200487 if (!db_bind_text(stmt, "$imsi", imsi))
488 return -EIO;
489 if (!db_bind_int(stmt, "$val", nam_val ? 1 : 0))
490 return -EIO;
491
492 /* execute the statement */
493 rc = sqlite3_step(stmt);
Max3ce36862017-02-20 11:18:04 +0100494 if (rc != SQLITE_DONE) {
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200495 LOGHLR(imsi, LOGL_ERROR, "%s %s: SQL error: %s\n",
496 nam_val ? "enable" : "disable",
497 is_ps ? "PS" : "CS",
498 sqlite3_errmsg(dbc->db));
499 ret = -EIO;
500 goto out;
Max3ce36862017-02-20 11:18:04 +0100501 }
502
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200503 /* verify execution result */
504 rc = sqlite3_changes(dbc->db);
505 if (!rc) {
506 LOGP(DAUC, LOGL_ERROR, "Cannot %s %s: no such subscriber: IMSI='%s'\n",
507 nam_val ? "enable" : "disable",
508 is_ps ? "PS" : "CS",
509 imsi);
510 ret = -ENOENT;
511 goto out;
512 } else if (rc != 1) {
513 LOGHLR(imsi, LOGL_ERROR, "%s %s: SQL modified %d rows (expected 1)\n",
514 nam_val ? "enable" : "disable",
515 is_ps ? "PS" : "CS",
Max3ce36862017-02-20 11:18:04 +0100516 rc);
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200517 ret = -EIO;
Max3ce36862017-02-20 11:18:04 +0100518 }
519
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200520out:
Max3ce36862017-02-20 11:18:04 +0100521 db_remove_reset(stmt);
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200522 return ret;
Max3ce36862017-02-20 11:18:04 +0100523}
524
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200525int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
526 const char *vlr_or_sgsn_number, bool is_ps)
Harald Weltee687be52016-05-03 18:49:27 +0200527{
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200528 sqlite3_stmt *stmt;
Harald Weltee687be52016-05-03 18:49:27 +0200529 int rc, ret = 0;
530
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200531 stmt = dbc->stmt[is_ps ? DB_STMT_UPD_SGSN_BY_ID
532 : DB_STMT_UPD_VLR_BY_ID];
Harald Weltee687be52016-05-03 18:49:27 +0200533
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200534 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
535 return -EIO;
Harald Weltee687be52016-05-03 18:49:27 +0200536
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200537 if (!db_bind_text(stmt, "$number", vlr_or_sgsn_number))
538 return -EIO;
Harald Weltee687be52016-05-03 18:49:27 +0200539
540 /* execute the statement */
541 rc = sqlite3_step(stmt);
542 if (rc != SQLITE_DONE) {
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200543 LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%"PRId64": SQL Error: %s\n",
544 is_ps? "SGSN" : "VLR", subscr_id, sqlite3_errmsg(dbc->db));
545 ret = -EIO;
546 goto out;
Harald Weltee687be52016-05-03 18:49:27 +0200547 }
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200548
549 /* verify execution result */
550 rc = sqlite3_changes(dbc->db);
551 if (!rc) {
552 LOGP(DAUC, LOGL_ERROR, "Cannot update %s number for subscriber ID=%"PRId64
553 ": no such subscriber\n",
554 is_ps? "SGSN" : "VLR", subscr_id);
555 ret = -ENOENT;
556 } else if (rc != 1) {
557 LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%"PRId64
558 ": SQL modified %d rows (expected 1)\n",
559 is_ps? "SGSN" : "VLR", subscr_id, rc);
560 ret = -EIO;
561 }
562
Harald Weltee687be52016-05-03 18:49:27 +0200563out:
Max00b37152017-02-20 11:09:27 +0100564 db_remove_reset(stmt);
Harald Weltee687be52016-05-03 18:49:27 +0200565 return ret;
566}
Harald Welteb18f0e02016-05-05 21:03:03 +0200567
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200568int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
569 bool purge_val, bool is_ps)
Harald Welteb18f0e02016-05-05 21:03:03 +0200570{
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200571 sqlite3_stmt *stmt;
572 int rc, ret = 0;
Harald Welteb18f0e02016-05-05 21:03:03 +0200573
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200574 stmt = dbc->stmt[is_ps ? DB_STMT_UPD_PURGE_PS_BY_IMSI
575 : DB_STMT_UPD_PURGE_CS_BY_IMSI];
Harald Welteb18f0e02016-05-05 21:03:03 +0200576
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200577 if (!db_bind_text(stmt, "$imsi", by_imsi))
578 return -EIO;
579 if (!db_bind_int(stmt, "$val", purge_val ? 1 : 0))
580 return -EIO;
Harald Welteb18f0e02016-05-05 21:03:03 +0200581
582 /* execute the statement */
583 rc = sqlite3_step(stmt);
584 if (rc != SQLITE_DONE) {
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200585 LOGP(DAUC, LOGL_ERROR, "%s %s: SQL error: %s\n",
586 purge_val ? "purge" : "un-purge",
587 is_ps ? "PS" : "CS",
588 sqlite3_errmsg(dbc->db));
589 ret = -EIO;
590 goto out;
Harald Welteb18f0e02016-05-05 21:03:03 +0200591 }
Max00b37152017-02-20 11:09:27 +0100592
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200593 /* verify execution result */
594 rc = sqlite3_changes(dbc->db);
595 if (!rc) {
596 LOGP(DAUC, LOGL_ERROR, "Cannot %s %s: no such subscriber: IMSI='%s'\n",
597 purge_val ? "purge" : "un-purge",
598 is_ps ? "PS" : "CS",
599 by_imsi);
600 ret = -ENOENT;
601 goto out;
602 } else if (rc != 1) {
603 LOGHLR(by_imsi, LOGL_ERROR, "%s %s: SQL modified %d rows (expected 1)\n",
604 purge_val ? "purge" : "un-purge",
605 is_ps ? "PS" : "CS",
606 rc);
607 ret = -EIO;
608 }
609
610out:
Max00b37152017-02-20 11:09:27 +0100611 db_remove_reset(stmt);
Harald Welteb18f0e02016-05-05 21:03:03 +0200612
613 return ret;
614}
Neels Hofmeyr00b1d432017-10-17 01:43:48 +0200615
616/*! Update nam_cs/nam_ps in the db and trigger notifications to GSUP clients.
617 * \param hlr Global hlr context.
618 * \param subscr Subscriber from a fresh db_subscr_get_by_*() call.
619 * \param nam_val True to enable CS/PS, false to disable.
620 * \param is_ps True to enable/disable PS, false for CS.
621 * \returns 0 on success, ENOEXEC if there is no need to change, a negative
622 * value on error.
623 */
624int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps)
625{
626 int rc;
627 struct lu_operation *luop;
628 struct osmo_gsup_conn *co;
629 bool is_val = is_ps? subscr->nam_ps : subscr->nam_cs;
630
631 if (is_val == nam_val) {
632 LOGHLR(subscr->imsi, LOGL_DEBUG, "Already has the requested value when asked to %s %s\n",
633 nam_val ? "enable" : "disable", is_ps ? "PS" : "CS");
634 return ENOEXEC;
635 }
636
637 rc = db_subscr_nam(hlr->dbc, subscr->imsi, nam_val, is_ps);
638 if (rc)
639 return rc > 0? -rc : rc;
640
641 /* If we're disabling, send a notice out to the GSUP client that is
642 * responsible. Otherwise no need. */
643 if (nam_val)
644 return 0;
645
646 /* FIXME: only send to single SGSN where latest update for IMSI came from */
647 llist_for_each_entry(co, &hlr->gs->clients, list) {
648 luop = lu_op_alloc_conn(co);
649 if (!luop) {
650 LOGHLR(subscr->imsi, LOGL_ERROR,
651 "Cannot notify GSUP client, cannot allocate lu_operation,"
652 " for %s:%u\n",
653 co && co->conn && co->conn->server? co->conn->server->addr : "unset",
654 co && co->conn && co->conn->server? co->conn->server->port : 0);
655 continue;
656 }
657 luop->subscr = *subscr;
658 lu_op_tx_del_subscr_data(luop);
659 lu_op_free(luop);
660 }
661 return 0;
662}