/* OSMO-CC Processing: convert causes * * (C) 2019 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 "message.h" #include "cause.h" /* stolen from freeswitch */ /* map sip responses to QSIG cause codes ala RFC4497 section 8.4.4 */ static uint8_t status2isdn_cause(uint16_t status) { switch (status) { case 200: return 16; //SWITCH_CAUSE_NORMAL_CLEARING; case 401: case 402: case 403: case 407: case 603: return 21; //SWITCH_CAUSE_CALL_REJECTED; case 404: return 1; //SWITCH_CAUSE_UNALLOCATED_NUMBER; case 485: case 604: return 3; //SWITCH_CAUSE_NO_ROUTE_DESTINATION; case 408: case 504: return 102; //SWITCH_CAUSE_RECOVERY_ON_TIMER_EXPIRE; case 410: return 22; //SWITCH_CAUSE_NUMBER_CHANGED; case 413: case 414: case 416: case 420: case 421: case 423: case 505: case 513: return 127; //SWITCH_CAUSE_INTERWORKING; case 480: return 180; //SWITCH_CAUSE_NO_USER_RESPONSE; case 400: case 481: case 500: case 503: return 41; //SWITCH_CAUSE_NORMAL_TEMPORARY_FAILURE; case 486: case 600: return 17; //SWITCH_CAUSE_USER_BUSY; case 484: return 28; //SWITCH_CAUSE_INVALID_NUMBER_FORMAT; case 488: case 606: return 88; //SWITCH_CAUSE_INCOMPATIBLE_DESTINATION; case 502: return 38; //SWITCH_CAUSE_NETWORK_OUT_OF_ORDER; case 405: return 63; //SWITCH_CAUSE_SERVICE_UNAVAILABLE; case 406: case 415: case 501: return 79; //SWITCH_CAUSE_SERVICE_NOT_IMPLEMENTED; case 482: case 483: return 25; //SWITCH_CAUSE_EXCHANGE_ROUTING_ERROR; case 487: return 31; //??? SWITCH_CAUSE_ORIGINATOR_CANCEL; default: return 31; //SWITCH_CAUSE_NORMAL_UNSPECIFIED; } } static uint16_t isdn2status_cause(uint8_t cause, uint8_t location) { switch (cause) { case 1: return 404; case 2: return 404; case 3: return 404; case 17: return 486; case 18: return 408; case 19: return 480; case 20: return 480; case 21: if (location == OSMO_CC_LOCATION_USER) return 603; return 403; case 22: //return 301; return 410; case 23: return 410; case 26: return 404; case 27: return 502; case 28: return 484; case 29: return 501; case 31: return 480; case 34: return 503; case 38: return 503; case 41: return 503; case 42: return 503; case 47: return 503; case 55: return 403; case 57: return 403; case 58: return 503; case 65: return 488; case 69: return 501; case 70: return 488; case 79: return 501; case 87: return 403; case 88: return 503; case 102: return 504; case 111: return 500; case 127: return 500; default: return 468; } } static uint8_t socket2isdn_cause(uint8_t sock) { switch (sock) { case OSMO_CC_SOCKET_CAUSE_FAILED: return 47; case OSMO_CC_SOCKET_CAUSE_BROKEN_PIPE: return 41; case OSMO_CC_SOCKET_CAUSE_VERSION_MISMATCH: return 38; case OSMO_CC_SOCKET_CAUSE_TIMEOUT: return 41; default: return 31; } } void osmo_cc_convert_cause(struct osmo_cc_ie_cause *cause) { /* complete cause, from socket cause */ if (cause->socket_cause && cause->isdn_cause == 0 && ntohs(cause->sip_cause_networkorder) == 0) cause->isdn_cause = socket2isdn_cause(cause->socket_cause); /* convert ISDN cause to SIP cause */ if (cause->isdn_cause && ntohs(cause->sip_cause_networkorder) == 0) { cause->sip_cause_networkorder = htons(isdn2status_cause(cause->isdn_cause, cause->location)); } /* convert SIP cause to ISDN cause */ if (ntohs(cause->sip_cause_networkorder) && cause->isdn_cause == 0) { cause->isdn_cause = status2isdn_cause(ntohs(cause->sip_cause_networkorder)); } /* no cause at all: use Normal Call Clearing */ if (cause->isdn_cause == 0 && ntohs(cause->sip_cause_networkorder) == 0) { cause->isdn_cause = OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR; cause->sip_cause_networkorder = htons(486); } } void osmo_cc_convert_cause_msg(osmo_cc_msg_t *msg) { void *ie; uint8_t type; uint16_t length; void *value; /* search for (all) cause IE and convert the values, if needed */ ie = msg->data; while ((value = osmo_cc_msg_sep_ie(msg, &ie, &type, &length))) { if (type == OSMO_CC_IE_CAUSE && length >= sizeof(struct osmo_cc_ie_cause)) { osmo_cc_convert_cause(value); } } } uint8_t osmo_cc_collect_cause(uint8_t old_cause, uint8_t new_cause) { /* first cause */ if (old_cause == 0) return new_cause; /* first prio: return 17 */ if (old_cause == OSMO_CC_ISDN_CAUSE_USER_BUSY || new_cause == OSMO_CC_ISDN_CAUSE_USER_BUSY) return OSMO_CC_ISDN_CAUSE_USER_BUSY; /* second prio: return 21 */ if (old_cause == OSMO_CC_ISDN_CAUSE_CALL_REJECTED || new_cause == OSMO_CC_ISDN_CAUSE_CALL_REJECTED) return OSMO_CC_ISDN_CAUSE_CALL_REJECTED; /* third prio: return other than 88 and 18 (what ever was first) */ if (old_cause != OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST && old_cause != OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND) return old_cause; if (new_cause != OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST && new_cause != OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND) return new_cause; /* fourth prio: return 88 */ if (old_cause == OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST || new_cause == OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST) return OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST; /* fith prio: return 18 */ return OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND; }