osmo-msc/src/libmsc/gsm_09_11.c

160 lines
5.1 KiB
C

/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
* (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009 by Mike Haben <michael.haben@btinternet.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/**
* MSC-specific handling of call independent Supplementary
* Services messages (NC_SS) according to GSM TS 09.11
* "Signalling interworking for supplementary services".
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <osmocom/msc/gsm_04_80.h>
#include <osmocom/msc/gsm_subscriber.h>
#include <osmocom/msc/debug.h>
#include <osmocom/msc/osmo_msc.h>
#include <osmocom/msc/vlr.h>
#include <osmocom/msc/gsm_04_08.h>
#include <osmocom/msc/transaction.h>
/* 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);
}
/* Entry point for call independent MO SS messages */
int gsm0911_rcv_nc_ss(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;
}