346 lines
10 KiB
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;
|
|
}
|
|
|