parent
59119f380f
commit
ad8a7be345
@ -0,0 +1,15 @@ |
||||
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||
|
||||
noinst_LIBRARIES = libosmocc.a
|
||||
|
||||
libosmocc_a_SOURCES = \
|
||||
message.c \
|
||||
socket.c \
|
||||
cause.c \
|
||||
screen.c \
|
||||
endpoint.c \
|
||||
session.c \
|
||||
sdp.c \
|
||||
rtp.c \
|
||||
helper.c
|
||||
|
@ -0,0 +1,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; |
||||
} |
||||
|
@ -0,0 +1,5 @@ |
||||
|
||||
void osmo_cc_convert_cause(struct osmo_cc_ie_cause *cause); |
||||
void osmo_cc_convert_cause_msg(osmo_cc_msg_t *msg); |
||||
uint8_t osmo_cc_collect_cause(uint8_t old_cause, uint8_t new_cause); |
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,128 @@ |
||||
#ifndef OSMO_CC_ENDPOINT_H |
||||
#define OSMO_CC_ENDPOINT_H |
||||
|
||||
#include "message.h" |
||||
#include "socket.h" |
||||
#include "cause.h" |
||||
|
||||
/* special osmo-cc error codes */ |
||||
#define OSMO_CC_RC_SEE_ERRNO -1 |
||||
#define OSMO_CC_RC_VERSION_MISMATCH 1 |
||||
|
||||
#define OSMO_CC_ATTACH_TIMER 2 |
||||
|
||||
/* call control state */ |
||||
enum osmo_cc_state { |
||||
OSMO_CC_STATE_IDLE = 0, |
||||
/* call states */ |
||||
OSMO_CC_STATE_INIT_OUT, /* outgoing CC-SETUP-REQ sent */ |
||||
OSMO_CC_STATE_INIT_IN, /* incoming CC-SETUP-IND received */ |
||||
OSMO_CC_STATE_OVERLAP_OUT, /* received CC-SETUP-ACK-IND on outgoing call */ |
||||
OSMO_CC_STATE_OVERLAP_IN, /* sent CC-SETUP-ACK-REQ on incoming call */ |
||||
OSMO_CC_STATE_PROCEEDING_OUT, /* received CC-PROC-IND on outgoing call */ |
||||
OSMO_CC_STATE_PROCEEDING_IN, /* sent CC-PROC-REQ on incoming call */ |
||||
OSMO_CC_STATE_ALERTING_OUT, /* received CC-ALERT-IND on outgoing call */ |
||||
OSMO_CC_STATE_ALERTING_IN, /* sent CC-ALERT-REQ on incoming call */ |
||||
OSMO_CC_STATE_CONNECTING_OUT, /* received CC-SETUP-CNF on outgoing call */ |
||||
OSMO_CC_STATE_CONNECTING_IN, /* sent CC-SETUP-RSP on incoming call */ |
||||
OSMO_CC_STATE_ACTIVE, /* received or sent CC-SETUP-COMPL-* */ |
||||
OSMO_CC_STATE_DISCONNECTING_OUT, /* sent CC-DISC-REQ */ |
||||
OSMO_CC_STATE_DISCONNECTING_IN, /* received CC-DISC-IND */ |
||||
OSMO_CC_STATE_DISC_COLLISION, /* received CC-DISC-IND after sending CC-DISC_REQ */ |
||||
OSMO_CC_STATE_RELEASING_OUT, /* sent CC-REL-REQ */ |
||||
/* attachment states */ |
||||
OSMO_CC_STATE_ATTACH_SENT, /* outgoing CC-ATT-REQ sent to socket */ |
||||
OSMO_CC_STATE_ATTACH_OUT, /* received CC-ATT-RSP on outgoing socket */ |
||||
OSMO_CC_STATE_ATTACH_WAIT, /* wait for outgoing attachment after failure */ |
||||
OSMO_CC_STATE_ATTACH_IN, /* incoming CC-ATT-REQ received from socket*/ |
||||
}; |
||||
|
||||
/* sample type */ |
||||
typedef int16_t osmo_cc_sample_t; |
||||
|
||||
#define OSMO_CC_SAMPLE_MILLIWATT 23170 /* peak sine at -3 dB of full sample range */ |
||||
#define OSMO_CC_SAMPLE_SPEECH 3672 /* peak speech at -16 dB of milliwatt */ |
||||
#define OSMO_CC_SAMPLE_MIN -32768 /* lowest level */ |
||||
#define OSMO_CC_SAMPLE_MAX 32767 /* highest level */ |
||||
|
||||
struct osmo_cc_call; |
||||
|
||||
typedef struct osmo_cc_screen_list { |
||||
struct osmo_cc_screen_list *next; |
||||
int has_from_type; |
||||
uint8_t from_type; |
||||
int has_from_present; |
||||
uint8_t from_present; |
||||
char from[128]; |
||||
int has_to_type; |
||||
uint8_t to_type; |
||||
int has_to_present; |
||||
uint8_t to_present; |
||||
char to[128]; |
||||
} osmo_cc_screen_list_t; |
||||
|
||||
/* endpoint instance */ |
||||
typedef struct osmo_cc_endpoint { |
||||
struct osmo_cc_endpoint *next; |
||||
void *priv; |
||||
void (*ll_msg_cb)(struct osmo_cc_endpoint *ep, uint32_t callref, osmo_cc_msg_t *msg); |
||||
void (*ul_msg_cb)(struct osmo_cc_call *call, osmo_cc_msg_t *msg); |
||||
osmo_cc_msg_list_t *ll_queue; /* messages towards lower layer */ |
||||
struct osmo_cc_call *call_list; |
||||
const char *local_name; /* name of interface */ |
||||
const char *local_address; /* host+port */ |
||||
const char *local_host; |
||||
uint16_t local_port; |
||||
const char *remote_address; /* host+port */ |
||||
const char *remote_host; |
||||
uint16_t remote_port; |
||||
uint8_t serving_location; |
||||
osmo_cc_socket_t os; |
||||
osmo_cc_screen_list_t *screen_calling_in; |
||||
osmo_cc_screen_list_t *screen_called_in; |
||||
osmo_cc_screen_list_t *screen_calling_out; |
||||
osmo_cc_screen_list_t *screen_called_out; |
||||
int remote_auto; /* automatic remote address */ |
||||
struct timer attach_timer; /* timer to retry attachment */ |
||||
} osmo_cc_endpoint_t; |
||||
|
||||
extern osmo_cc_endpoint_t *osmo_cc_endpoint_list; |
||||
|
||||
/* call process */ |
||||
typedef struct osmo_cc_call { |
||||
struct osmo_cc_call *next; |
||||
osmo_cc_endpoint_t *ep; |
||||
enum osmo_cc_state state; |
||||
int lower_layer_released; /* when lower layer sent release, while upper layer gets a disconnect */ |
||||
int upper_layer_released; /* when upper layer sent release, while lower layer gets a disconnect */ |
||||
uint32_t callref; |
||||
osmo_cc_msg_list_t *sock_queue; /* messages from socket */ |
||||
const char *attached_host; /* host and port from remote peer that attached to us */ |
||||
uint16_t attached_port; |
||||
const char *attached_name; /* interface name from remote peer that attached to us */ |
||||
} osmo_cc_call_t; |
||||
|
||||
/* returns 0 if ok
|
||||
* returns <0 for error as indicated |
||||
* returns >=1 to indicate osmo-cc error code |
||||
*/ |
||||
|
||||
void osmo_cc_help(void); |
||||
int osmo_cc_new(osmo_cc_endpoint_t *ep, const char *version, const char *name, uint8_t serving_location, void (*ll_msg_cb)(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg), void (*ul_msg_cb)(osmo_cc_call_t *call, osmo_cc_msg_t *msg), void *priv, int argc, const char *argv[]); |
||||
void osmo_cc_delete(struct osmo_cc_endpoint *ep); |
||||
int osmo_cc_handle(void); |
||||
osmo_cc_call_t *osmo_cc_call_by_callref(osmo_cc_endpoint_t *ep, uint32_t callref); |
||||
void osmo_cc_ll_msg(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg); |
||||
void osmo_cc_ul_msg(void *priv, uint32_t callref, osmo_cc_msg_t *msg); |
||||
osmo_cc_call_t *osmo_cc_call_new(osmo_cc_endpoint_t *ep); |
||||
void osmo_cc_call_delete(struct osmo_cc_call *call); |
||||
enum osmo_cc_session_addrtype osmo_cc_address_type(const char *address); |
||||
const char *osmo_cc_host_of_address(const char *address); |
||||
const char *osmo_cc_port_of_address(const char *address); |
||||
|
||||
#include "session.h" |
||||
#include "rtp.h" |
||||
#include "sdp.h" |
||||
#include "screen.h" |
||||
|
||||
#endif /* OSMO_CC_ENDPOINT_H */ |
@ -0,0 +1,171 @@ |
||||
/* Osmo-CC: helpers to simplify Osmo-CC usage
|
||||
* |
||||
* (C) 2016 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 <stdint.h> |
||||
#include <stdlib.h> |
||||
#include <errno.h> |
||||
#include <sys/time.h> |
||||
#include <inttypes.h> |
||||
#include "../libtimer/timer.h" |
||||
#include "../libdebug/debug.h" |
||||
#include "endpoint.h" |
||||
#include "helper.h" |
||||
|
||||
osmo_cc_session_t *osmo_cc_helper_audio_offer(void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug) |
||||
{ |
||||
osmo_cc_session_t *session; |
||||
osmo_cc_session_media_t *media; |
||||
const char *sdp; |
||||
int i; |
||||
|
||||
session = osmo_cc_new_session(priv, NULL, NULL, NULL, 0, 0, NULL, NULL, debug); |
||||
if (!session) |
||||
return NULL; |
||||
|
||||
media = osmo_cc_add_media(session, 0, 0, NULL, osmo_cc_session_media_type_audio, 0, osmo_cc_session_media_proto_rtp, 1, 1, receiver, debug); |
||||
osmo_cc_rtp_open(media); |
||||
|
||||
for (i = 0; codecs[i].payload_name; i++) |
||||
osmo_cc_add_codec(media, codecs[i].payload_name, codecs[i].payload_rate, codecs[i].payload_channels, codecs[i].encoder, codecs[i].decoder, debug); |
||||
|
||||
sdp = osmo_cc_session_send_offer(session); |
||||
osmo_cc_add_ie_sdp(msg, sdp); |
||||
|
||||
return session; |
||||
} |
||||
|
||||
const char *osmo_cc_helper_audio_accept(void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec) |
||||
{ |
||||
char offer_sdp[65536]; |
||||
const char *accept_sdp; |
||||
osmo_cc_session_media_t *media, *selected_media = NULL; |
||||
osmo_cc_session_codec_t *codec, *selected_codec = NULL; |
||||
int rc; |
||||
int i, selected_i; |
||||
|
||||
if (*session_p) { |
||||
PDEBUG(DCC, DEBUG_ERROR, "Session already set, please fix!\n"); |
||||
abort(); |
||||
} |
||||
if (*codec_p) { |
||||
PDEBUG(DCC, DEBUG_ERROR, "Codec already set, please fix!\n"); |
||||
abort(); |
||||
} |
||||
|
||||
/* SDP IE */ |
||||
rc = osmo_cc_get_ie_sdp(msg, 0, offer_sdp, sizeof(offer_sdp)); |
||||
if (rc < 0) { |
||||
PDEBUG(DCC, DEBUG_ERROR, "There is no SDP included in setup request.\n"); |
||||
return NULL; |
||||
} |
||||
|
||||
*session_p = osmo_cc_session_receive_offer(priv, offer_sdp); |
||||
if (!*session_p) { |
||||
PDEBUG(DCC, DEBUG_ERROR, "Failed to parse SDP.\n"); |
||||
return NULL; |
||||
} |
||||
|
||||
selected_i = -1; |
||||
osmo_cc_session_for_each_media((*session_p)->media_list, media) { |
||||
/* only audio */ |
||||
if (media->description.type != osmo_cc_session_media_type_audio) |
||||
continue; |
||||
osmo_cc_session_for_each_codec(media->codec_list, codec) { |
||||
for (i = 0; codecs[i].payload_name; i++) { |
||||
if (osmo_cc_session_if_codec(codec, codecs[i].payload_name, codecs[i].payload_rate, codecs[i].payload_channels)) { |
||||
/* select the first matchting codec or the one we prefer */ |
||||
if (selected_i < 0 || i < selected_i) { |
||||
selected_codec = codec; |
||||
selected_media = media; |
||||
selected_i = i; |
||||
} |
||||
/* if we don't force our preferred codec, use the preferred one from the remote */ |
||||
if (!force_our_codec) |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
if (!selected_codec) { |
||||
PDEBUG(DCC, DEBUG_ERROR, "No codec found in setup message that we support.\n"); |
||||
osmo_cc_free_session(*session_p); |
||||
return NULL; |
||||
} |
||||
osmo_cc_session_accept_codec(selected_codec, codecs[selected_i].encoder, codecs[selected_i].decoder); |
||||
osmo_cc_session_accept_media(selected_media, 0, 0, NULL, 1, 1, receiver); |
||||
osmo_cc_rtp_open(selected_media); |
||||
osmo_cc_rtp_connect(selected_media); |
||||
*codec_p = selected_codec; |
||||
|
||||
accept_sdp = osmo_cc_session_send_answer(*session_p); |
||||
if (!accept_sdp) { |
||||
osmo_cc_free_session(*session_p); |
||||
return NULL; |
||||
} |
||||
|
||||
return accept_sdp; |
||||
} |
||||
|
||||
int osmo_cc_helper_audio_negotiate(osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p) |
||||
{ |
||||
char sdp[65536]; |
||||
osmo_cc_session_media_t *media; |
||||
int rc; |
||||
|
||||
if (!(*session_p)) { |
||||
PDEBUG(DCC, DEBUG_ERROR, "Session not set, please fix!\n"); |
||||
abort(); |
||||
} |
||||
|
||||
/* once done, just ignore further messages that reply to setup */ |
||||
if (*codec_p) |
||||
return 0; |
||||
|
||||
/* SDP IE */ |
||||
rc = osmo_cc_get_ie_sdp(msg, 0, sdp, sizeof(sdp)); |
||||
if (rc < 0) |
||||
return 0; // no reply in this message
|
||||
|
||||
rc = osmo_cc_session_receive_answer(*session_p, sdp); |
||||
if (rc < 0) |
||||
return rc; |
||||
|
||||
osmo_cc_session_for_each_media((*session_p)->media_list, media) { |
||||
/* only audio */ |
||||
if (media->description.type != osmo_cc_session_media_type_audio) |
||||
continue; |
||||
/* select first codec, if one was accpeted */ |
||||
if (media->codec_list) |
||||
*codec_p = media->codec_list; |
||||
if (*codec_p) { |
||||
osmo_cc_rtp_connect(media); |
||||
/* no more media streams */ |
||||
break; |
||||
} |
||||
} |
||||
if (!(*codec_p)) { |
||||
PDEBUG(DCC, DEBUG_ERROR, "No codec found in setup reply message that we support.\n"); |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
@ -0,0 +1,13 @@ |
||||
|
||||
struct osmo_cc_helper_audio_codecs { |
||||
const char *payload_name; |
||||
uint32_t payload_rate; |
||||
int payload_channels; |
||||
void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len); |
||||
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len); |
||||
}; |
||||
|
||||
osmo_cc_session_t *osmo_cc_helper_audio_offer(void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug); |
||||
const char *osmo_cc_helper_audio_accept(void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec); |
||||
int osmo_cc_helper_audio_negotiate(osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p); |
||||
|
@ -0,0 +1,879 @@ |
||||
/* Osmo-CC: Message handling
|
||||
* |
||||
* (C) 2016 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 <stdlib.h> |
||||
#include <string.h> |
||||
#include <errno.h> |
||||
#include <arpa/inet.h> |
||||
#include "../libdebug/debug.h" |
||||
#include "message.h" |
||||
|
||||
static uint32_t new_callref = 0; |
||||
|
||||
uint32_t osmo_cc_new_callref(void) |
||||
{ |
||||
return (++new_callref); |
||||
} |
||||
|
||||
const char *osmo_cc_msg_name(uint8_t msg_type) |
||||
{ |
||||
switch (msg_type) { |
||||
case OSMO_CC_MSG_SETUP_REQ: |
||||
return "CC-SETUP-REQ"; |
||||
case OSMO_CC_MSG_SETUP_IND: |
||||
return "CC-SETUP-IND"; |
||||
case OSMO_CC_MSG_REJ_REQ: |
||||
return "CC-REJ-REQ"; |
||||
case OSMO_CC_MSG_REJ_IND: |
||||
return "CC-REJ-IND"; |
||||
case OSMO_CC_MSG_SETUP_ACK_REQ: |
||||
return "CC-SETUP-ACK-REQ"; |
||||
case OSMO_CC_MSG_SETUP_ACK_IND: |
||||
return "CC-SETUP-ACK-IND"; |
||||
case OSMO_CC_MSG_PROC_REQ: |
||||
return "CC-PROC-REQ"; |
||||
case OSMO_CC_MSG_PROC_IND: |
||||
return "CC-PROC-IND"; |
||||
case OSMO_CC_MSG_ALERT_REQ: |
||||
return "CC-ALERT-REQ"; |
||||
case OSMO_CC_MSG_ALERT_IND: |
||||
return "CC-ALERT-IND"; |
||||
case OSMO_CC_MSG_SETUP_RSP: |
||||
return "CC-SETUP-RSP"; |
||||
case OSMO_CC_MSG_SETUP_CNF: |
||||
return "CC-SETUP-CNF"; |
||||
case OSMO_CC_MSG_SETUP_COMP_REQ: |
||||
return "CC-SETUP-COMP-REQ"; |
||||
case OSMO_CC_MSG_SETUP_COMP_IND: |
||||
return "CC-SETUP-COMP-IND"; |
||||
case OSMO_CC_MSG_DISC_REQ: |
||||
return "CC-DISC-REQ"; |
||||
case OSMO_CC_MSG_DISC_IND: |
||||
return "CC-DISC-IND"; |
||||
case OSMO_CC_MSG_REL_REQ: |
||||
return "CC-REL-REQ"; |
||||
case OSMO_CC_MSG_REL_CNF: |
||||
return "CC-REL-CNF"; |
||||
case OSMO_CC_MSG_REL_IND: |
||||
return "CC-REL-IND"; |
||||
case OSMO_CC_MSG_PROGRESS_REQ: |
||||
return "CC-PROGRESS-REQ"; |
||||
case OSMO_CC_MSG_PROGRESS_IND: |
||||
return "CC-PROGRESS-IND"; |
||||
case OSMO_CC_MSG_NOTIFY_REQ: |
||||
return "CC-NOTIFY-REQ"; |
||||
case OSMO_CC_MSG_NOTIFY_IND: |
||||
return "CC-NOTIFY-IND"; |
||||
case OSMO_CC_MSG_INFO_REQ: |
||||
return "CC-INFO-REQ"; |
||||
case OSMO_CC_MSG_INFO_IND: |
||||
return "CC-INFO-IND"; |
||||
case OSMO_CC_MSG_ATTACH_REQ: |
||||
return "CC-ATTACH-REQ"; |
||||
case OSMO_CC_MSG_ATTACH_IND: |
||||
return "CC-ATTACH-IND"; |
||||
case OSMO_CC_MSG_ATTACH_RSP: |
||||
return "CC-ATTACH-RSP"; |
||||
case OSMO_CC_MSG_ATTACH_CNF: |
||||
return "CC-ATTACH-CNF"; |
||||
default: |
||||
return "<unknown>"; |
||||
} |
||||
} |
||||
|
||||
/* create message with maximum size */ |
||||
osmo_cc_msg_t *osmo_cc_new_msg(uint8_t msg_type) |
||||
{ |
||||
osmo_cc_msg_t *msg; |
||||
|
||||
/* allocate message */ |
||||
msg = calloc(1, sizeof(*msg) + 65535); |
||||
if (!msg) { |
||||
PDEBUG(DCC, DEBUG_ERROR, "No memory\n"); |
||||
abort(); |
||||
} |
||||
/* set message type and zero lentgh */ |
||||
msg->type = msg_type; |
||||
msg->length_networkorder = htons(0); |
||||
|
||||
return msg; |
||||
} |
||||
|
||||
/* clone message */ |
||||
osmo_cc_msg_t *osmo_cc_clone_msg(osmo_cc_msg_t *msg) |
||||
{ |
||||
osmo_cc_msg_t *new_msg; |
||||
|
||||
new_msg = osmo_cc_new_msg(msg->type); |
||||
new_msg->length_networkorder = msg->length_networkorder; |
||||
memcpy(new_msg->data, msg->data, ntohs(msg->length_networkorder)); |
||||
|
||||
return new_msg; |
||||
} |
||||
|
||||
osmo_cc_msg_t *osmo_cc_msg_list_dequeue(osmo_cc_msg_list_t **mlp, uint32_t *callref_p) |
||||
{ |
||||
osmo_cc_msg_list_t *ml; |
||||
osmo_cc_msg_t *msg; |
||||
|
||||
ml = *mlp; |
||||
msg = ml->msg; |
||||
if (callref_p) |
||||
*callref_p = ml->callref; |
||||
*mlp = ml->next; |
||||
free(ml); |
||||
|
||||
return msg; |
||||
} |
||||
|
||||
osmo_cc_msg_list_t *osmo_cc_msg_list_enqueue(osmo_cc_msg_list_t **mlp, osmo_cc_msg_t *msg, uint32_t callref) |
||||
{ |
||||
osmo_cc_msg_list_t *ml; |
||||
|
||||
ml = calloc(1, sizeof(*ml)); |
||||
ml->msg = msg; |
||||
ml->callref = callref; |
||||
while (*mlp) |
||||
mlp = &((*mlp)->next); |
||||
*mlp = ml; |
||||
|
||||
return ml; |
||||
} |
||||
|
||||
/* destroy message */ |
||||
void osmo_cc_free_msg(osmo_cc_msg_t *msg) |
||||
{ |
||||
free(msg); |
||||
} |
||||
|
||||
static void osmo_cc_debug_ie(osmo_cc_msg_t *msg, int level) |
||||
{ |
||||
uint16_t msg_len, len; |
||||
uint8_t *p; |
||||
osmo_cc_ie_t *ie; |
||||
|
||||
msg_len = ntohs(msg->length_networkorder); |
||||
p = msg->data; |
||||
|
||||
PDEBUG(DCC, level, "Debugging Message: type=0x%02x length=%d value=%s\n", msg->type, msg_len, debug_hex(p, msg_len)); |
||||
while (msg_len) { |
||||
ie = (osmo_cc_ie_t *)p; |
||||
/* check for minimum IE length */ |
||||
if (msg_len < sizeof(*ie)) { |
||||
PDEBUG(DCC, level, "****** Rest of message is too short for an IE: value=%s\n", debug_hex(p, msg_len)); |
||||
return; |
||||
} |
||||
/* get actual IE length */ |
||||
len = ntohs(ie->length_networkorder); |
||||
/* check if IE length does not exceed message */ |
||||
if (msg_len < sizeof(*ie) + len) { |
||||
PDEBUG(DCC, level, "****** IE: type=0x%02x length=%d would exceed the rest length of message (%d bytes left)\n", ie->type, len, msg_len - (int)sizeof(*ie)); |
||||
return; |
||||
} |
||||
PDEBUG(DCC, level, "IE: type=0x%02x length=%d value=%s\n", ie->type, len, debug_hex(ie->data, len)); |
||||
p += sizeof(*ie) + len; |
||||
msg_len -= sizeof(*ie) + len; |
||||
} |
||||
} |
||||
|
||||
/* search and return information element
|
||||
* we give the IE type we are searching for |
||||
* we also give the repetition, to find IE that is repeated |
||||
* the result is stored in *ie_data |
||||
* the return length is the length that exceeds the given ie_len |
||||
* if there is an error, a value < 0 is returned |
||||
*/ |
||||
int osmo_cc_get_ie_struct(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat, int ie_len, const osmo_cc_ie_t **ie_struct) |
||||
{ |
||||
uint16_t msg_len, len; |
||||
uint8_t *p; |
||||
osmo_cc_ie_t *ie; |
||||
|
||||
msg_len = ntohs(msg->length_networkorder); |
||||
p = msg->data; |
||||
|
||||
while (msg_len) { |
||||
ie = (osmo_cc_ie_t *)p; |
||||
/* check for minimum IE length */ |
||||
if (msg_len < sizeof(*ie)) { |
||||
PDEBUG(DCC, DEBUG_ERROR, "MSG short read\n"); |
||||
osmo_cc_debug_ie(msg, DEBUG_ERROR); |
||||
return -EINVAL; |
||||
} |
||||
/* get actual IE length */ |
||||
len = ntohs(ie->length_networkorder); |
||||
/* check if IE length does not exceed message */ |
||||
if (msg_len < sizeof(*ie) + len) { |
||||
PDEBUG(DCC, DEBUG_ERROR, "MSG short read\n"); |
||||
osmo_cc_debug_ie(msg, DEBUG_ERROR); |
||||
return -EINVAL; |
||||
} |
||||
/* check if IE matches the one that is searched for */ |
||||
if (ie->type != ie_type) { |
||||
p += sizeof(*ie) + len; |
||||
msg_len -= sizeof(*ie) + len; |
||||
continue; |
||||
} |
||||
/* check if IE repetition exists */ |
||||
if (ie_repeat) { |
||||
--ie_repeat; |
||||
p += sizeof(*ie) + len; |
||||
msg_len -= sizeof(*ie) + len; |
||||
continue; |
||||
} |
||||
/* return IE and indicate how many bytes we have more than the given length*/ |
||||
if (ntohs(ie->length_networkorder) < ie_len) { |
||||
PDEBUG(DCC, DEBUG_ERROR, "IE 0x%02d has length of %d, but we expect it to have at least %d!\n", ie_type, ntohs(ie->length_networkorder), ie_len); |
||||
return -EINVAL; |
||||
} |
||||
*ie_struct = ie; |
||||
return ntohs(ie->length_networkorder) - ie_len; |
||||
} |
||||
|
||||
/* IE not found */ |
||||
return -EINVAL; |
||||
} |
||||
|
||||
/* as above, but return data of IE only */ |
||||
int osmo_cc_get_ie_data(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat, int ie_len, const void **ie_data) |
||||
{ |
||||
const osmo_cc_ie_t *ie; |
||||
int rc; |
||||
|
||||
rc = osmo_cc_get_ie_struct(msg, ie_type, ie_repeat, ie_len, &ie); |
||||
if (rc >= 0) |
||||
*ie_data = ie->data; |
||||
|
||||
return rc; |
||||
} |
||||
|
||||
/* as above, but return 1 if IE exists */ |
||||
int osmo_cc_has_ie(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat) |
||||
{ |
||||
const osmo_cc_ie_t *ie; |
||||
int rc; |
||||
|
||||
rc = osmo_cc_get_ie_struct(msg, ie_type, ie_repeat, 0, &ie); |
||||
if (rc >= 0) |
||||
return 1; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/* remove IE from message */ |
||||
int osmo_cc_remove_ie(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat) |
||||
{ |
||||
const osmo_cc_ie_t *ie; |
||||
int rc; |
||||
int msg_len, before_ie, ie_size, after_ie; |
||||
|
||||
rc = osmo_cc_get_ie_struct(msg, ie_type, ie_repeat, 0, &ie); |
||||
if (rc < 0) |
||||
return rc; |
||||
|
||||
msg_len = ntohs(msg->length_networkorder); |
||||
before_ie = (void *)ie - (void *)msg->data; |
||||
ie_size = sizeof(*ie) + ntohs(ie->length_networkorder); |
||||
after_ie = msg_len - ie_size - before_ie; |
||||
if (after_ie) |
||||
memcpy(msg->data + before_ie, msg->data + before_ie + ie_size, after_ie); |
||||
msg->length_networkorder = htons(msg_len - ie_size); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/* add information element
|
||||
* the type is given by ie_type and length is given by ie_len |
||||
* the return value is a pointer to the data of the IE |
||||
*/ |
||||
void *osmo_cc_add_ie(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_len) |
||||
{ |
||||
uint16_t msg_len; |
||||
int new_msg_len; |
||||
uint8_t *p; |
||||
osmo_cc_ie_t *ie; |
||||
|
||||
/* get pointer to first IE, if any */ |
||||
p = msg->data; |
||||
/* expand messasge */ |
||||
msg_len = ntohs(msg->length_networkorder); |
||||
new_msg_len = msg_len + sizeof(*ie) + ie_len; |
||||
if (new_msg_len > 65535) { |
||||
PDEBUG(DCC, DEBUG_ERROR, "MSG overflow\n"); |
||||
return NULL; |
||||
} |
||||
msg->length_networkorder = htons(new_msg_len); |
||||
/* go to end of (unexpanded) message */ |
||||
ie = (osmo_cc_ie_t *)(p + msg_len); |
||||
/* add ie */ |
||||
ie->type = ie_type; |
||||
ie->length_networkorder = htons(ie_len); |
||||
memset(ie->data, 0, ie_len); /* just in case there is something, but it shouldn't */ |
||||
|
||||
return ie->data; |
||||
} |
||||
|
||||
/* gets the information element's data that *iep points to and returns that ie.
|
||||
* if *iep points to msg->data, the first IE's data is returned. (must be set before first call.) |
||||
* if *iep points to the end of the message, NULL is returned. |
||||
* if there is no next IE, *iep is set to point to the end of message. |
||||
*/ |
||||
void *osmo_cc_msg_sep_ie(osmo_cc_msg_t *msg, void **iep, uint8_t *ie_type, uint16_t *ie_length) |
||||
{ |
||||
uint16_t msg_len; |
||||
osmo_cc_ie_t *ie; |
||||
|
||||
/* in case that *iep points to start of message, make it point to first IE */ |
||||
if (*iep == msg) |
||||
*iep = msg->data; |
||||
/* case IE */ |
||||
ie = *iep; |
||||
/* check if it is NULL */ |
||||
if (ie == NULL) |
||||
return NULL; |
||||
/* check if it points to the end of message or there is not at least an IE header */ |
||||
msg_len = ntohs(msg->length_networkorder); |
||||
if ((int)((uint8_t *)ie - msg->data) > (int)(msg_len - sizeof(*ie))) |
||||
return NULL; |
||||
/* increment iep and return IE */ |
||||
*ie_type = ie->type; |
||||
*ie_length = ntohs(ie->length_networkorder); |
||||
*iep = (uint8_t *)ie + sizeof(*ie) + *ie_length; |
||||
return ie->data; |
||||
} |
||||
|
||||
/* copy given block to given string with given size */ |
||||
static void _ie2string(char *string, size_t string_size, const char *ie_string, int ie_size) |
||||
{ |
||||
int copy_size; |
||||
|
||||
copy_size = string_size - 1; |
||||
if (ie_size < copy_size) |
||||
copy_size = ie_size; |
||||
memcpy(string, ie_string, copy_size); |
||||
string[copy_size] = '\0'; |
||||
} |
||||
|
||||
/* helper to encode called party number (dialing) */ |
||||
void osmo_cc_add_ie_called(osmo_cc_msg_t *msg, uint8_t type, uint8_t plan, const char *dialing) |
||||
{ |
||||
struct osmo_cc_ie_called *ie_called; |
||||
|
||||
ie_called = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLED, sizeof(*ie_called) + strlen(dialing)); |
||||
ie_called->type = type; |
||||
ie_called->plan = plan; |
||||
memcpy(ie_called->digits, dialing, strlen(dialing)); |
||||
} |
||||
|
||||
/* helper to decode called party number (dialing) */ |
||||
int osmo_cc_get_ie_called(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, uint8_t *plan, char *dialing, size_t dialing_size) |
||||
{ |
||||
struct osmo_cc_ie_called *ie_called; |
||||
int rc; |
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLED, ie_repeat, sizeof(*ie_called), (const void **)&ie_called); |
||||
if (rc < 0) |
||||
return rc; |
||||
*type = ie_called->type; |
||||
*plan = ie_called->plan; |
||||
_ie2string(dialing, dialing_size, ie_called->digits, rc); |
||||
return rc; |
||||
} |
||||
|
||||
/* helper to encode called party sub address (dialing) */ |
||||
void osmo_cc_add_ie_called_sub(osmo_cc_msg_t *msg, uint8_t type, const char *dialing) |
||||
{ |
||||
struct osmo_cc_ie_called_sub *ie_called_sub; |
||||
|
||||
ie_called_sub = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLED_SUB, sizeof(*ie_called_sub) + strlen(dialing)); |
||||
ie_called_sub->type = type; |
||||
memcpy(ie_called_sub->digits, dialing, strlen(dialing)); |
||||
} |
||||
|
||||
/* helper to decode called party sub address (dialing) */ |
||||
int osmo_cc_get_ie_called_sub(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, char *dialing, size_t dialing_size) |
||||
{ |
||||
struct osmo_cc_ie_called_sub *ie_called_sub; |
||||
int rc; |
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLED_SUB, ie_repeat, sizeof(*ie_called_sub), (const void **)&ie_called_sub); |
||||
if (rc < 0) |
||||
return rc; |
||||
*type = ie_called_sub->type; |
||||
_ie2string(dialing, dialing_size, ie_called_sub->digits, rc); |
||||
return rc; |
||||
} |
||||
|
||||
/* helper to encode called party name (dialing) */ |
||||
void osmo_cc_add_ie_called_name(osmo_cc_msg_t *msg, const char *name) |
||||
{ |
||||
struct osmo_cc_ie_called_name *ie_called_name; |
||||
|
||||
ie_called_name = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLED_NAME, sizeof(*ie_called_name) + strlen(name)); |
||||
memcpy(ie_called_name->name, name, strlen(name)); |
||||
} |
||||
|
||||
/* helper to decode called party name (dialing) */ |
||||
int osmo_cc_get_ie_called_name(osmo_cc_msg_t *msg, int ie_repeat, char *name, size_t name_size) |
||||
{ |
||||
struct osmo_cc_ie_called_name *ie_called_name; |
||||
int rc; |
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLED_NAME, ie_repeat, sizeof(*ie_called_name), (const void **)&ie_called_name); |
||||
if (rc < 0) |
||||
return rc; |
||||
_ie2string(name, name_size, ie_called_name->name, rc); |
||||
return rc; |
||||
} |
||||
|
||||
/* helper to encode called interface name */ |
||||
void osmo_cc_add_ie_called_interface(osmo_cc_msg_t *msg, const char *interface) |
||||
{ |
||||
struct osmo_cc_ie_called_interface *ie_interface; |
||||
|
||||
ie_interface = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLED_INTERFACE, sizeof(*ie_interface) + strlen(interface)); |
||||
memcpy(ie_interface->name, interface, strlen(interface)); |
||||
} |
||||
|
||||
/* helper to decode called interface name */ |
||||
int osmo_cc_get_ie_called_interface(osmo_cc_msg_t *msg, int ie_repeat, char *interface, size_t interface_size) |
||||
{ |
||||
struct osmo_cc_ie_called_interface *ie_interface; |
||||
int rc; |
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLED_INTERFACE, ie_repeat, sizeof(*ie_interface), (const void **)&ie_interface); |
||||
if (rc < 0) |
||||
return rc; |
||||
_ie2string(interface, interface_size, ie_interface->name, rc); |
||||
return rc; |
||||
} |
||||
|
||||
/* helper to encode complete IE */ |
||||
void osmo_cc_add_ie_complete(osmo_cc_msg_t *msg) |
||||
{ |
||||
osmo_cc_add_ie(msg, OSMO_CC_IE_COMPLETE, 0); |
||||
} |
||||
|
||||
/* helper to decode complete IE */ |
||||
int osmo_cc_get_ie_complete(osmo_cc_msg_t *msg, int ie_repeat) |
||||
{ |
||||
int rc; |
||||
void *ie_complete; |
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_COMPLETE, ie_repeat, 0, (const void **)&ie_complete); |
||||
return rc; |
||||
} |
||||
|
||||
/* helper to encode calling/connected party number (caller ID or connected ID) */ |
||||
void osmo_cc_add_ie_calling(osmo_cc_msg_t *msg, uint8_t type, uint8_t plan, uint8_t present, uint8_t screen, const char *callerid) |
||||
{ |
||||
struct osmo_cc_ie_calling *ie_calling; |
||||
|
||||
ie_calling = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLING, sizeof(*ie_calling) + strlen(callerid)); |
||||
ie_calling->type = type; |
||||
ie_calling->plan = plan; |
||||
ie_calling->present = present; |
||||
ie_calling->screen = screen; |
||||
memcpy(ie_calling->digits, callerid, strlen(callerid)); |
||||
} |
||||
|
||||
/* helper to decode calling/connected party number (caller ID or connected ID) */ |
||||
int osmo_cc_get_ie_calling(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, uint8_t *plan, uint8_t *present, uint8_t *screen, char *callerid, size_t callerid_size) |
||||
{ |
||||
struct osmo_cc_ie_calling *ie_calling; |
||||
int rc; |
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLING, ie_repeat, sizeof(*ie_calling), (const void **)&ie_calling); |
||||
if (rc < 0) |
||||
return rc; |
||||
*type = ie_calling->type; |
||||
*plan = ie_calling->plan; |
||||
*present = ie_calling->present; |
||||
*screen = ie_calling->screen; |
||||
_ie2string(callerid, callerid_size, ie_calling->digits, rc); |
||||
return rc; |
||||
} |
||||
|
||||
/* helper to encode calling/connected sub address (caller ID or connected ID) */ |
||||
void osmo_cc_add_ie_calling_sub(osmo_cc_msg_t *msg, uint8_t type, const char *callerid) |
||||
{ |
||||
struct osmo_cc_ie_calling_sub *ie_calling_sub; |
||||
|
||||
ie_calling_sub = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLING_SUB, sizeof(*ie_calling_sub) + strlen(callerid)); |
||||
ie_calling_sub->type = type; |
||||
memcpy(ie_calling_sub->digits, callerid, strlen(callerid)); |
||||
} |
||||
|
||||
/* helper to decode calling/connected sub address (caller ID or connected ID) */ |
||||
int osmo_cc_get_ie_calling_sub(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, char *callerid, size_t callerid_size) |
||||
{ |
||||
struct osmo_cc_ie_calling_sub *ie_calling_sub; |
||||
int rc; |
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLING_SUB, ie_repeat, sizeof(*ie_calling_sub), (const void **)&ie_calling_sub); |
||||
if (rc < 0) |
||||
return rc; |
||||
*type = ie_calling_sub->type; |
||||
_ie2string(callerid, callerid_size, ie_calling_sub->digits, rc); |
||||
return rc; |
||||
} |
||||
|
||||
/* helper to encode calling/connected name (caller ID or connected ID) */ |
||||
void osmo_cc_add_ie_calling_name(osmo_cc_msg_t *msg, const char *name) |
||||
{ |
||||
struct osmo_cc_ie_calling_name *ie_calling_name; |
||||
|
||||
ie_calling_name = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLING_NAME, sizeof(*ie_calling_name) + strlen(name)); |
||||
memcpy(ie_calling_name->name, name, strlen(name)); |
||||
} |
||||
|
||||
/* helper to decode calling/connected name address (caller ID or connected ID) */ |
||||
int osmo_cc_get_ie_calling_name(osmo_cc_msg_t *msg, int ie_repeat, char *name, size_t name_size) |
||||
{ |
||||
struct osmo_cc_ie_calling_name *ie_calling_name; |
||||
int rc; |
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLING_NAME, ie_repeat, sizeof(*ie_calling_name), (const void **)&ie_calling_name); |
||||
if (rc < 0) |
||||
return rc; |
||||
_ie2string(name, name_size, ie_calling_name->name, rc); |
||||
return rc; |
||||
} |
||||
|
||||
/* helper to encode calling interface name */ |
||||
void osmo_cc_add_ie_calling_interface(osmo_cc_msg_t *msg, const char *interface) |
||||
{ |
||||
struct osmo_cc_ie_calling_interface *ie_interface; |
||||
|
||||
ie_interface = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLING_INTERFACE, sizeof(*ie_interface) + strlen(interface)); |
||||
memcpy(ie_interface->name, interface, strlen(interface)); |
||||
} |
||||
|
||||
/* helper to decode calling interface name */ |
||||
int osmo_cc_get_ie_calling_interface(osmo_cc_msg_t *msg, int ie_repeat, char *interface, size_t interface_size) |
||||
{ |
||||
struct osmo_cc_ie_calling_interface *ie_interface; |
||||
int rc; |
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLING_INTERFACE, ie_repeat, sizeof(*ie_interface), (const void **)&ie_interface); |
||||
if (rc < 0) |
||||
return rc; |
||||
_ie2string(interface, interface_size, ie_interface->name, rc); |
||||
return rc; |
||||
} |
||||
|
||||
/* helper to encode network specific caller/connected ID */ |
||||
void osmo_cc_add_ie_calling_network(osmo_cc_msg_t *msg, uint8_t type, const char *networkid) |
||||
{ |
||||
struct osmo_cc_ie_network *ie_network; |
||||
|
||||
ie_network = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLING_NETWORK, sizeof(*ie_network) + strlen(networkid)); |
||||
ie_network->type = type; |
||||
memcpy(ie_network->id, networkid, strlen(networkid)); |
||||
} |
||||
|
||||
/* helper to encode network specific caller/connected ID */ |
||||
int osmo_cc_get_ie_calling_network(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, char *networkid, size_t networkid_size) |
||||
{ |
||||
struct osmo_cc_ie_network *ie_network; |
||||
int rc; |
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLING_NETWORK, ie_repeat, sizeof(*ie_network), (const void **)&ie_network); |
||||
if (rc < 0) |
||||
return rc; |
||||
*type = ie_network->type; |
||||
_ie2string(networkid, networkid_size, ie_network->id, rc); |
||||
return rc; |
||||
} |
||||
|
||||
/* helper to encode bearer capability */ |
||||
void osmo_cc_add_ie_bearer(osmo_cc_msg_t *msg, uint8_t coding, uint8_t capability, uint8_t mode) |
||||
{ |
||||
struct osmo_cc_ie_bearer *ie_bearer; |
||||
|
||||
ie_bearer = osmo_cc_add_ie(msg, OSMO_CC_IE_BEARER, sizeof(*ie_bearer)); |
||||
ie_bearer->coding = coding; |
||||
ie_bearer->capability = capability; |
||||
ie_bearer->mode = mode; |
||||
} |
||||
|
||||
/* helper to decode bearer capability */ |
||||
int osmo_cc_get_ie_bearer(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *coding, uint8_t *capability, uint8_t *mode) |
||||
{ |
||||
struct osmo_cc_ie_bearer *ie_bearer; |
||||
int rc; |
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_BEARER, ie_repeat, sizeof(*ie_bearer), (const void **)&ie_bearer); |
||||
if (rc < 0) |
||||
return rc; |
||||
*coding = ie_bearer->coding; |
||||
*capability = ie_bearer->capability; |
||||
*mode = ie_bearer->mode; |
||||
return rc; |
||||
} |
||||
|
||||
/* helper to encode redirection and redirecting number */ |
||||
void osmo_cc_add_ie_redir(osmo_cc_msg_t *msg, uint8_t type, uint8_t plan, uint8_t present, uint8_t screen, uint8_t redir_reason, const char *callerid) |
||||
{ |
||||
struct osmo_cc_ie_redir *ie_redir; |
||||
|
||||
ie_redir = osmo_cc_add_ie(msg, OSMO_CC_IE_REDIR, sizeof(*ie_redir) + strlen(callerid)); |
||||
ie_redir->type = type; |
||||
ie_redir->plan = plan; |
||||
ie_redir->present = present; |
||||
ie_redir->screen = screen; |
||||
ie_redir->redir_reason = redir_reason; |
||||
memcpy(ie_redir->digits, callerid, strlen(callerid)); |
||||
} |
||||
|
||||
/* helper to decode redirection and redirecting number */ |
||||
int osmo_cc_get_ie_redir(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, uint8_t *plan, uint8_t *present, uint8_t *screen, uint8_t *reason, char *callerid, size_t callerid_size) |
||||
{ |
||||
struct osmo_cc_ie_redir *ie_redir; |
||||
int rc; |
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_REDIR, ie_repeat, sizeof(*ie_redir), (const void **)&ie_redir); |
||||
if (rc < 0) |
||||
return rc; |
||||
*type = ie_redir->type; |
||||
*plan = ie_redir->plan; |
||||
*present = ie_redir->present; |
||||
*screen = ie_redir->screen; |
||||
*reason = ie_redir->redir_reason; |
||||
_ie2string(callerid, callerid_size, ie_redir->digits, rc); |
||||
return rc; |
||||
} |
||||
|
||||
/* helper to encode DTMF tones */ |
||||
void osmo_cc_add_ie_dtmf(osmo_cc_msg_t *msg, uint8_t duration_ms, uint8_t pause_ms, uint8_t dtmf_mode, const char *digits) |
||||
{ |
||||
struct osmo_cc_ie_dtmf *ie_dtmf; |
||||
|
||||
ie_dtmf = osmo_cc_add_ie(msg, OSMO_CC_IE_DTMF, sizeof(*ie_dtmf) + strlen(digits)); |
||||
ie_dtmf->duration_ms = duration_ms; |
||||
ie_dtmf->pause_ms = pause_ms; |
||||
ie_dtmf->dtmf_mode = dtmf_mode; |
||||
memcpy(ie_dtmf->digits, digits, strlen(digits)); |
||||
} |
||||
|
||||
/* helper to decode DTMF tones */ |
||||
int osmo_cc_get_ie_dtmf(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *duration_ms, uint8_t *pause_ms, uint8_t *dtmf_mode, char *digits, size_t digits_size) |
||||
{ |
||||
struct osmo_cc_ie_dtmf *ie_dtmf; |
||||
int rc; |
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_DTMF, ie_repeat, sizeof(*ie_dtmf), (const void **)&ie_dtmf); |
||||
if (rc < 0) |
||||
return rc; |
||||
*duration_ms = ie_dtmf->duration_ms; |
||||
*pause_ms = ie_dtmf->pause_ms; |
||||
*dtmf_mode = ie_dtmf->dtmf_mode; |
||||
_ie2string(digits, digits_size, ie_dtmf->digits, rc); |
||||
return rc; |
||||
} |
||||
|
||||
/* helper to encode keypad press */ |
||||
void osmo_cc_add_ie_keypad(osmo_cc_msg_t *msg, const char *digits) |
||||
{ |
||||
struct osmo_cc_ie_keypad *ie_keypad; |
||||
|
||||
ie_keypad = osmo_cc_add_ie(msg, OSMO_CC_IE_KEYPAD, sizeof(*ie_keypad) + strlen(digits)); |
||||
memcpy(ie_keypad->digits, digits, strlen(digits)); |
||||
} |
||||
|
||||
/* helper to decode keypad press */ |
||||
int osmo_cc_get_ie_keypad(osmo_cc_msg_t *msg, int ie_repeat, char *digits, size_t digits_size) |
||||
{ |
||||
struct osmo_cc_ie_keypad *ie_keypad; |
||||
int rc; |
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_KEYPAD, ie_repeat, sizeof(*ie_keypad), (const void **)&ie_keypad); |
||||
if (rc < 0) |
||||
return rc; |
||||
_ie2string(digits, digits_size, ie_keypad->digits, rc); |
||||
return rc; |
||||
} |
||||
|
||||
/* helper to encode call progress information */ |
||||
void osmo_cc_add_ie_progress(osmo_cc_msg_t *msg, uint8_t coding, uint8_t location, uint8_t progress) |
||||
{ |
||||
struct osmo_cc_ie_progress *ie_progress; |
||||
|
||||
ie_progress = osmo_cc_add_ie(msg, OSMO_CC_IE_PROGRESS, sizeof(*ie_progress)); |
||||
ie_progress->coding = coding; |
||||
ie_progress->location = location; |
||||
ie_progress->progress = progress; |
||||
} |
||||
|
||||
/* helper to decode call progress information */ |
||||
int osmo_cc_get_ie_progress(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *coding, uint8_t *location, uint8_t *progress) |
||||
{ |
||||