blob: 5acd1e8b33fa701b52d896ee0ccc5cd812721152 [file] [log] [blame]
Neels Hofmeyr98509462017-10-09 17:28:53 +02001/* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
2 * All Rights Reserved
3 *
4 * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21#include <stdio.h>
22#include <errno.h>
23#include <getopt.h>
24#include <inttypes.h>
25
26#include <osmocom/core/application.h>
27#include <osmocom/core/utils.h>
28#include <osmocom/core/logging.h>
29
30#include "db.h"
31#include "logging.h"
32
33#define comment_start() fprintf(stderr, "\n===== %s\n", __func__);
34#define comment(fmt, args...) fprintf(stderr, "\n--- " fmt "\n\n", ## args);
35#define comment_end() fprintf(stderr, "===== %s: SUCCESS\n\n", __func__);
36
37/* Perform a function call and verbosely assert that its return value is as expected.
38 * The return code is then available in g_rc. */
39#define ASSERT_RC(call, expect_rc) \
40 do { \
41 fprintf(stderr, #call " --> " #expect_rc "\n"); \
42 g_rc = call; \
43 if (g_rc != (expect_rc)) \
44 fprintf(stderr, " MISMATCH: got rc = %d, expected: " \
45 #expect_rc " = %d\n", g_rc, expect_rc); \
46 OSMO_ASSERT(g_rc == (expect_rc)); \
47 fprintf(stderr, "\n"); \
48 } while (0)
49
50/* Do db_subscr_get_by_xxxx and verbosely assert that its return value is as expected.
51 * Print the subscriber struct to stderr to be validated by db_test.err.
52 * The result is then available in g_subscr. */
53#define ASSERT_SEL(by, val, expect_rc) \
54 do { \
55 int rc; \
56 g_subscr = (struct hlr_subscriber){}; \
57 fprintf(stderr, "db_subscr_get_by_" #by "(dbc, " #val ", &g_subscr) --> " \
58 #expect_rc "\n"); \
59 rc = db_subscr_get_by_##by(dbc, val, &g_subscr); \
60 if (rc != (expect_rc)) \
61 fprintf(stderr, " MISMATCH: got rc = %d, expected: " \
62 #expect_rc " = %d\n", rc, expect_rc); \
63 OSMO_ASSERT(rc == (expect_rc)); \
64 if (!rc) \
65 dump_subscr(&g_subscr); \
66 fprintf(stderr, "\n"); \
67 } while (0)
68
69static struct db_context *dbc = NULL;
70static void *ctx = NULL;
71static struct hlr_subscriber g_subscr;
72static int g_rc;
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +020073static int64_t g_id;
Neels Hofmeyr98509462017-10-09 17:28:53 +020074
75#define Pfv(name, fmt, val) \
76 fprintf(stderr, " ." #name " = " fmt ",\n", val)
77#define Pfo(name, fmt, obj) \
78 Pfv(name, fmt, obj->name)
79
80/* Print a subscriber struct to stderr to be validated by db_test.err. */
81void dump_subscr(struct hlr_subscriber *subscr)
82{
83#define Ps(name) \
84 if (*subscr->name) \
85 Pfo(name, "'%s'", subscr)
86#define Pd(name) \
87 Pfv(name, "%"PRId64, (int64_t)subscr->name)
88#define Pd_nonzero(name) \
89 if (subscr->name) \
90 Pd(name)
91#define Pb(if_val, name) \
92 if (subscr->name == (if_val)) \
93 Pfv(name, "%s", subscr->name ? "true" : "false")
94
95 fprintf(stderr, "struct hlr_subscriber {\n");
96 Pd(id);
97 Ps(imsi);
98 Ps(msisdn);
99 Ps(vlr_number);
100 Ps(sgsn_number);
101 Ps(sgsn_address);
102 Pd_nonzero(periodic_lu_timer);
103 Pd_nonzero(periodic_rau_tau_timer);
104 Pb(false, nam_cs);
105 Pb(false, nam_ps);
106 if (subscr->lmsi)
107 Pfo(lmsi, "0x%x", subscr);
108 Pb(true, ms_purged_cs);
109 Pb(true, ms_purged_ps);
110 fprintf(stderr, "}\n");
111#undef Ps
112#undef Pd
113#undef Pd_nonzero
114#undef Pb
115}
116
117void dump_aud(const char *label, struct osmo_sub_auth_data *aud)
118{
119 if (aud->type == OSMO_AUTH_TYPE_NONE) {
120 fprintf(stderr, "%s: none\n", label);
121 return;
122 }
123
124 fprintf(stderr, "%s: struct osmo_sub_auth_data {\n", label);
125#define Pf(name, fmt) \
126 Pfo(name, fmt, aud)
127#define Phex(name) \
128 Pfv(name, "'%s'", osmo_hexdump_nospc(aud->name, sizeof(aud->name)))
129
130 Pfv(type, "%s", osmo_sub_auth_type_name(aud->type));
131 Pfv(algo, "%s", osmo_auth_alg_name(aud->algo));
132 switch (aud->type) {
133 case OSMO_AUTH_TYPE_GSM:
134 Phex(u.gsm.ki);
135 break;
136 case OSMO_AUTH_TYPE_UMTS:
137 Phex(u.umts.opc);
138 Pf(u.umts.opc_is_op, "%u");
139 Phex(u.umts.k);
140 Phex(u.umts.amf);
141 if (aud->u.umts.sqn) {
142 Pf(u.umts.sqn, "%"PRIu64);
143 Pf(u.umts.sqn, "0x%"PRIx64);
144 }
145 if (aud->u.umts.ind_bitlen)
146 Pf(u.umts.ind_bitlen, "%u");
147 break;
148 default:
149 OSMO_ASSERT(false);
150 }
151
152 fprintf(stderr, "}\n");
153
154#undef Pf
155#undef Phex
156}
157
158static const char *imsi0 = "123456789000000";
159static const char *imsi1 = "123456789000001";
160static const char *imsi2 = "123456789000002";
161static const char *short_imsi = "123456";
162static const char *unknown_imsi = "999999999";
163
164static void test_subscr_create_update_sel_delete()
165{
166 int64_t id0, id1, id2, id_short;
167 comment_start();
168
169 comment("Create with valid / invalid IMSI");
170
171 ASSERT_RC(db_subscr_create(dbc, imsi0), 0);
172 ASSERT_SEL(imsi, imsi0, 0);
173 id0 = g_subscr.id;
174 ASSERT_RC(db_subscr_create(dbc, imsi1), 0);
175 ASSERT_SEL(imsi, imsi1, 0);
176 id1 = g_subscr.id;
177 ASSERT_RC(db_subscr_create(dbc, imsi2), 0);
178 ASSERT_SEL(imsi, imsi2, 0);
179 id2 = g_subscr.id;
180 ASSERT_RC(db_subscr_create(dbc, imsi0), -EIO);
181 ASSERT_SEL(imsi, imsi0, 0);
182 ASSERT_RC(db_subscr_create(dbc, imsi1), -EIO);
183 ASSERT_RC(db_subscr_create(dbc, imsi1), -EIO);
184 ASSERT_SEL(imsi, imsi1, 0);
185 ASSERT_RC(db_subscr_create(dbc, imsi2), -EIO);
186 ASSERT_RC(db_subscr_create(dbc, imsi2), -EIO);
187 ASSERT_SEL(imsi, imsi2, 0);
188
189 ASSERT_RC(db_subscr_create(dbc, "123456789 000003"), -EINVAL);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200190 ASSERT_SEL(imsi, "123456789000003", -ENOENT);
Neels Hofmeyr98509462017-10-09 17:28:53 +0200191
192 ASSERT_RC(db_subscr_create(dbc, "123456789000002123456"), -EINVAL);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200193 ASSERT_SEL(imsi, "123456789000002123456", -ENOENT);
Neels Hofmeyr98509462017-10-09 17:28:53 +0200194
195 ASSERT_RC(db_subscr_create(dbc, "foobar123"), -EINVAL);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200196 ASSERT_SEL(imsi, "foobar123", -ENOENT);
Neels Hofmeyr98509462017-10-09 17:28:53 +0200197
198 ASSERT_RC(db_subscr_create(dbc, "123"), -EINVAL);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200199 ASSERT_SEL(imsi, "123", -ENOENT);
Neels Hofmeyr98509462017-10-09 17:28:53 +0200200
201 ASSERT_RC(db_subscr_create(dbc, short_imsi), 0);
202 ASSERT_SEL(imsi, short_imsi, 0);
203 id_short = g_subscr.id;
204
205
206 comment("Set valid / invalid MSISDN");
207
208 ASSERT_SEL(imsi, imsi0, 0);
209 ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0, "54321"), 0);
210 ASSERT_SEL(imsi, imsi0, 0);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200211 ASSERT_SEL(msisdn, "54321", 0);
Neels Hofmeyr98509462017-10-09 17:28:53 +0200212 ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0,
213 "54321012345678912345678"), -EINVAL);
214 ASSERT_SEL(imsi, imsi0, 0);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200215 ASSERT_SEL(msisdn, "54321", 0);
216 ASSERT_SEL(msisdn, "54321012345678912345678", -ENOENT);
Neels Hofmeyr98509462017-10-09 17:28:53 +0200217 ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0,
218 "543 21"), -EINVAL);
219 ASSERT_SEL(imsi, imsi0, 0);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200220 ASSERT_SEL(msisdn, "543 21", -ENOENT);
Neels Hofmeyr98509462017-10-09 17:28:53 +0200221 ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0,
222 "foobar123"), -EINVAL);
223 ASSERT_SEL(imsi, imsi0, 0);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200224 ASSERT_SEL(msisdn, "foobar123", -ENOENT);
Neels Hofmeyr98509462017-10-09 17:28:53 +0200225 ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0,
226 "5"), 0);
227 ASSERT_SEL(imsi, imsi0, 0);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200228 ASSERT_SEL(msisdn, "5", 0);
229 ASSERT_SEL(msisdn, "54321", -ENOENT);
Neels Hofmeyr98509462017-10-09 17:28:53 +0200230 ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0,
231 "543210123456789"), 0);
232 ASSERT_SEL(imsi, imsi0, 0);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200233 ASSERT_SEL(msisdn, "543210123456789", 0);
Neels Hofmeyr98509462017-10-09 17:28:53 +0200234 ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0,
235 "5432101234567891"), -EINVAL);
236 ASSERT_SEL(imsi, imsi0, 0);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200237 ASSERT_SEL(msisdn, "5432101234567891", -ENOENT);
Neels Hofmeyr98509462017-10-09 17:28:53 +0200238
239 comment("Set MSISDN on non-existent / invalid IMSI");
240
241 ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, unknown_imsi, "99"), -ENOENT);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200242 ASSERT_SEL(msisdn, "99", -ENOENT);
Neels Hofmeyr98509462017-10-09 17:28:53 +0200243
244 ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, "foobar", "99"), -ENOENT);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200245 ASSERT_SEL(msisdn, "99", -ENOENT);
Neels Hofmeyr98509462017-10-09 17:28:53 +0200246
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200247 comment("Set / unset nam_cs and nam_ps");
248
249 /* nam_val, is_ps */
250 ASSERT_RC(db_subscr_nam(dbc, imsi0, false, true), 0);
251 ASSERT_SEL(imsi, imsi0, 0);
252 ASSERT_RC(db_subscr_nam(dbc, imsi0, false, false), 0);
253 ASSERT_SEL(imsi, imsi0, 0);
254 ASSERT_RC(db_subscr_nam(dbc, imsi0, true, false), 0);
255 ASSERT_SEL(imsi, imsi0, 0);
256 ASSERT_RC(db_subscr_nam(dbc, imsi0, true, true), 0);
257 ASSERT_SEL(imsi, imsi0, 0);
258
259 comment("Set / unset nam_cs and nam_ps *again*");
260 ASSERT_RC(db_subscr_nam(dbc, imsi0, false, true), 0);
261 ASSERT_SEL(imsi, imsi0, 0);
262 ASSERT_RC(db_subscr_nam(dbc, imsi0, false, true), 0);
263 ASSERT_SEL(imsi, imsi0, 0);
264 ASSERT_RC(db_subscr_nam(dbc, imsi0, false, false), 0);
265 ASSERT_SEL(imsi, imsi0, 0);
266 ASSERT_RC(db_subscr_nam(dbc, imsi0, false, false), 0);
267 ASSERT_SEL(imsi, imsi0, 0);
268 ASSERT_RC(db_subscr_nam(dbc, imsi0, true, true), 0);
269 ASSERT_SEL(imsi, imsi0, 0);
270 ASSERT_RC(db_subscr_nam(dbc, imsi0, true, true), 0);
271 ASSERT_SEL(imsi, imsi0, 0);
272 ASSERT_RC(db_subscr_nam(dbc, imsi0, true, false), 0);
273 ASSERT_SEL(imsi, imsi0, 0);
274 ASSERT_RC(db_subscr_nam(dbc, imsi0, true, false), 0);
275 ASSERT_SEL(imsi, imsi0, 0);
276
277 comment("Set nam_cs and nam_ps on non-existent / invalid IMSI");
278
279 ASSERT_RC(db_subscr_nam(dbc, unknown_imsi, false, true), -ENOENT);
280 ASSERT_RC(db_subscr_nam(dbc, unknown_imsi, false, false), -ENOENT);
281 ASSERT_SEL(imsi, unknown_imsi, -ENOENT);
282
283 ASSERT_RC(db_subscr_nam(dbc, "foobar", false, true), -ENOENT);
284 ASSERT_RC(db_subscr_nam(dbc, "foobar", false, false), -ENOENT);
285
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200286 comment("Record LU for PS and CS (SGSN and VLR names)");
287
288 ASSERT_RC(db_subscr_lu(dbc, id0, "5952", true), 0);
289 ASSERT_SEL(id, id0, 0);
290 ASSERT_RC(db_subscr_lu(dbc, id0, "712", false), 0);
291 ASSERT_SEL(id, id0, 0);
292
293 comment("Record LU for PS and CS (SGSN and VLR names) *again*");
294
295 ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0);
296 ASSERT_SEL(id, id0, 0);
297 ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0);
298 ASSERT_SEL(id, id0, 0);
299 ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0);
300 ASSERT_SEL(id, id0, 0);
301 ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0);
302 ASSERT_SEL(id, id0, 0);
303
304 comment("Unset LU info for PS and CS (SGSN and VLR names)");
305 ASSERT_RC(db_subscr_lu(dbc, id0, "", true), 0);
306 ASSERT_SEL(id, id0, 0);
307 ASSERT_RC(db_subscr_lu(dbc, id0, "", false), 0);
308 ASSERT_SEL(id, id0, 0);
309
310 ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0);
311 ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0);
312 ASSERT_SEL(id, id0, 0);
313 ASSERT_RC(db_subscr_lu(dbc, id0, NULL, true), 0);
314 ASSERT_SEL(id, id0, 0);
315 ASSERT_RC(db_subscr_lu(dbc, id0, NULL, false), 0);
316 ASSERT_SEL(id, id0, 0);
317
318 comment("Record LU for non-existent ID");
319 ASSERT_RC(db_subscr_lu(dbc, 99999, "5952", true), -ENOENT);
320 ASSERT_RC(db_subscr_lu(dbc, 99999, "712", false), -ENOENT);
321 ASSERT_SEL(id, 99999, -ENOENT);
322
Neels Hofmeyr98509462017-10-09 17:28:53 +0200323 comment("Delete non-existent / invalid IDs");
324
325 ASSERT_RC(db_subscr_delete_by_id(dbc, 999), -ENOENT);
326 ASSERT_RC(db_subscr_delete_by_id(dbc, -10), -ENOENT);
327
328 comment("Delete subscribers");
329
330 ASSERT_SEL(imsi, imsi0, 0);
331 ASSERT_RC(db_subscr_delete_by_id(dbc, id0), 0);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200332 ASSERT_SEL(imsi, imsi0, -ENOENT);
Neels Hofmeyr98509462017-10-09 17:28:53 +0200333 ASSERT_RC(db_subscr_delete_by_id(dbc, id0), -ENOENT);
334
335 ASSERT_SEL(imsi, imsi1, 0);
336 ASSERT_RC(db_subscr_delete_by_id(dbc, id1), 0);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200337 ASSERT_SEL(imsi, imsi1, -ENOENT);
Neels Hofmeyr98509462017-10-09 17:28:53 +0200338
339 ASSERT_SEL(imsi, imsi2, 0);
340 ASSERT_RC(db_subscr_delete_by_id(dbc, id2), 0);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200341 ASSERT_SEL(imsi, imsi2, -ENOENT);
Neels Hofmeyr98509462017-10-09 17:28:53 +0200342
343 ASSERT_SEL(imsi, short_imsi, 0);
344 ASSERT_RC(db_subscr_delete_by_id(dbc, id_short), 0);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200345 ASSERT_SEL(imsi, short_imsi, -ENOENT);
Neels Hofmeyr98509462017-10-09 17:28:53 +0200346
347 comment_end();
348}
349
350static struct {
351 bool verbose;
352} cmdline_opts = {
353 .verbose = false,
354};
355
356static void print_help(const char *program)
357{
358 printf("Usage:\n"
359 " %s [-v] [N [N...]]\n"
360 "Options:\n"
361 " -h --help show this text.\n"
362 " -v --verbose print source file and line numbers\n",
363 program
364 );
365}
366
367static void handle_options(int argc, char **argv)
368{
369 while (1) {
370 int option_index = 0, c;
371 static struct option long_options[] = {
372 {"help", 0, 0, 'h'},
373 {"verbose", 1, 0, 'v'},
374 {0, 0, 0, 0}
375 };
376
377 c = getopt_long(argc, argv, "hv",
378 long_options, &option_index);
379 if (c == -1)
380 break;
381
382 switch (c) {
383 case 'h':
384 print_help(argv[0]);
385 exit(0);
386 case 'v':
387 cmdline_opts.verbose = true;
388 break;
389 default:
390 /* catch unknown options *as well as* missing arguments. */
391 fprintf(stderr, "Error in command line options. Exiting.\n");
392 exit(-1);
393 break;
394 }
395 }
396
397 if (optind < argc) {
398 fprintf(stderr, "too many args\n");
399 exit(-1);
400 }
401}
402
403int main(int argc, char **argv)
404{
405 printf("db_test.c\n");
406
407 ctx = talloc_named_const(NULL, 1, "db_test");
408
409 handle_options(argc, argv);
410
411 osmo_init_logging(&hlr_log_info);
412 log_set_print_filename(osmo_stderr_target, cmdline_opts.verbose);
413 log_set_print_timestamp(osmo_stderr_target, 0);
414 log_set_use_color(osmo_stderr_target, 0);
415 log_set_print_category(osmo_stderr_target, 1);
416
417 /* omit the SQLite version and compilation flags from test output */
418 log_set_log_level(osmo_stderr_target, LOGL_ERROR);
419 dbc = db_open(ctx, "db_test.db");
420 log_set_log_level(osmo_stderr_target, 0);
421 OSMO_ASSERT(dbc);
422
423 test_subscr_create_update_sel_delete();
424
425 printf("Done\n");
426 return 0;
427}
428
429/* stubs */
430int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec,
431 struct osmo_sub_auth_data *aud2g,
432 struct osmo_sub_auth_data *aud3g,
433 const uint8_t *rand_auts, const uint8_t *auts)
434{ OSMO_ASSERT(false); return -1; }