Add Osmo-CC library to replace MNCC
parent
59119f380f
commit
ad8a7be345
|
@ -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)
|
||||
{
|
||||
struct osmo_cc_ie_progress *ie_progress;
|
||||
int rc;
|
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_PROGRESS, ie_repeat, sizeof(*ie_progress), (const void **)&ie_progress);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
*coding = ie_progress->coding;
|
||||
*location = ie_progress->location;
|
||||
*progress = ie_progress->progress;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* helper to encode notification */
|
||||
void osmo_cc_add_ie_notify(osmo_cc_msg_t *msg, uint8_t notify)
|
||||
{
|
||||
struct osmo_cc_ie_notify *ie_notify;
|
||||
|
||||
ie_notify = osmo_cc_add_ie(msg, OSMO_CC_IE_NOTIFY, sizeof(*ie_notify));
|
||||
ie_notify->notify = notify;
|
||||
}
|
||||
|
||||
/* helper to decode notification */
|
||||
int osmo_cc_get_ie_notify(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *notify)
|
||||
{
|
||||
struct osmo_cc_ie_notify *ie_notify;
|
||||
int rc;
|
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_NOTIFY, ie_repeat, sizeof(*ie_notify), (const void **)&ie_notify);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
*notify = ie_notify->notify;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* helper to encode cause */
|
||||
void osmo_cc_add_ie_cause(osmo_cc_msg_t *msg, uint8_t location, uint8_t isdn_cause, uint16_t sip_cause, uint8_t socket_cause)
|
||||
{
|
||||
struct osmo_cc_ie_cause *ie_cause;
|
||||
|
||||
ie_cause = osmo_cc_add_ie(msg, OSMO_CC_IE_CAUSE, sizeof(*ie_cause));
|
||||
ie_cause->location = location;
|
||||
ie_cause->isdn_cause = isdn_cause;
|
||||
ie_cause->sip_cause_networkorder = htons(sip_cause);
|
||||
ie_cause->socket_cause = socket_cause;
|
||||
}
|
||||
|
||||
/* helper to deccode cause */
|
||||
int osmo_cc_get_ie_cause(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *location, uint8_t *isdn_cause, uint16_t *sip_cause, uint8_t *socket_cause)
|
||||
{
|
||||
struct osmo_cc_ie_cause *ie_cause;
|
||||
int rc;
|
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CAUSE, ie_repeat, sizeof(*ie_cause), (const void **)&ie_cause);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
*location = ie_cause->location;
|
||||
*isdn_cause = ie_cause->isdn_cause;
|
||||
*sip_cause = ntohs(ie_cause->sip_cause_networkorder);
|
||||
*socket_cause = ie_cause->socket_cause;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* helper to encode DISPLAY information */
|
||||
void osmo_cc_add_ie_display(osmo_cc_msg_t *msg, const char *text)
|
||||
{
|
||||
struct osmo_cc_ie_display *ie_display;
|
||||
|
||||
ie_display = osmo_cc_add_ie(msg, OSMO_CC_IE_DISPLAY, sizeof(*ie_display) + strlen(text));
|
||||
memcpy(ie_display->text, text, strlen(text));
|
||||
}
|
||||
|
||||
/* helper to decode DISPLAY information */
|
||||
int osmo_cc_get_ie_display(osmo_cc_msg_t *msg, int ie_repeat, char *text, size_t text_size)
|
||||
{
|
||||
struct osmo_cc_ie_display *ie_display;
|
||||
int rc;
|
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_DISPLAY, ie_repeat, sizeof(*ie_display), (const void **)&ie_display);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
_ie2string(text, text_size, ie_display->text, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* helper to encode SDP */
|
||||
void osmo_cc_add_ie_sdp(osmo_cc_msg_t *msg, const char *sdp)
|
||||
{
|
||||
struct osmo_cc_ie_sdp *ie_sdp;
|
||||
|
||||
ie_sdp = osmo_cc_add_ie(msg, OSMO_CC_IE_SDP, sizeof(*ie_sdp) + strlen(sdp));
|
||||
memcpy(ie_sdp->sdp, sdp, strlen(sdp));
|
||||
}
|
||||
|
||||
/* helper to decode SDP */
|
||||
int osmo_cc_get_ie_sdp(osmo_cc_msg_t *msg, int ie_repeat, char *sdp, size_t sdp_size)
|
||||
{
|
||||
struct osmo_cc_ie_sdp *ie_sdp;
|
||||
int rc;
|
||||
|
||||
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_SDP, ie_repeat, sizeof(*ie_sdp), (const void **)&ie_sdp);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
_ie2string(sdp, sdp_size, ie_sdp->sdp, rc);
|
||||
return rc;
|
||||