diff --git a/src/host/layer23/include/osmocom/bb/common/osmocom_data.h b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h index 17dad10df..a88a12db5 100644 --- a/src/host/layer23/include/osmocom/bb/common/osmocom_data.h +++ b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h @@ -1,6 +1,8 @@ #ifndef osmocom_data_h #define osmocom_data_h +#include + #include #include #include @@ -77,6 +79,8 @@ struct osmocom_ms { struct gsm48_cclayer cclayer; struct osmomncc_entity mncc_entity; struct llist_head trans_list; + + sqlite3 *db_sms; }; enum osmobb_sig_subsys { diff --git a/src/host/layer23/include/osmocom/bb/mobile/db.h b/src/host/layer23/include/osmocom/bb/mobile/db.h new file mode 100644 index 000000000..00b37ff43 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/db.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +int db_open(sqlite3 **db, const char *db_name); +void db_close(sqlite3 *db); diff --git a/src/host/layer23/include/osmocom/bb/mobile/db_sms.h b/src/host/layer23/include/osmocom/bb/mobile/db_sms.h new file mode 100644 index 000000000..199207e5d --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/db_sms.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +struct db_sms_record { + char *dst_number; + char *src_imsi; + char *text; + int row_id; +}; + +int db_prepare(sqlite3 *db); +int db_pop_sms(sqlite3 *db, struct db_sms_record **sms); +int db_push_sms(sqlite3 *db, char *dst_imsi, uint32_t dst_tmsi, + char *src_number, char *text); diff --git a/src/host/layer23/include/osmocom/bb/mobile/subscriber.h b/src/host/layer23/include/osmocom/bb/mobile/subscriber.h index cd821e66f..73ef8cf4e 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/subscriber.h +++ b/src/host/layer23/include/osmocom/bb/mobile/subscriber.h @@ -118,6 +118,7 @@ char *gsm_check_imsi(const char *imsi); int gsm_subscr_get_key_seq(struct osmocom_ms *ms, struct gsm_subscriber *subscr); int multi_imsi_work(struct osmocom_ms *ms); int multi_imsi_spoof(struct osmocom_ms *ms, struct gsm_subscriber_creds *src); +int multi_imsi_switch_imsi(struct osmocom_ms *ms, const char *imsi); #endif /* _SUBSCRIBER_H */ diff --git a/src/host/layer23/src/mobile/Makefile.am b/src/host/layer23/src/mobile/Makefile.am index 04dd0257f..a276076ec 100644 --- a/src/host/layer23/src/mobile/Makefile.am +++ b/src/host/layer23/src/mobile/Makefile.am @@ -1,11 +1,20 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBGPS_CFLAGS) -LDADD = ../common/liblayer23.a $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBGPS_LIBS) +LDADD = \ + ../common/liblayer23.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOVTY_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOCODEC_LIBS) \ + $(LIBGPS_LIBS) \ + -lsqlite3 noinst_LIBRARIES = libmobile.a -libmobile_a_SOURCES = gsm322.c gsm480_ss.c gsm411_sms.c gsm48_cc.c gsm48_mm.c \ +libmobile_a_SOURCES = \ + gsm322.c gsm480_ss.c gsm411_sms.c gsm48_cc.c gsm48_mm.c \ gsm48_rr.c mnccms.c settings.c subscriber.c support.c \ - transaction.c vty_interface.c voice.c mncc_sock.c + transaction.c vty_interface.c voice.c mncc_sock.c \ + db.c db_sms.c bin_PROGRAMS = mobile diff --git a/src/host/layer23/src/mobile/app_mobile.c b/src/host/layer23/src/mobile/app_mobile.c index 7198f939d..28ac33019 100644 --- a/src/host/layer23/src/mobile/app_mobile.c +++ b/src/host/layer23/src/mobile/app_mobile.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include #include @@ -56,6 +58,38 @@ int mncc_recv_dummy(struct osmocom_ms *ms, int msg_type, void *arg); int (*mncc_recv_app)(struct osmocom_ms *ms, int, void *); static int quit; +static int db_sms_check(struct osmocom_ms *ms) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm_settings *set = &ms->settings; + struct db_sms_record *sms = NULL; + const char *sms_sca = "99999"; + int rc; + + // Phone should be registered and should support SMS + if (cs->state != GSM322_C3_CAMPED_NORMALLY || !set->sms_ptp) + return 0; + + // Check if we have a new message to send + rc = db_pop_sms(ms->db_sms, &sms); + if (rc || !sms) + return 0; + + // So, we got a new SMS to send + vty_notify(ms, "Got a new SMS to send:\n"); + vty_notify(ms, "IMSI (%s) -> extension (%s)\n", + sms->src_imsi, sms->dst_number); + + // Set up required IMSI + multi_imsi_switch_imsi(ms, sms->src_imsi); + + // Send SMS + sms_send(ms, sms_sca, sms->dst_number, sms->text); + + talloc_free(sms); + return 0; +} + /* handle ms instance */ int mobile_work(struct osmocom_ms *ms) { @@ -73,6 +107,7 @@ int mobile_work(struct osmocom_ms *ms) w |= gsm_sim_job_dequeue(ms); w |= mncc_dequeue(ms); w |= multi_imsi_work(ms); + w |= db_sms_check(ms); if (w) work = 1; } while (w); @@ -166,6 +201,7 @@ int mobile_exit(struct osmocom_ms *ms, int force) gsm411_sms_exit(ms); gsm_sim_exit(ms); lapdm_channel_exit(&ms->lapdm_channel); + db_close(ms->db_sms); if (ms->started) { ms->shutdown = 2; /* being down, wait for reset */ @@ -183,6 +219,7 @@ int mobile_exit(struct osmocom_ms *ms, int force) /* power-on ms instance */ int mobile_init(struct osmocom_ms *ms) { + char *db_name; int rc; gsm_settings_arfcn(ms); @@ -196,6 +233,27 @@ int mobile_init(struct osmocom_ms *ms) ms->lapdm_channel.lapdm_acch.datalink[DL_SAPI3].dl.t200_usec = 0; lapdm_channel_set_l1(&ms->lapdm_channel, l1ctl_ph_prim_cb, ms); + // Compose database name + db_name = talloc_asprintf(ms, "sms_db_%s.sqlite3", ms->name); + + // Init database connection + rc = db_open(&ms->db_sms, db_name); + if (rc) { + ms->l2_wq.bfd.fd = -1; + mobile_exit(ms, 1); + return rc; + } + + // Prepare database if required + rc = db_prepare(ms->db_sms); + if (rc) { + ms->l2_wq.bfd.fd = -1; + mobile_exit(ms, 1); + return rc; + } + + talloc_free(db_name); + /* init SAP client before SIM card starts up */ osmosap_init(ms); diff --git a/src/host/layer23/src/mobile/db.c b/src/host/layer23/src/mobile/db.c new file mode 100644 index 000000000..f22d5049a --- /dev/null +++ b/src/host/layer23/src/mobile/db.c @@ -0,0 +1,88 @@ +/* + * (C) 2017 by Vadim Yanitskiy + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +extern void *l23_ctx; + +static char *db_compose_path(const char *db_name) +{ + const char *db_home_path = ".osmocom/bb"; + const char *db_tmp_path = "/tmp"; + char const *home = getenv("HOME"); + char *db_full_path; + size_t len; + + if (home != NULL) { + len = strlen(home) + strlen(db_home_path) + strlen(db_name) + 3; + db_full_path = talloc_size(l23_ctx, len); + if (db_full_path != NULL) + snprintf(db_full_path, len, "%s/%s/%s", + home, db_home_path, db_name); + } else { + len = strlen(db_tmp_path) + strlen(db_name) + 2; + db_full_path = talloc_size(l23_ctx, len); + if (db_full_path != NULL) + snprintf(db_full_path, len, "%s/%s", + db_tmp_path, db_name); + } + + return db_full_path; +} + +int db_open(sqlite3 **db, const char *db_name) +{ + char *db_full_path; + int rc; + + // Compose full database path + db_full_path = db_compose_path(db_name); + if (db_full_path == NULL) + return -ENOMEM; + + // Connect to database + rc = sqlite3_open(db_full_path, db); + if (rc) { + fprintf(stderr, "[!] Couldn't open database: %s\n", + sqlite3_errmsg(*db)); + goto final; + } + + fprintf(stderr, "[i] Successfully connected to database\n"); + +final: + talloc_free(db_full_path); + + return rc; +} + +void db_close(sqlite3 *db) +{ + if (!db) + sqlite3_close(db); +} diff --git a/src/host/layer23/src/mobile/db_sms.c b/src/host/layer23/src/mobile/db_sms.c new file mode 100644 index 000000000..fbca27737 --- /dev/null +++ b/src/host/layer23/src/mobile/db_sms.c @@ -0,0 +1,191 @@ +/* + * (C) 2017 by Vadim Yanitskiy + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +extern void *l23_ctx; + +int db_prepare(sqlite3 *db) +{ + char *err = 0; + int rc = 0; + + const char *inbox_sql = \ + "CREATE TABLE IF NOT EXISTS inbox (" \ + "dst_imsi TEXT NOT NULL, " \ + "dst_tmsi TEXT NOT NULL, " \ + "src_number TEXT NOT NULL, " \ + "date DATETIME DEFAULT CURRENT_TIMESTAMP, " \ + "handled INT DEFAULT 0, " \ + "text TEXT NOT NULL" \ + ");"; + + const char *outbox_sql = \ + "CREATE TABLE IF NOT EXISTS outbox (" \ + "src_imsi text TEXT NOT NULL, " \ + "dst_number text TEXT NOT NULL, " \ + "date DATETIME DEFAULT CURRENT_TIMESTAMP, " \ + "handled INT DEFAULT 0, " \ + "text TEXT NOT NULL" \ + ");"; + + rc = sqlite3_exec(db, inbox_sql, NULL, 0, &err); + if (rc != SQLITE_DONE && rc != SQLITE_OK) { + fprintf(stderr, "[!] Couldn't init database: %s\n", err); + return rc; + } + + rc = sqlite3_exec(db, outbox_sql, NULL, 0, &err) == SQLITE_DONE; + if (rc != SQLITE_DONE && rc != SQLITE_OK) { + fprintf(stderr, "[!] Couldn't init database: %s\n", err); + return rc; + } + + fprintf(stderr, "[i] Database init complete\n"); + + return 0; +} + +int db_push_sms(sqlite3 *db, char *dst_imsi, uint32_t dst_tmsi, + char *src_number, char *text) +{ + sqlite3_stmt *stmt; + char *tmsi_str; + int rc; + + const char *push_sql = \ + "INSERT INTO inbox " \ + "(dst_imsi, dst_tmsi, src_number, text) " + "VALUES (?, ?, ?, ?);"; + + rc = sqlite3_prepare(db, push_sql, strlen(push_sql), &stmt, NULL); + if (rc != SQLITE_OK) { + fprintf(stderr, "[!] SQLite error: %s\n", sqlite3_errmsg(db)); + return rc; + } + + // Convert TMSI to string + tmsi_str = talloc_asprintf(l23_ctx, "0x%08x", dst_tmsi); + + // Bind query params + sqlite3_bind_text(stmt, 1, dst_imsi, strlen(dst_imsi), 0); + sqlite3_bind_text(stmt, 2, tmsi_str, strlen(tmsi_str), 0); + sqlite3_bind_text(stmt, 3, src_number, strlen(src_number), 0); + sqlite3_bind_text(stmt, 4, text, strlen(text), 0); + + // Commit + rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE && rc != SQLITE_OK) { + fprintf(stderr, "[!] SQLite error: %s\n", sqlite3_errmsg(db)); + return rc; + } + + printf("[i] Message saved\n"); + + sqlite3_finalize(stmt); + talloc_free(tmsi_str); + + return 0; +} + +int db_pop_sms(sqlite3 *db, struct db_sms_record **sms) +{ + struct db_sms_record *sms_new = NULL; + sqlite3_stmt *stmt; + int rc; + + const char *pop_select_sql = \ + "SELECT rowid, src_imsi, dst_number, text " \ + "FROM outbox WHERE handled = 0;"; + + const char *pop_update_sql = \ + "UPDATE outbox SET handled = 1 " \ + "WHERE rowid = ?;"; + + // Prepare query + rc = sqlite3_prepare(db, pop_select_sql, strlen(pop_select_sql), + &stmt, NULL); + if (rc != SQLITE_OK) { + fprintf(stderr, "[!] SQLite error: %s\n", sqlite3_errmsg(db)); + return rc; + } + + // Attempt to get a record + rc = sqlite3_step(stmt); + if (rc != SQLITE_ROW) { + if (rc != SQLITE_DONE && rc != SQLITE_OK) + fprintf(stderr, "[!] SQLite error: %s\n", sqlite3_errmsg(db)); + + goto final; + } + + // Allocate a new structure for SMS + sms_new = talloc(l23_ctx, struct db_sms_record); + if (sms_new == NULL) { + rc = -ENOMEM; + goto final; + } + + // Fill structure + sms_new->row_id = sqlite3_column_int(stmt, 0); + sms_new->src_imsi = talloc_strdup(sms_new, + (char *) sqlite3_column_text(stmt, 1)); + sms_new->dst_number = talloc_strdup(sms_new, + (char *) sqlite3_column_text(stmt, 2)); + sms_new->text = talloc_strdup(sms_new, + (char *) sqlite3_column_text(stmt, 3)); + + // Set external pointer + *sms = sms_new; + + // Prepare another query + sqlite3_finalize(stmt); + rc = sqlite3_prepare(db, pop_update_sql, strlen(pop_update_sql), + &stmt, NULL); + if (rc != SQLITE_OK) { + fprintf(stderr, "[!] SQLite error: %s\n", sqlite3_errmsg(db)); + return rc; + } + + // Mark this record as 'handled' + sqlite3_bind_int(stmt, 1, sms_new->row_id); + rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE && rc != SQLITE_OK) { + fprintf(stderr, "[!] SQLite error: %s\n", sqlite3_errmsg(db)); + goto final; + } + + printf("[i] Got a new message from DB\n"); + +final: + sqlite3_finalize(stmt); + + return (rc != SQLITE_DONE && rc != SQLITE_OK) ? rc : 0; +} diff --git a/src/host/layer23/src/mobile/gsm411_sms.c b/src/host/layer23/src/mobile/gsm411_sms.c index 655fe5354..85449bfa8 100644 --- a/src/host/layer23/src/mobile/gsm411_sms.c +++ b/src/host/layer23/src/mobile/gsm411_sms.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include #include #include @@ -213,6 +215,10 @@ fail: fprintf(fp, "[SMS from %s]\n%s\n", gsms->address, gsms->text); fclose(fp); + // Save SMS to database + db_push_sms(ms->db_sms, ms->subscr.imsi, ms->subscr.tmsi, + gsms->address, gsms->text); + talloc_free(sms_file); return 0;