aboutsummaryrefslogtreecommitdiffstats
path: root/src/libosmocc/cause.c
blob: 4a3a41efb44418c7178bf694e5ecd7ee77f171b2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/* OSMO-CC Processing: convert causes
 *
 * (C) 2019 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 <stdint.h>
#include <arpa/inet.h>
#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;
}