Add Osmo-CC library to replace MNCC

pull/1/head
Andreas Eversberg 3 years ago
parent 59119f380f
commit ad8a7be345
  1. 1
      .gitignore
  2. 1
      configure.ac
  3. 3
      src/Makefile.am
  4. 2
      src/libdebug/debug.c
  5. 1
      src/libdebug/debug.h
  6. 15
      src/libosmocc/Makefile.am
  7. 252
      src/libosmocc/cause.c
  8. 5
      src/libosmocc/cause.h
  9. 1476
      src/libosmocc/endpoint.c
  10. 128
      src/libosmocc/endpoint.h
  11. 171
      src/libosmocc/helper.c
  12. 13
      src/libosmocc/helper.h
  13. 879
      src/libosmocc/message.c
  14. 437
      src/libosmocc/message.h
  15. 399
      src/libosmocc/rtp.c
  16. 8
      src/libosmocc/rtp.h
  17. 684
      src/libosmocc/screen.c
  18. 7
      src/libosmocc/screen.h
  19. 539
      src/libosmocc/sdp.c
  20. 6
      src/libosmocc/sdp.h
  21. 639
      src/libosmocc/session.c
  22. 119
      src/libosmocc/session.h
  23. 583
      src/libosmocc/socket.c
  24. 44
      src/libosmocc/socket.h

1
.gitignore vendored

@ -50,6 +50,7 @@ src/libclipper/libclipper.a
src/libserial/libserial.a
src/libv27/libv27.a
src/libmtp/libmtp.a
src/libosmocc/libosmocc.a
src/anetz/libgermanton.a
src/anetz/anetz
src/bnetz/bnetz

@ -87,6 +87,7 @@ AC_OUTPUT(
src/libserial/Makefile
src/libv27/Makefile
src/libmtp/Makefile
src/libosmocc/Makefile
src/anetz/Makefile
src/bnetz/Makefile
src/cnetz/Makefile

@ -28,7 +28,8 @@ SUBDIRS = \
libclipper \
libserial \
libv27 \
libmtp
libmtp \
libosmocc
if HAVE_ALSA
SUBDIRS += \

@ -56,7 +56,7 @@ struct debug_cat {
{ "eurosignal", "\033[1;34m" },
{ "frame", "\033[0;36m" },
{ "call", "\033[0;37m" },
{ "mncc", "\033[1;32m" },
{ "cc", "\033[1;32m" },
{ "database", "\033[0;33m" },
{ "transaction", "\033[0;32m" },
{ "dms", "\033[0;33m" },

@ -20,6 +20,7 @@
#define DFRAME 13
#define DCALL 14
#define DMNCC 15
#define DCC 15
#define DDB 16
#define DTRANS 17
#define DDMS 18

@ -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)
{