/* common process * * (C) 2020 by Andreas Eversberg * 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 . */ #include #include #include #include #include #include #include #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; }