FreeCalypso > hg > osmo-playpen
comparison euse-demo/osmo-euse-demo.c @ 0:d87e6dbd32c2
osmo-euse-demo compiles and links
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Tue, 01 Aug 2023 17:14:37 -0800 |
parents | |
children | 2067c55e2c79 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:d87e6dbd32c2 |
---|---|
1 /* osmo-demo-euse: An External USSD Entity (EUSE) for demo purpose */ | |
2 | |
3 /* (C) 2018 by Harald Welte <laforge@gnumonks.org> | |
4 * | |
5 * All Rights Reserved | |
6 * | |
7 * This program is free software; you can redistribute it and/or modify | |
8 * it under the terms of the GNU Affero General Public License as published by | |
9 * the Free Software Foundation; either version 3 of the License, or | |
10 * (at your option) any later version. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, | |
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 * GNU Affero General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Affero General Public License | |
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 */ | |
20 | |
21 /* | |
22 * This program illustrates how to implement an external USSD application using | |
23 * the existing osmocom libraries, particularly libosmocore, libosmogsm and libosmo-gsup-client. | |
24 * | |
25 * It will receive any MS-originated USSD message that is routed to it via the HLR, and | |
26 * simply respond it quoted in the following string: 'You sent "foobar"' (assuming the original | |
27 * message was 'foobar'). | |
28 */ | |
29 | |
30 #include <string.h> | |
31 #include <stdio.h> | |
32 #include <errno.h> | |
33 #include <signal.h> | |
34 | |
35 #include <osmocom/core/msgb.h> | |
36 #include <osmocom/core/select.h> | |
37 #include <osmocom/core/application.h> | |
38 #include <osmocom/core/utils.h> | |
39 #include <osmocom/core/logging.h> | |
40 | |
41 #include <osmocom/gsm/gsup.h> | |
42 #include <osmocom/gsm/gsm0480.h> | |
43 #include <osmocom/gsm/protocol/gsm_04_80.h> | |
44 | |
45 #include <osmocom/gsupclient/gsup_client.h> | |
46 | |
47 /* logging categories */ | |
48 enum { | |
49 DMAIN, | |
50 }; | |
51 | |
52 static struct osmo_gsup_client *g_gc; | |
53 | |
54 /*! send a SS/USSD response to a given imsi/session. | |
55 * \param[in] gsupc GSUP client connection through which to send | |
56 * \param[in] imsi IMSI of the subscriber | |
57 * \param[in] session_id Unique identifier of SS session for which this response is | |
58 * \param[in] gsup_msg_type GSUP message type (OSMO_GSUP_MSGT_PROC_SS_{REQUEST,RESULT,ERROR}) | |
59 * \param[in] final Is this the final result (true=END) or an intermediate result (false=CONTINUE) | |
60 * \param[in] msg Optional binary/BER encoded SS date (for FACILITY IE). Can be NULL. Freed in | |
61 * this function call. | |
62 */ | |
63 static int euse_tx_ss(struct osmo_gsup_client *gsupc, const char *imsi, uint32_t session_id, | |
64 enum osmo_gsup_message_type gsup_msg_type, bool final, struct msgb *ss_msg) | |
65 { | |
66 struct osmo_gsup_message resp = {0}; | |
67 struct msgb *resp_msg; | |
68 | |
69 switch (gsup_msg_type) { | |
70 case OSMO_GSUP_MSGT_PROC_SS_REQUEST: | |
71 case OSMO_GSUP_MSGT_PROC_SS_RESULT: | |
72 case OSMO_GSUP_MSGT_PROC_SS_ERROR: | |
73 break; | |
74 default: | |
75 msgb_free(ss_msg); | |
76 return -EINVAL; | |
77 } | |
78 | |
79 resp.message_type = gsup_msg_type; | |
80 OSMO_STRLCPY_ARRAY(resp.imsi, imsi); | |
81 if (final) | |
82 resp.session_state = OSMO_GSUP_SESSION_STATE_END; | |
83 else | |
84 resp.session_state = OSMO_GSUP_SESSION_STATE_CONTINUE; | |
85 resp.session_id = session_id; | |
86 if (ss_msg) { | |
87 resp.ss_info = msgb_data(ss_msg); | |
88 resp.ss_info_len = msgb_length(ss_msg); | |
89 } | |
90 | |
91 resp_msg = gsm0480_msgb_alloc_name(__func__); | |
92 OSMO_ASSERT(resp_msg); | |
93 osmo_gsup_encode(resp_msg, &resp); | |
94 msgb_free(ss_msg); | |
95 return osmo_gsup_client_send(gsupc, resp_msg); | |
96 } | |
97 | |
98 /*! send a SS/USSD reject to a given IMSI/session. | |
99 * \param[in] gsupc GSUP client connection through which to send | |
100 * \param[in] imsi IMSI of the subscriber | |
101 * \param[in] session_id Unique identifier of SS session for which this response is | |
102 * \param[in] invoke_id InvokeID of the request | |
103 * \param[in] problem_tag Problem code tag (table 3.13) | |
104 * \param[in] problem_code Problem code (table 3.14-3.17) | |
105 */ | |
106 static int euse_tx_ussd_reject(struct osmo_gsup_client *gsupc, const char *imsi, uint32_t session_id, | |
107 int invoke_id, uint8_t problem_tag, uint8_t problem_code) | |
108 { | |
109 struct msgb *msg = gsm0480_gen_reject(invoke_id, problem_tag, problem_code); | |
110 LOGP(DMAIN, LOGL_NOTICE, "Tx %s/0x%08x: Reject(%d, 0x%02x, 0x%02x)\n", imsi, session_id, | |
111 invoke_id, problem_tag, problem_code); | |
112 OSMO_ASSERT(msg); | |
113 return euse_tx_ss(gsupc, imsi, session_id, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg); | |
114 } | |
115 | |
116 /*! send a SS/USSD response in 7-bit GSM default alphabet o a given imsi/session. | |
117 * \param[in] gsupc GSUP client connection through which to send | |
118 * \param[in] imsi IMSI of the subscriber | |
119 * \param[in] session_id Unique identifier of SS session for which this response is | |
120 * \param[in] final Is this the final result (true=END) or an intermediate result | |
121 * (false=CONTINUE) | |
122 * \param[in] invoke_id InvokeID of the request | |
123 */ | |
124 static int euse_tx_ussd_resp_7bit(struct osmo_gsup_client *gsupc, const char *imsi, uint32_t session_id, | |
125 bool final, uint8_t invoke_id, const char *text) | |
126 { | |
127 struct msgb *ss_msg; | |
128 | |
129 /* encode response; remove L3 header */ | |
130 ss_msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text); | |
131 LOGP(DMAIN, LOGL_DEBUG, "Tx %s/0x%08x: USSD Result(%d, %s, '%s')\n", imsi, session_id, | |
132 invoke_id, final ? "END" : "CONTINUE", text); | |
133 OSMO_ASSERT(ss_msg); | |
134 return euse_tx_ss(gsupc, imsi, session_id, OSMO_GSUP_MSGT_PROC_SS_RESULT, final, ss_msg); | |
135 } | |
136 | |
137 static int euse_rx_proc_ss_req(struct osmo_gsup_client *gsupc, const struct osmo_gsup_message *gsup) | |
138 { | |
139 char buf[GSM0480_USSD_7BIT_STRING_LEN+1]; | |
140 struct ss_request req = {0}; | |
141 | |
142 if (gsup->ss_info && gsup->ss_info_len) { | |
143 if (gsm0480_parse_facility_ie(gsup->ss_info, gsup->ss_info_len, &req)) { | |
144 return euse_tx_ussd_reject(gsupc, gsup->imsi, gsup->session_id, -1, | |
145 GSM_0480_PROBLEM_CODE_TAG_GENERAL, | |
146 GSM_0480_GEN_PROB_CODE_BAD_STRUCTURE); | |
147 } | |
148 } | |
149 | |
150 LOGP(DMAIN, LOGL_INFO, "Rx %s/0x%08x: USSD SessionState=%s, OpCode=%s, '%s'\n", gsup->imsi, | |
151 gsup->session_id, osmo_gsup_session_state_name(gsup->session_state), | |
152 gsm0480_op_code_name(req.opcode), req.ussd_text); | |
153 | |
154 /* we only handle single-request-response USSD in this demo */ | |
155 if (gsup->session_state != OSMO_GSUP_SESSION_STATE_BEGIN) { | |
156 return euse_tx_ussd_reject(gsupc, gsup->imsi, gsup->session_id, req.invoke_id, | |
157 GSM_0480_PROBLEM_CODE_TAG_GENERAL, | |
158 GSM_0480_GEN_PROB_CODE_UNRECOGNISED); | |
159 } | |
160 | |
161 snprintf(buf, sizeof(buf), "You sent \"%s\"", req.ussd_text); | |
162 return euse_tx_ussd_resp_7bit(gsupc, gsup->imsi, gsup->session_id, true, req.invoke_id, buf); | |
163 } | |
164 | |
165 static int gsupc_read_cb(struct osmo_gsup_client *gsupc, struct msgb *msg) | |
166 { | |
167 struct osmo_gsup_message gsup_msg = {0}; | |
168 int rc; | |
169 | |
170 rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup_msg); | |
171 if (rc < 0) { | |
172 LOGP(DMAIN, LOGL_ERROR, "Error decoding GSUP: %s\n", msgb_hexdump(msg)); | |
173 return rc; | |
174 } | |
175 DEBUGP(DMAIN, "Rx GSUP %s: %s\n", osmo_gsup_message_type_name(gsup_msg.message_type), | |
176 msgb_hexdump(msg)); | |
177 | |
178 switch (gsup_msg.message_type) { | |
179 case OSMO_GSUP_MSGT_PROC_SS_REQUEST: | |
180 case OSMO_GSUP_MSGT_PROC_SS_RESULT: | |
181 euse_rx_proc_ss_req(gsupc, &gsup_msg); | |
182 break; | |
183 case OSMO_GSUP_MSGT_PROC_SS_ERROR: | |
184 break; | |
185 default: | |
186 LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %s\n", | |
187 osmo_gsup_message_type_name(gsup_msg.message_type)); | |
188 break; | |
189 } | |
190 | |
191 msgb_free(msg); | |
192 return 0; | |
193 } | |
194 | |
195 | |
196 static struct log_info_cat default_categories[] = { | |
197 [DMAIN] = { | |
198 .name = "DMAIN", | |
199 .description = "Main Program", | |
200 .enabled = 1, .loglevel = LOGL_DEBUG, | |
201 }, | |
202 }; | |
203 | |
204 static const struct log_info gsup_log_info = { | |
205 .cat = default_categories, | |
206 .num_cat = ARRAY_SIZE(default_categories), | |
207 }; | |
208 | |
209 static void print_usage(void) | |
210 { | |
211 printf("Usage: osmo-euse-demo [hlr-ip [hlr-gsup-port]]\n"); | |
212 } | |
213 | |
214 int main(int argc, char **argv) | |
215 { | |
216 char *server_host = "127.0.0.1"; | |
217 uint16_t server_port = OSMO_GSUP_PORT; | |
218 void *ctx = talloc_named_const(NULL, 0, "demo-euse"); | |
219 | |
220 osmo_init_logging2(ctx, &gsup_log_info); | |
221 | |
222 printf("argc=%d\n", argc); | |
223 | |
224 if (argc > 1) { | |
225 if (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { | |
226 print_usage(); | |
227 exit(0); | |
228 } else | |
229 server_host = argv[1]; | |
230 } | |
231 if (argc > 2) | |
232 server_port = atoi(argv[2]); | |
233 | |
234 g_gc = osmo_gsup_client_create(ctx, "EUSE-foobar", server_host, server_port, gsupc_read_cb, NULL); | |
235 | |
236 while (1) { | |
237 osmo_select_main(0); | |
238 } | |
239 | |
240 exit(0); | |
241 } | |
242 |