/* Network-specific handling of mobile-originated USSDs. */ /* (C) 2008-2009 by Harald Welte * (C) 2008, 2009 by Holger Hans Peter Freyther * (C) 2009 by Mike Haben * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ /* This module defines the network-specific handling of mobile-originated USSD messages. */ #include #include #include #include #include #include #include #include #include #include #include /* FIXME: choose a proper range */ static uint32_t new_callref = 0x20000001; /* Declarations of USSD strings to be recognised */ const char USSD_TEXT_OWN_NUMBER[] = "*#100#"; /* A network-specific handler function */ static int send_own_number(struct gsm_subscriber_connection *conn, const struct ss_request *req) { char *own_number = conn->vsub->msisdn; char response_string[GSM_EXTENSION_LENGTH + 20]; DEBUGP(DMM, "%s: MSISDN = %s\n", vlr_subscr_name(conn->vsub), own_number); /* Need trailing CR as EOT character */ snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number); return gsm0480_send_ussd_response(conn, response_string, req); } /* Entrypoint - handler function common to all mobile-originated USSDs */ int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); struct gsm_trans *trans; struct ss_request req; uint8_t pdisc, tid; uint8_t msg_type; int rc; pdisc = gsm48_hdr_pdisc(gh); msg_type = gsm48_hdr_msg_type(gh); tid = gsm48_hdr_trans_id_flip_ti(gh); /* Associate logging messages with this subscriber */ log_set_context(LOG_CTX_VLR_SUBSCR, conn->vsub); DEBUGP(DMM, "Received SS/USSD data (trans_id=%x, msg_type=%s)\n", tid, gsm48_pdisc_msgtype_name(pdisc, msg_type)); /* Reuse existing transaction, or create a new one */ trans = trans_find_by_id(conn, pdisc, tid); if (!trans) { /** * According to GSM TS 04.80, section 2.4.2 "Register * (mobile station to network direction)", the REGISTER * message is sent by the mobile station to the network * to assign a new transaction identifier for call independent * supplementary service control and to request or acknowledge * a supplementary service. */ if (msg_type != GSM0480_MTYPE_REGISTER) { LOGP(DMM, LOGL_ERROR, "Unexpected message (msg_type=%s), " "transaction is not allocated yet\n", gsm48_pdisc_msgtype_name(pdisc, msg_type)); gsm0480_send_ussd_reject(conn, &req, GSM_0480_PROBLEM_CODE_TAG_GENERAL, GSM_0480_GEN_PROB_CODE_UNRECOGNISED); return -EINVAL; } DEBUGP(DMM, " -> (new transaction)\n"); trans = trans_alloc(conn->network, conn->vsub, pdisc, tid, new_callref++); if (!trans) { DEBUGP(DMM, " -> No memory for trans\n"); gsm0480_send_ussd_return_error(conn, &req, GSM0480_ERR_CODE_SYSTEM_FAILURE); return -ENOMEM; } trans->conn = msc_subscr_conn_get(conn, MSC_CONN_USE_TRANS_NC_SS); trans->dlci = OMSC_LINKID_CB(msg); cm_service_request_concludes(conn, msg); } memset(&req, 0, sizeof(req)); rc = gsm0480_decode_ss_request(gh, msgb_l3len(msg), &req); if (!rc) { LOGP(DMM, LOGL_ERROR, "SS/USSD message parsing error, " "rejecting request...\n"); gsm0480_send_ussd_reject(conn, &req, GSM_0480_PROBLEM_CODE_TAG_GENERAL, GSM_0480_GEN_PROB_CODE_UNRECOGNISED); /* The GSM 04.80 API uses inverted codes (0 means error) */ return -EPROTO; } /* Interrogation or releaseComplete? */ if (req.ussd_text[0] == '\0' || req.ussd_text[0] == 0xFF) { if (req.ss_code > 0) { /* Assume interrogateSS or modification of it and reject */ return gsm0480_send_ussd_return_error(conn, &req, GSM0480_ERR_CODE_ILLEGAL_SS_OPERATION); } /* Still assuming a Release-Complete and returning */ return 0; } msc_subscr_conn_communicating(conn); if (!strcmp(USSD_TEXT_OWN_NUMBER, (const char *)req.ussd_text)) { DEBUGP(DMM, "USSD: Own number requested\n"); rc = send_own_number(conn, &req); } else { DEBUGP(DMM, "Unhandled USSD %s\n", req.ussd_text); rc = gsm0480_send_ussd_return_error(conn, &req, GSM0480_ERR_CODE_UNEXPECTED_DATA_VALUE); } /** * TODO: as we only handle *#100# for now, and always * respond with RELEASE COMPLETE, let's manually free * the transaction here, until the external interface * is implemented. */ trans_free(trans); return rc; }