osmo-cc-ss5-endpoint/src/common/common.c

346 lines
10 KiB
C

/* common process
*
* (C) 2020 by Andreas Eversberg <jolly@eversberg.eu>
* 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <sys/types.h>
#include "../libdebug/debug.h"
#include "../libtimer/timer.h"
#include "../libselect/select.h"
#include "../libosmocc/endpoint.h"
#include "../libosmocc/helper.h"
#include "common.h"
/*
* generate messages towards CC
*/
void reject_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, uint8_t isdn_cause)
{
osmo_cc_msg_t *new_msg;
/* create osmo-cc message */
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REJ_IND);
/* cause */
osmo_cc_add_ie_cause(new_msg, OSMO_CC_LOCATION_BEYOND_INTERWORKING, isdn_cause, 0, 0);
/* send message to osmo-cc */
osmo_cc_ll_msg(cc_ep, callref, new_msg);
}
void release_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, uint8_t isdn_cause)
{
osmo_cc_msg_t *new_msg;
/* create osmo-cc message */
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
/* cause */
osmo_cc_add_ie_cause(new_msg, OSMO_CC_LOCATION_BEYOND_INTERWORKING, isdn_cause, 0, 0);
/* send message to osmo-cc */
osmo_cc_ll_msg(cc_ep, callref, new_msg);
}
void disconnect_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, uint8_t isdn_cause)
{
osmo_cc_msg_t *new_msg;
/* create osmo-cc message */
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_DISC_IND);
/* progress */
osmo_cc_add_ie_progress(new_msg, OSMO_CC_CODING_ITU_T, OSMO_CC_LOCATION_BEYOND_INTERWORKING, OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE);
/* cause */
osmo_cc_add_ie_cause(new_msg, OSMO_CC_LOCATION_BEYOND_INTERWORKING, isdn_cause, 0, 0);
/* send message to osmo-cc */
osmo_cc_ll_msg(cc_ep, callref, new_msg);
}
void proceed_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, const char *sdp)
{
osmo_cc_msg_t *new_msg;
/* create osmo-cc message */
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_PROC_IND);
/* progress */
osmo_cc_add_ie_progress(new_msg, OSMO_CC_CODING_ITU_T, OSMO_CC_LOCATION_BEYOND_INTERWORKING, OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE);
/* sdp */
osmo_cc_add_ie_sdp(new_msg, sdp);
/* send message to osmo-cc */
osmo_cc_ll_msg(cc_ep, callref, new_msg);
}
void alert_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref)
{
osmo_cc_msg_t *new_msg;
/* create osmo-cc message */
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_ALERT_IND);
/* send message to osmo-cc */
osmo_cc_ll_msg(cc_ep, callref, new_msg);
}
void answer_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref)
{
osmo_cc_msg_t *new_msg;
/* create osmo-cc message */
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_CNF);
/* send message to osmo-cc */
osmo_cc_ll_msg(cc_ep, callref, new_msg);
}
void setup_comp_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref)
{
osmo_cc_msg_t *new_msg;
/* create osmo-cc message */
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_COMP_IND);
/* send message to osmo-cc */
osmo_cc_ll_msg(cc_ep, callref, new_msg);
}
/*
* dial string generation and parsing
*/
static char *prefix_1_digit[] = { "1", "7", NULL };
static char *prefix_2_digit[] = {
"20", "27", "28", "30", "31", "32", "33", "34", "36", "39", "40", "41",
"43", "44", "45", "46", "47", "48", "49", "51", "52", "53", "54", "55",
"56", "57", "58", "60", "61", "62", "63", "64", "65", "66", "81", "82",
"83", "84", "86", "89", "90", "91", "92", "93", "94", "95", "98",
NULL };
/* use number and number type to generate an SS5 dial string
* the digits are checked if they can be dialed
* if the number is already in SS5 format, only digits are checked
*/
int generate_dial_string(uint8_t type, const char *dialing, char *string, int string_size)
{
int full_string_given = 0;
int i, ii;
if ((int)strlen(dialing) + 4 > string_size) {
PDEBUG(DSS5, DEBUG_NOTICE, "Dial string is too long for our digit register, call is rejected!\n");
return -EINVAL;
}
/* check for correct digits */
for (i = 0, ii = strlen(dialing); i < ii; i++) {
/* string may start with 'a' or 'b', but then 'c' must be the last digit */
if (dialing[i] == 'a' || dialing[i] == 'b') {
full_string_given = 1;
if (dialing[ii - 1] != 'c') {
PDEBUG(DSS5, DEBUG_NOTICE, "Number starts with 'a' (KP1) or 'b' (KP2) but missing 'c' (ST) at the end, call is rejected!\n");
return -EINVAL;
}
/* remove check of last digit 'c' */
--ii;
continue;
}
/* string must only consist of numerical digits and '*' (code 11) and '#' (code 12) */
if (!strchr("0123456789*#", dialing[i])) {
PDEBUG(DSS5, DEBUG_NOTICE, "Number has invalid digits, call is rejected!\n");
return -EINVAL;
}
}
/* if full string with 'a'/'b' and 'c' is given, we have complete dial string */
if (full_string_given) {
strcpy(string, dialing);
return 0;
}
/* if number is not of international type, create national dial string */
if (type != OSMO_CC_TYPE_INTERNATIONAL) {
// make GCC happy
strcpy(string, "a0");
strcat(string, dialing);
strcat(string, "c");
return 0;
}
/* check international prefix with length of 1 digit */
if ((int)strlen(dialing) < 1) {
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
return -EINVAL;
}
for (i = 0; prefix_1_digit[i]; i++) {
if (prefix_1_digit[i][0] == dialing[0])
break;
}
/* if number is of international type, create international dial string */
if (prefix_1_digit[i]) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-overflow"
sprintf(string, "b%c0%sc", dialing[0], dialing + 1);
#pragma GCC diagnostic pop
return 0;
}
/* check international prefix with length of 2 digits */
if ((int)strlen(dialing) < 2) {
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
return -EINVAL;
}
for (i = 0; prefix_2_digit[i]; i++) {
if (prefix_2_digit[i][0] == dialing[0]
&& prefix_2_digit[i][1] == dialing[1])
break;
}
/* if number is of international type, create international dial string */
if (prefix_2_digit[i]) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-overflow"
sprintf(string, "b%c%c0%sc", dialing[0], dialing[1], dialing + 2);
#pragma GCC diagnostic pop
return 0;
}
/* check international prefix with length of 3 digits */
if ((int)strlen(dialing) < 3) {
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
return -EINVAL;
}
/* if number is of international type, create international dial string */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-overflow"
sprintf(string, "b%c%c%c0%sc", dialing[0], dialing[1], dialing[2], dialing + 3);
#pragma GCC diagnostic pop
return 0;
}
/* parse received SS5 dial string and convert it into a national or international number */
int parse_dial_string(uint8_t *type, char *dialing, int dialing_size, const char *string)
{
char kp_digit;
const char *prefix;
int length;
int i;
/* remove start and stop digits, set string after start digit and set length to digits between start and stop */
if (string[0] != 'a' && string[0] != 'b') {
PDEBUG(DSS5, DEBUG_NOTICE, "Received digits do not start with 'a' (KP1) nor 'b' (KP2), call is rejected!\n");
return -EINVAL;
}
length = strlen(string) - 1;
if (string[length] != 'c') {
PDEBUG(DSS5, DEBUG_NOTICE, "Received digits do end with 'c' (ST), call is rejected!\n");
return -EINVAL;
}
kp_digit = *string++;
length--;
if (length > dialing_size - 1) {
PDEBUG(DSS5, DEBUG_NOTICE, "Received dial string is too long, call is rejected!\n");
return -EINVAL;
}
if (!length) {
PDEBUG(DSS5, DEBUG_NOTICE, "Nothing dialed between KP and ST, call is rejected!\n");
return -EINVAL;
}
/* received local or national call */
if (kp_digit == 'a') {
/* remove discriminaing digit */
if (string[0] == '0') {
string++;
length--;
*type = OSMO_CC_TYPE_NATIONAL;
} else
*type = OSMO_CC_TYPE_UNKNOWN;
strncpy(dialing, string, length);
dialing[length] = '\0';
return 0;
}
/* check international prefix with length of 1 digit */
if (length < 2) {
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
return -EINVAL;
}
for (i = 0; prefix_1_digit[i]; i++) {
if (prefix_1_digit[i][0] == string[0])
break;
}
/* if number is of international type, create international dial string */
if (prefix_1_digit[i]) {
prefix = string;
string += 1;
length -= 1;
/* remove discriminaing digit */
string++;
--length;
*type = OSMO_CC_TYPE_INTERNATIONAL;
dialing[0] = prefix[0];
strncpy(dialing + 1, string, length);
dialing[1 + length] = '\0';
return 0;
}
/* check international prefix with length of 2 digits */
if (length < 3) {
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
return -EINVAL;
}
for (i = 0; prefix_2_digit[i]; i++) {
if (prefix_2_digit[i][0] == string[0]
&& prefix_2_digit[i][1] == string[1])
break;
}
/* if number is of international type, create international dial string */
if (prefix_2_digit[i]) {
prefix = string;
string += 2;
length -= 2;
/* remove discriminaing digit */
string++;
--length;
*type = OSMO_CC_TYPE_INTERNATIONAL;
dialing[0] = prefix[0];
dialing[1] = prefix[1];
strncpy(dialing + 2, string, length);
dialing[2 + length] = '\0';
return 0;
}
/* check international prefix with length of 3 digits */
if (length < 4) {
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
return -EINVAL;
}
/* if number is of international type, create international dial string */
prefix = string;
string += 3;
length -= 3;
/* remove discriminaing digit */
string++;
--length;
*type = OSMO_CC_TYPE_INTERNATIONAL;
dialing[0] = prefix[0];
dialing[1] = prefix[1];
dialing[2] = prefix[2];
strncpy(dialing + 3, string, length);
dialing[3 + length] = '\0';
return 0;
}