Initial import from osmocom-analog
This commit is contained in:
commit
f9013904f4
|
@ -0,0 +1,50 @@
|
|||
Makefile
|
||||
Makefile.in
|
||||
.deps
|
||||
.libs
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
*.pc
|
||||
aclocal.m4
|
||||
m4/*.m4
|
||||
autom4te.cache
|
||||
config.h*
|
||||
config.sub
|
||||
config.log
|
||||
config.status
|
||||
config.guess
|
||||
configure
|
||||
compile
|
||||
depcomp
|
||||
missing
|
||||
ltmain.sh
|
||||
install-sh
|
||||
stamp-h1
|
||||
tests/*_test
|
||||
*~
|
||||
|
||||
# libtool and e.g. arm-poky-linux-gnueabi-libtool
|
||||
*libtool
|
||||
|
||||
#.tarball-version
|
||||
.version
|
||||
.dirstamp
|
||||
|
||||
# tests
|
||||
tests/atconfig
|
||||
tests/package.m4
|
||||
tests/testsuite
|
||||
tests/testsuite.log
|
||||
tests/subchan_demux/.dirstamp
|
||||
tests/subchan_demux/subchan_demux_test
|
||||
tests/ipa_recv/ipa_recv_test
|
||||
tests/trau_pcu_ericsson/trau_pcu_ericsson_test
|
||||
tests/trau_sync/trau_sync_test
|
||||
|
||||
|
||||
|
||||
# vi/vim files
|
||||
*.sw?
|
||||
|
||||
contrib/libosmo-cc.spec
|
|
@ -0,0 +1 @@
|
|||
1.0.0
|
|
@ -0,0 +1,19 @@
|
|||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
|
||||
SUBDIRS = include src
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libosmocc.pc
|
||||
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
$(top_srcdir)/.version:
|
||||
echo $(VERSION) > $@-t && mv $@-t $@
|
||||
dist-hook:
|
||||
echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
$(NULL)
|
||||
|
||||
@RELMAKE@
|
|
@ -0,0 +1,135 @@
|
|||
AC_INIT([libosmo-cc],
|
||||
m4_esyscmd([./git-version-gen .tarball-version]),
|
||||
[openbsc@lists.osmocom.org])
|
||||
|
||||
dnl *This* is the root dir, even if an install-sh exists in ../ or ../../
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
|
||||
AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip 1.6 subdir-objects])
|
||||
AC_CONFIG_TESTDIR(tests)
|
||||
|
||||
CFLAGS="$CFLAGS -std=gnu11"
|
||||
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
dnl include release helper
|
||||
RELMAKE='-include osmo-release.mk'
|
||||
AC_SUBST([RELMAKE])
|
||||
|
||||
dnl checks for programs
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
LT_INIT([pic-only])
|
||||
|
||||
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
|
||||
AS_CASE(["$LD"],[*clang*],
|
||||
[AS_CASE(["${host_os}"],
|
||||
[*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
|
||||
|
||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
|
||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
|
||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
|
||||
AC_MSG_WARN([You need to install pkg-config])
|
||||
fi
|
||||
PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
CFLAGS="$CFLAGS -Wall"
|
||||
CPPFLAGS="$CPPFLAGS -Wall"
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-sanitize],
|
||||
[Compile with address sanitizer enabled],
|
||||
)],
|
||||
[sanitize=$enableval], [sanitize="no"])
|
||||
if test x"$sanitize" = x"yes"
|
||||
then
|
||||
CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
fi
|
||||
|
||||
# The following test is taken from WebKit's webkit.m4
|
||||
saved_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS -fvisibility=hidden "
|
||||
AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])],
|
||||
[ AC_MSG_RESULT([yes])
|
||||
SYMBOL_VISIBILITY="-fvisibility=hidden"],
|
||||
AC_MSG_RESULT([no]))
|
||||
CFLAGS="$saved_CFLAGS"
|
||||
AC_SUBST(SYMBOL_VISIBILITY)
|
||||
|
||||
dnl Generate the output
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-sanitize],
|
||||
[Compile with address sanitizer enabled],
|
||||
)],
|
||||
[sanitize=$enableval], [sanitize="no"])
|
||||
if test x"$sanitize" = x"yes"
|
||||
then
|
||||
CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(werror,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-werror],
|
||||
[Turn all compiler warnings into errors, with exceptions:
|
||||
a) deprecation (allow upstream to mark deprecation without breaking builds);
|
||||
b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
|
||||
]
|
||||
)],
|
||||
[werror=$enableval], [werror="no"])
|
||||
if test x"$werror" = x"yes"
|
||||
then
|
||||
WERROR_FLAGS="-Werror"
|
||||
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
|
||||
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
|
||||
CFLAGS="$CFLAGS $WERROR_FLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
|
||||
fi
|
||||
|
||||
_cflags_save=$CFLAGS
|
||||
CFLAGS="$CFLAGS $ORTP_CFLAGS"
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_PROGRAM(
|
||||
[[#include <ortp/ortp.h>]],
|
||||
[[ortp_set_log_level_mask(NULL, 0xffff);]]
|
||||
)],
|
||||
[AC_DEFINE([HAVE_ORTP_LOG_DOMAIN], [1],
|
||||
[ortp_set_log_level_mask requires domain parameter])],
|
||||
[AC_DEFINE([HAVE_ORTP_LOG_DOMAIN], [0],
|
||||
[ortp_set_log_level_mask has no domain parameter])])
|
||||
CFLAGS=$_cflags_save
|
||||
|
||||
_cflags_save=$CFLAGS
|
||||
CFLAGS="$CFLAGS $ORTP_CFLAGS -Werror"
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_PROGRAM(
|
||||
[[#include <ortp/ortp.h>
|
||||
void fake_cb(struct _RtpSession *r, void *arg1, void *arg2, void *arg3) { return; }]],
|
||||
[[rtp_session_signal_connect(NULL, "", fake_cb, (void*) fake_cb);]]
|
||||
)],
|
||||
[AC_DEFINE([RTP_SIGNAL_PTR_CAST(arg)], [(void*)(arg)],
|
||||
[rtp_session_signal_connect requires pointer parameter])],
|
||||
[AC_DEFINE([RTP_SIGNAL_PTR_CAST(arg)], [(unsigned long)(arg)],
|
||||
[rtp_session_signal_connect requires ulong parameter])])
|
||||
CFLAGS=$_cflags_save
|
||||
|
||||
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
|
||||
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
|
||||
|
||||
AC_OUTPUT(
|
||||
libosmocc.pc
|
||||
include/Makefile
|
||||
src/Makefile
|
||||
Makefile)
|
|
@ -0,0 +1,151 @@
|
|||
#!/bin/sh
|
||||
# Print a version string.
|
||||
scriptversion=2010-01-28.01
|
||||
|
||||
# Copyright (C) 2007-2010 Free Software Foundation, Inc.
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
|
||||
# It may be run two ways:
|
||||
# - from a git repository in which the "git describe" command below
|
||||
# produces useful output (thus requiring at least one signed tag)
|
||||
# - from a non-git-repo directory containing a .tarball-version file, which
|
||||
# presumes this script is invoked like "./git-version-gen .tarball-version".
|
||||
|
||||
# In order to use intra-version strings in your project, you will need two
|
||||
# separate generated version string files:
|
||||
#
|
||||
# .tarball-version - present only in a distribution tarball, and not in
|
||||
# a checked-out repository. Created with contents that were learned at
|
||||
# the last time autoconf was run, and used by git-version-gen. Must not
|
||||
# be present in either $(srcdir) or $(builddir) for git-version-gen to
|
||||
# give accurate answers during normal development with a checked out tree,
|
||||
# but must be present in a tarball when there is no version control system.
|
||||
# Therefore, it cannot be used in any dependencies. GNUmakefile has
|
||||
# hooks to force a reconfigure at distribution time to get the value
|
||||
# correct, without penalizing normal development with extra reconfigures.
|
||||
#
|
||||
# .version - present in a checked-out repository and in a distribution
|
||||
# tarball. Usable in dependencies, particularly for files that don't
|
||||
# want to depend on config.h but do want to track version changes.
|
||||
# Delete this file prior to any autoconf run where you want to rebuild
|
||||
# files to pick up a version string change; and leave it stale to
|
||||
# minimize rebuild time after unrelated changes to configure sources.
|
||||
#
|
||||
# It is probably wise to add these two files to .gitignore, so that you
|
||||
# don't accidentally commit either generated file.
|
||||
#
|
||||
# Use the following line in your configure.ac, so that $(VERSION) will
|
||||
# automatically be up-to-date each time configure is run (and note that
|
||||
# since configure.ac no longer includes a version string, Makefile rules
|
||||
# should not depend on configure.ac for version updates).
|
||||
#
|
||||
# AC_INIT([GNU project],
|
||||
# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
|
||||
# [bug-project@example])
|
||||
#
|
||||
# Then use the following lines in your Makefile.am, so that .version
|
||||
# will be present for dependencies, and so that .tarball-version will
|
||||
# exist in distribution tarballs.
|
||||
#
|
||||
# BUILT_SOURCES = $(top_srcdir)/.version
|
||||
# $(top_srcdir)/.version:
|
||||
# echo $(VERSION) > $@-t && mv $@-t $@
|
||||
# dist-hook:
|
||||
# echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
case $# in
|
||||
1) ;;
|
||||
*) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
|
||||
esac
|
||||
|
||||
tarball_version_file=$1
|
||||
nl='
|
||||
'
|
||||
|
||||
# First see if there is a tarball-only version file.
|
||||
# then try "git describe", then default.
|
||||
if test -f $tarball_version_file
|
||||
then
|
||||
v=`cat $tarball_version_file` || exit 1
|
||||
case $v in
|
||||
*$nl*) v= ;; # reject multi-line output
|
||||
[0-9]*) ;;
|
||||
*) v= ;;
|
||||
esac
|
||||
test -z "$v" \
|
||||
&& echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
|
||||
fi
|
||||
|
||||
if test -n "$v"
|
||||
then
|
||||
: # use $v
|
||||
elif
|
||||
v=`git describe --abbrev=4 HEAD 2>/dev/null \
|
||||
|| git describe --abbrev=4 --match='v*' HEAD 2>/dev/null` \
|
||||
&& case $v in
|
||||
[0-9]*) ;;
|
||||
v[0-9]*) ;;
|
||||
*) (exit 1) ;;
|
||||
esac
|
||||
then
|
||||
# Is this a new git that lists number of commits since the last
|
||||
# tag or the previous older version that did not?
|
||||
# Newer: v6.10-77-g0f8faeb
|
||||
# Older: v6.10-g0f8faeb
|
||||
case $v in
|
||||
*-*-*) : git describe is okay three part flavor ;;
|
||||
*-*)
|
||||
: git describe is older two part flavor
|
||||
# Recreate the number of commits and rewrite such that the
|
||||
# result is the same as if we were using the newer version
|
||||
# of git describe.
|
||||
vtag=`echo "$v" | sed 's/-.*//'`
|
||||
numcommits=`git rev-list "$vtag"..HEAD | wc -l`
|
||||
v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
|
||||
;;
|
||||
esac
|
||||
|
||||
# Change the first '-' to a '.', so version-comparing tools work properly.
|
||||
# Remove the "g" in git describe's output string, to save a byte.
|
||||
v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
|
||||
else
|
||||
v=UNKNOWN
|
||||
fi
|
||||
|
||||
v=`echo "$v" |sed 's/^v//'`
|
||||
|
||||
# Don't declare a version "dirty" merely because a time stamp has changed.
|
||||
git status > /dev/null 2>&1
|
||||
|
||||
dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
|
||||
case "$dirty" in
|
||||
'') ;;
|
||||
*) # Append the suffix only if there isn't one already.
|
||||
case $v in
|
||||
*-dirty) ;;
|
||||
*) v="$v-dirty" ;;
|
||||
esac ;;
|
||||
esac
|
||||
|
||||
# Omit the trailing newline, so that m4_esyscmd can use the result directly.
|
||||
echo "$v" | tr -d '\012'
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-end: "$"
|
||||
# End:
|
|
@ -0,0 +1,14 @@
|
|||
noinst_HEADERS = \
|
||||
osmocom/cc/screen.h
|
||||
|
||||
nobase_include_HEADERS = \
|
||||
osmocom/cc/endpoint.h \
|
||||
osmocom/cc/socket.h \
|
||||
osmocom/cc/message.h \
|
||||
osmocom/cc/cause.h \
|
||||
osmocom/cc/session.h \
|
||||
osmocom/cc/sdp.h \
|
||||
osmocom/cc/rtp.h \
|
||||
osmocom/cc/g711.h \
|
||||
osmocom/cc/helper.h \
|
||||
osmocom/cc/misc.h
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
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);
|
|
@ -0,0 +1,126 @@
|
|||
#pragma once
|
||||
|
||||
#include <osmocom/cc/message.h>
|
||||
#include <osmocom/cc/socket.h>
|
||||
#include <osmocom/cc/cause.h>
|
||||
#include <osmocom/cc/session.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,0
|
||||
|
||||
/* 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 osmo_timer_list attach_timer; /* timer to retry attachment */
|
||||
osmo_cc_session_config_t session_config; /* SDP/RTP default configuration */
|
||||
} 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 REL, while upper layer gets a DISC */
|
||||
int upper_layer_released; /* when upper layer sent REL, while lower layer gets a DISC */
|
||||
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_set_log_cat(int cc_log_cat);
|
||||
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);
|
||||
osmo_cc_call_t *osmo_cc_get_attached_interface(osmo_cc_endpoint_t *ep, const char *interface);
|
||||
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);
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
void g711_init(void);
|
||||
void g711_encode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_encode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_decode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_decode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_encode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_encode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_decode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_decode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_transcode_alaw_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_transcode_alaw_flipped_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_transcode_alaw_to_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_transcode_ulaw_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_transcode_ulaw_flipped_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_transcode_ulaw_to_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_transcode_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
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 *priv);
|
||||
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
};
|
||||
|
||||
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv,
|
||||
struct osmo_cc_helper_audio_codecs *codecs,
|
||||
void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number,
|
||||
uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len),
|
||||
osmo_cc_msg_t *msg, int debug);
|
||||
const char *osmo_cc_helper_audio_accept(
|
||||
osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs,
|
||||
void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number,
|
||||
uint32_t timestamp, uint32_t ssrc, 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);
|
||||
const char *osmo_cc_helper_audio_accept_te(
|
||||
osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs,
|
||||
void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number,
|
||||
uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len),
|
||||
osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p,
|
||||
osmo_cc_session_codec_t **telephone_event_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);
|
||||
int osmo_cc_helper_audio_negotiate_te(osmo_cc_msg_t *msg, osmo_cc_session_t **session_p,
|
||||
osmo_cc_session_codec_t **codec_p, osmo_cc_session_codec_t **telephone_event_p);
|
|
@ -0,0 +1,521 @@
|
|||
#pragma once
|
||||
|
||||
#define OSMO_CC_VERSION "OSMOCCv1"
|
||||
|
||||
/* call control messages types */
|
||||
enum osmo_cc_msg_type {
|
||||
OSMO_CC_MSG_SETUP_REQ = 0x00,
|
||||
OSMO_CC_MSG_SETUP_IND = 0x01,
|
||||
OSMO_CC_MSG_REJ_REQ = 0x10,
|
||||
OSMO_CC_MSG_REJ_IND = 0x11,
|
||||
OSMO_CC_MSG_SETUP_ACK_REQ = 0x20,
|
||||
OSMO_CC_MSG_SETUP_ACK_IND = 0x21,
|
||||
OSMO_CC_MSG_PROC_REQ = 0x30,
|
||||
OSMO_CC_MSG_PROC_IND = 0x31,
|
||||
OSMO_CC_MSG_ALERT_REQ = 0x40,
|
||||
OSMO_CC_MSG_ALERT_IND = 0x41,
|
||||
OSMO_CC_MSG_SETUP_RSP = 0x02,
|
||||
OSMO_CC_MSG_SETUP_CNF = 0x03,
|
||||
OSMO_CC_MSG_SETUP_COMP_REQ = 0x50,
|
||||
OSMO_CC_MSG_SETUP_COMP_IND = 0x51,
|
||||
OSMO_CC_MSG_DISC_REQ = 0x60,
|
||||
OSMO_CC_MSG_DISC_IND = 0x61,
|
||||
OSMO_CC_MSG_REL_REQ = 0x70,
|
||||
OSMO_CC_MSG_REL_CNF = 0x73,
|
||||
OSMO_CC_MSG_REL_IND = 0x71,
|
||||
OSMO_CC_MSG_PROGRESS_REQ = 0x80,
|
||||
OSMO_CC_MSG_PROGRESS_IND = 0x81,
|
||||
OSMO_CC_MSG_NOTIFY_REQ = 0x84,
|
||||
OSMO_CC_MSG_NOTIFY_IND = 0x85,
|
||||
OSMO_CC_MSG_INFO_REQ = 0x88,
|
||||
OSMO_CC_MSG_INFO_IND = 0x89,
|
||||
OSMO_CC_MSG_MODIFY_REQ = 0x90,
|
||||
OSMO_CC_MSG_MODIFY_IND = 0x91,
|
||||
OSMO_CC_MSG_MODIFY_RSP = 0x92,
|
||||
OSMO_CC_MSG_MODIFY_CNF = 0x93,
|
||||
OSMO_CC_MSG_ATTACH_REQ = 0xf8,
|
||||
OSMO_CC_MSG_ATTACH_IND = 0xf9,
|
||||
OSMO_CC_MSG_ATTACH_RSP = 0xfa,
|
||||
OSMO_CC_MSG_ATTACH_CNF = 0xfb,
|
||||
OSMO_CC_MSG_DUMMY_REQ = 0xfc,
|
||||
};
|
||||
#define OSMO_CC_MSG_NUM 0x100
|
||||
|
||||
#define OSMO_CC_MSG_MASK 0x03,
|
||||
#define OSMO_CC_MSG_REQ 0x00,
|
||||
#define OSMO_CC_MSG_IND 0x01,
|
||||
#define OSMO_CC_MSG_RSP 0x02,
|
||||
#define OSMO_CC_MSG_CNF 0x03,
|
||||
|
||||
const char *osmo_cc_msg_value2name(int value);
|
||||
int osmo_cc_msg_name2value(const char *name);
|
||||
|
||||
/* information elements */
|
||||
enum osmo_cc_ie_type {
|
||||
OSMO_CC_IE_CALLED = 0x11,
|
||||
OSMO_CC_IE_CALLED_SUB = 0x12,
|
||||
OSMO_CC_IE_CALLED_NAME = 0x13,
|
||||
OSMO_CC_IE_CALLED_INTERFACE = 0x14,
|
||||
OSMO_CC_IE_DTMF = 0x1d,
|
||||
OSMO_CC_IE_KEYPAD = 0x1e,
|
||||
OSMO_CC_IE_COMPLETE = 0x1f,
|
||||
OSMO_CC_IE_CALLING = 0x21,
|
||||
OSMO_CC_IE_CALLING_SUB = 0x22,
|
||||
OSMO_CC_IE_CALLING_NAME = 0x23,
|
||||
OSMO_CC_IE_CALLING_INTERFACE = 0x24,
|
||||
OSMO_CC_IE_CALLING_NETWORK = 0x2f,
|
||||
OSMO_CC_IE_REDIR = 0x31,
|
||||
OSMO_CC_IE_PROGRESS = 0x32,
|
||||
OSMO_CC_IE_NOTIFY = 0x33,
|
||||
OSMO_CC_IE_DISPLAY = 0x34,
|
||||
OSMO_CC_IE_CAUSE = 0x41,
|
||||
OSMO_CC_IE_BEARER = 0x51,
|
||||
OSMO_CC_IE_SDP = 0x52,
|
||||
OSMO_CC_IE_SOCKET_ADDRESS = 0x5e,
|
||||
OSMO_CC_IE_PRIVATE = 0x5f,
|
||||
};
|
||||
#define OSMO_CC_IE_NUM 0x100
|
||||
|
||||
const char *osmo_cc_ie_value2name(int value);
|
||||
int osmo_cc_ie_name2value(const char *name);
|
||||
|
||||
/* type of number, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_TYPE_UNKNOWN 0
|
||||
#define OSMO_CC_TYPE_INTERNATIONAL 1
|
||||
#define OSMO_CC_TYPE_NATIONAL 2
|
||||
#define OSMO_CC_TYPE_NETWORK 3
|
||||
#define OSMO_CC_TYPE_SUBSCRIBER 4
|
||||
#define OSMO_CC_TYPE_ABBREVIATED 5
|
||||
#define OSMO_CC_TYPE_RESERVED 7
|
||||
#define OSMO_CC_TYPE_NUM 8
|
||||
|
||||
const char *osmo_cc_type_value2name(int value);
|
||||
int osmo_cc_type_name2value(const char *name);
|
||||
|
||||
/* numbering plan, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_PLAN_UNKNOWN 0
|
||||
#define OSMO_CC_PLAN_TELEPHONY 1
|
||||
#define OSMO_CC_PLAN_DATA 3
|
||||
#define OSMO_CC_PLAN_TTY 4
|
||||
#define OSMO_CC_PLAN_NATIONAL_STANDARD 8
|
||||
#define OSMO_CC_PLAN_PRIVATE 9
|
||||
#define OSMO_CC_PLAN_RESERVED 15
|
||||
#define OSMO_CC_PLAN_NUM 16
|
||||
|
||||
const char *osmo_cc_plan_value2name(int value);
|
||||
int osmo_cc_plan_name2value(const char *name);
|
||||
|
||||
/* presentation indicator, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_PRESENT_ALLOWED 0
|
||||
#define OSMO_CC_PRESENT_RESTRICTED 1
|
||||
#define OSMO_CC_PRESENT_NOT_AVAIL 2
|
||||
#define OSMO_CC_PRESENT_RESERVED 3
|
||||
#define OSMO_CC_PRESENT_NUM 4
|
||||
|
||||
const char *osmo_cc_present_value2name(int value);
|
||||
int osmo_cc_present_name2value(const char *name);
|
||||
|
||||
/* screening indicator, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_SCREEN_USER_UNSCREENED 0
|
||||
#define OSMO_CC_SCREEN_USER_VERIFIED_PASSED 1
|
||||
#define OSMO_CC_SCREEN_USER_VERIFIED_FAILED 2
|
||||
#define OSMO_CC_SCREEN_NETWORK 3
|
||||
#define OSMO_CC_SCREEN_NUM 4
|
||||
|
||||
const char *osmo_cc_screen_value2name(int value);
|
||||
int osmo_cc_screen_name2value(const char *name);
|
||||
|
||||
/* screening indicator, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_REDIR_REASON_UNKNOWN 0
|
||||
#define OSMO_CC_REDIR_REASON_CFB 1
|
||||
#define OSMO_CC_REDIR_REASON_CFNR 2
|
||||
#define OSMO_CC_REDIR_REASON_CD 4
|
||||
#define OSMO_CC_REDIR_REASON_CF_OUTOFORDER 9
|
||||
#define OSMO_CC_REDIR_REASON_CF_BY_DTE 10
|
||||
#define OSMO_CC_REDIR_REASON_CFU 15
|
||||
#define OSMO_CC_REDIR_REASON_NUM 16
|
||||
|
||||
const char *osmo_cc_redir_reason_value2name(int value);
|
||||
int osmo_cc_redir_reason_name2value(const char *name);
|
||||
|
||||
/* notification indicator, see ITU-T Rec. Q.931 ff. */
|
||||
#define OSMO_CC_NOTIFY_USER_SUSPENDED 0x00
|
||||
#define OSMO_CC_NOTIFY_USER_RESUMED 0x01
|
||||
#define OSMO_CC_NOTIFY_BEARER_SERVICE_CHANGE 0x02
|
||||
#define OSMO_CC_NOTIFY_CALL_COMPLETION_DELAY 0x03
|
||||
#define OSMO_CC_NOTIFY_CONFERENCE_ESTABLISHED 0x42
|
||||
#define OSMO_CC_NOTIFY_CONFERENCE_DISCONNECTED 0x43
|
||||
#define OSMO_CC_NOTIFY_OTHER_PARTY_ADDED 0x44
|
||||
#define OSMO_CC_NOTIFY_ISOLATED 0x45
|
||||
#define OSMO_CC_NOTIFY_REATTACHED 0x46
|
||||
#define OSMO_CC_NOTIFY_OTHER_PARTY_ISOLATED 0x47
|
||||
#define OSMO_CC_NOTIFY_OTHER_PARTY_REATTACHED 0x48
|
||||
#define OSMO_CC_NOTIFY_OTHER_PARTY_SPLIT 0x49
|
||||
#define OSMO_CC_NOTIFY_OTHER_PARTY_DISCONNECTED 0x4a
|
||||
#define OSMO_CC_NOTIFY_CONFERENCE_FLOATING 0x4b
|
||||
#define OSMO_CC_NOTIFY_CONFERENCE_DISC_PREEMPT 0x4c /* disconnect preemted */
|
||||
#define OSMO_CC_NOTIFY_CONFERENCE_FLOATING_SUP 0x4f /* served user preemted */
|
||||
#define OSMO_CC_NOTIFY_CALL_IS_A_WAITING_CALL 0x60
|
||||
#define OSMO_CC_NOTIFY_DIVERSION_ACTIVATED 0x68
|
||||
#define OSMO_CC_NOTIFY_RESERVED_CT_1 0x69
|
||||
#define OSMO_CC_NOTIFY_RESERVED_CT_2 0x6a
|
||||
#define OSMO_CC_NOTIFY_REVERSE_CHARGING 0x6e
|
||||
#define OSMO_CC_NOTIFY_REMOTE_HOLD 0x79
|
||||
#define OSMO_CC_NOTIFY_REMOTE_RETRIEVAL 0x7a
|
||||
#define OSMO_CC_NOTIFY_CALL_IS_DIVERTING 0x7b
|
||||
#define OSMO_CC_NOTIFY_NUM 0x100
|
||||
|
||||
const char *osmo_cc_notify_value2name(int value);
|
||||
int osmo_cc_notify_name2value(const char *name);
|
||||
|
||||
/* coding standard, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_CODING_ITU_T 0
|
||||
#define OSMO_CC_CODING_ISO_IEC 1
|
||||
#define OSMO_CC_CODING_NATIONAL 2
|
||||
#define OSMO_CC_CODING_STANDARD_SPECIFIC 3
|
||||
#define OSMO_CC_CODING_NUM 4
|
||||
|
||||
const char *osmo_cc_coding_value2name(int value);
|
||||
int osmo_cc_coding_name2value(const char *name);
|
||||
|
||||
/* cause, see ITU-T Rec. Q.850 */
|
||||
#define OSMO_CC_ISDN_CAUSE_UNASSIGNED_NR 1
|
||||
#define OSMO_CC_ISDN_CAUSE_NO_ROUTE_TRANSIT 2
|
||||
#define OSMO_CC_ISDN_CAUSE_NO_ROUTE 3
|
||||
#define OSMO_CC_ISDN_CAUSE_CHAN_UNACCEPT 6
|
||||
#define OSMO_CC_ISDN_CAUSE_OP_DET_BARRING 8
|
||||
#define OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR 16
|
||||
#define OSMO_CC_ISDN_CAUSE_USER_BUSY 17
|
||||
#define OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND 18
|
||||
#define OSMO_CC_ISDN_CAUSE_USER_ALERTING_NA 19
|
||||
#define OSMO_CC_ISDN_CAUSE_CALL_REJECTED 21
|
||||
#define OSMO_CC_ISDN_CAUSE_NUMBER_CHANGED 22
|
||||
#define OSMO_CC_ISDN_CAUSE_PRE_EMPTION 25
|
||||
#define OSMO_CC_ISDN_CAUSE_NONSE_USER_CLR 26
|
||||
#define OSMO_CC_ISDN_CAUSE_DEST_OOO 27
|
||||
#define OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT 28
|
||||
#define OSMO_CC_ISDN_CAUSE_FACILITY_REJ 29
|
||||
#define OSMO_CC_ISDN_CAUSE_RESP_STATUS_INQ 30
|
||||
#define OSMO_CC_ISDN_CAUSE_NORMAL_UNSPEC 31
|
||||
#define OSMO_CC_ISDN_CAUSE_NO_CIRCUIT_CHAN 34
|
||||
#define OSMO_CC_ISDN_CAUSE_NETWORK_OOO 38
|
||||
#define OSMO_CC_ISDN_CAUSE_TEMP_FAILURE 41
|
||||
#define OSMO_CC_ISDN_CAUSE_SWITCH_CONG 42
|
||||
#define OSMO_CC_ISDN_CAUSE_ACC_INF_DISCARD 43
|
||||
#define OSMO_CC_ISDN_CAUSE_REQ_CHAN_UNAVAIL 44
|
||||
#define OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL 47
|
||||
#define OSMO_CC_ISDN_CAUSE_QOS_UNAVAIL 49
|
||||
#define OSMO_CC_ISDN_CAUSE_REQ_FAC_NOT_SUBSC 50
|
||||
#define OSMO_CC_ISDN_CAUSE_INC_BARRED_CUG 55
|
||||
#define OSMO_CC_ISDN_CAUSE_BEARER_CAP_UNAUTH 57
|
||||
#define OSMO_CC_ISDN_CAUSE_BEARER_CA_UNAVAIL 58
|
||||
#define OSMO_CC_ISDN_CAUSE_SERV_OPT_UNAVAIL 63
|
||||
#define OSMO_CC_ISDN_CAUSE_BEARERSERV_UNIMPL 65
|
||||
#define OSMO_CC_ISDN_CAUSE_ACM_GE_ACM_MAX 68
|
||||
#define OSMO_CC_ISDN_CAUSE_REQ_FAC_NOTIMPL 69
|
||||
#define OSMO_CC_ISDN_CAUSE_RESTR_BCAP_AVAIL 70
|
||||
#define OSMO_CC_ISDN_CAUSE_SERV_OPT_UNIMPL 79
|
||||
#define OSMO_CC_ISDN_CAUSE_INVAL_CALLREF 81
|
||||
#define OSMO_CC_ISDN_CAUSE_USER_NOT_IN_CUG 87
|
||||
#define OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST 88
|
||||
#define OSMO_CC_ISDN_CAUSE_INVAL_TRANS_NET 91
|
||||
#define OSMO_CC_ISDN_CAUSE_SEMANTIC_INCORR 95
|
||||
#define OSMO_CC_ISDN_CAUSE_INVAL_MAND_INF 96
|
||||
#define OSMO_CC_ISDN_CAUSE_MSGTYPE_NOTEXIST 97
|
||||
#define OSMO_CC_ISDN_CAUSE_MSGTYPE_INCOMPAT 98
|
||||
#define OSMO_CC_ISDN_CAUSE_IE_NOTEXIST 99
|
||||
#define OSMO_CC_ISDN_CAUSE_COND_IE_ERR 100
|
||||
#define OSMO_CC_ISDN_CAUSE_MSG_INCOMP_STATE 101
|
||||
#define OSMO_CC_ISDN_CAUSE_RECOVERY_TIMER 102
|
||||
#define OSMO_CC_ISDN_CAUSE_PROTO_ERR 111
|
||||
#define OSMO_CC_ISDN_CAUSE_INTERWORKING 127
|
||||
#define OSMO_CC_ISDN_CAUSE_NUM 128
|
||||
|
||||
const char *osmo_cc_isdn_cause_value2name(int value);
|
||||
int osmo_cc_isdn_cause_name2value(const char *name);
|
||||
|
||||
/* location, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_LOCATION_USER 0
|
||||
#define OSMO_CC_LOCATION_PRIV_SERV_LOC_USER 1
|
||||
#define OSMO_CC_LOCATION_PUB_SERV_LOC_USER 2
|
||||
#define OSMO_CC_LOCATION_TRANSIT 3
|
||||
#define OSMO_CC_LOCATION_PUB_SERV_REM_USER 4
|
||||
#define OSMO_CC_LOCATION_PRIV_SERV_REM_USER 5
|
||||
#define OSMO_CC_LOCATION_BEYOND_INTERWORKING 10
|
||||
#define OSMO_CC_LOCATION_NUM 16
|
||||
|
||||
const char *osmo_cc_location_value2name(int value);
|
||||
int osmo_cc_location_name2value(const char *name);
|
||||
|
||||
/* progress description, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_PROGRESS_NOT_END_TO_END_ISDN 1
|
||||
#define OSMO_CC_PROGRESS_DEST_NOT_ISDN 2
|
||||
#define OSMO_CC_PROGRESS_ORIG_NOT_ISDN 3
|
||||
#define OSMO_CC_PROGRESS_RETURN_TO_ISDN 4
|
||||
#define OSMO_CC_PROGRESS_INTERWORKING 5
|
||||
#define OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE 8
|
||||
#define OSMO_CC_PROGRESS_NUM 16
|
||||
|
||||
const char *osmo_cc_progress_value2name(int value);
|
||||
int osmo_cc_progress_name2value(const char *name);
|
||||
|
||||
/* information transfer capability, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_CAPABILITY_SPEECH 0
|
||||
#define OSMO_CC_CAPABILITY_DATA 8
|
||||
#define OSMO_CC_CAPABILITY_DATA_RESTRICTED 9
|
||||
#define OSMO_CC_CAPABILITY_AUDIO 16
|
||||
#define OSMO_CC_CAPABILITY_DATA_WITH_TONES 17
|
||||
#define OSMO_CC_CAPABILITY_VIDEO 24
|
||||
#define OSMO_CC_CAPABILITY_NUM 32
|
||||
|
||||
const char *osmo_cc_capability_value2name(int value);
|
||||
int osmo_cc_capability_name2value(const char *name);
|
||||
|
||||
/* transfer mode, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_MODE_CIRCUIT 0
|
||||
#define OSMO_CC_MODE_PACKET 2
|
||||
#define OSMO_CC_MODE_NUM 4
|
||||
|
||||
const char *osmo_cc_mode_value2name(int value);
|
||||
int osmo_cc_mode_name2value(const char *name);
|
||||
|
||||
#define OSMO_CC_DTMF_MODE_OFF 0 /* stop tone */
|
||||
#define OSMO_CC_DTMF_MODE_ON 1 /* start tone */
|
||||
#define OSMO_CC_DTMF_MODE_DIGITS 2 /* play tone(s) with duration and pauses */
|
||||
#define OSMO_CC_DTMF_MODE_NUM 3
|
||||
|
||||
const char *osmo_cc_dtmf_mode_value2name(int value);
|
||||
int osmo_cc_dtmf_mode_name2value(const char *name);
|
||||
|
||||
#define OSMO_CC_SOCKET_CAUSE_VERSION_MISMATCH 1 /* version mismatch */
|
||||
#define OSMO_CC_SOCKET_CAUSE_FAILED 2 /* connection failed */
|
||||
#define OSMO_CC_SOCKET_CAUSE_BROKEN_PIPE 3 /* connected socket failed */
|
||||
#define OSMO_CC_SOCKET_CAUSE_TIMEOUT 4 /* keepalive packets timeout */
|
||||
// if you add causes here, add them in process_cause.c also!
|
||||
#define OSMO_CC_SOCKET_CAUSE_NUM 5
|
||||
|
||||
const char *osmo_cc_socket_cause_value2name(int value);
|
||||
int osmo_cc_socket_cause_name2value(const char *name);
|
||||
|
||||
/* network type (network IE) and meaning of 'id' */
|
||||
#define OSMO_CC_NETWORK_UNDEFINED 0x00
|
||||
#define OSMO_CC_NETWORK_ALSA_NONE 0x01
|
||||
#define OSMO_CC_NETWORK_POTS_NONE 0x02
|
||||
#define OSMO_CC_NETWORK_ISDN_NONE 0x03
|
||||
#define OSMO_CC_NETWORK_SIP_NONE 0x04
|
||||
#define OSMO_CC_NETWORK_GSM_IMSI 0x05 /* id has decimal IMSI */
|
||||
#define OSMO_CC_NETWORK_GSM_IMEI 0x06 /* id has decimal IMEI */
|
||||
#define OSMO_CC_NETWORK_WEB_NONE 0x07
|
||||
#define OSMO_CC_NETWORK_DECT_NONE 0x08
|
||||
#define OSMO_CC_NETWORK_BLUETOOTH_NONE 0x09
|
||||
#define OSMO_CC_NETWORK_SS5_NONE 0x0a
|
||||
#define OSMO_CC_NETWORK_R1_NONE 0x0b
|
||||
#define OSMO_CC_NETWORK_ANETZ_NONE 0x80
|
||||
#define OSMO_CC_NETWORK_BNETZ_MUENZ 0x81 /* id starts with 'M' */
|
||||
#define OSMO_CC_NETWORK_CNETZ_NONE 0x82
|
||||
#define OSMO_CC_NETWORK_NMT_NONE 0x83 /* id has decimal password */
|
||||
#define OSMO_CC_NETWORK_R2000_NONE 0x84
|
||||
#define OSMO_CC_NETWORK_AMPS_ESN 0x85 /* if has decimal ESN (TACS also) */
|
||||
#define OSMO_CC_NETWORK_MTS_NONE 0x86
|
||||
#define OSMO_CC_NETWORK_IMTS_NONE 0x87
|
||||
#define OSMO_CC_NETWORK_EUROSIGNAL_NONE 0x88
|
||||
#define OSMO_CC_NETWORK_JOLLYCOM_NONE 0x89 /* call from JollyCom... */
|
||||
#define OSMO_CC_NETWORK_MPT1327_PSTN 0x8a /* call from MPT1327 */
|
||||
#define OSMO_CC_NETWORK_MPT1327_PBX 0x8b /* id is selected PBX number */
|
||||
#define OSMO_CC_NETWORK_NUM 0x100
|
||||
|
||||
const char *osmo_cc_network_value2name(int value);
|
||||
int osmo_cc_network_name2value(const char *name);
|
||||
|
||||
typedef struct osmo_cc_msg {
|
||||
uint8_t type;
|
||||
uint16_t length_networkorder;
|
||||
uint8_t data[0];
|
||||
} __attribute__((packed)) osmo_cc_msg_t;
|
||||
|
||||
typedef struct osmo_cc_msg_list {
|
||||
struct osmo_cc_msg_list *next;
|
||||
struct osmo_cc_msg *msg;
|
||||
uint32_t callref;
|
||||
char host[128];
|
||||
uint16_t port;
|
||||
} osmo_cc_msg_list_t;
|
||||
|
||||
typedef struct osmo_cc_ie {
|
||||
uint8_t type;
|
||||
uint16_t length_networkorder;
|
||||
uint8_t data[0];
|
||||
} __attribute__((packed)) osmo_cc_ie_t;
|
||||
|
||||
struct osmo_cc_ie_called {
|
||||
uint8_t type;
|
||||
uint8_t plan;
|
||||
char digits[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_called_sub {
|
||||
uint8_t type;
|
||||
char digits[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_called_name {
|
||||
char name[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_called_interface {
|
||||
char name[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_calling {
|
||||
uint8_t type;
|
||||
uint8_t plan;
|
||||
uint8_t present;
|
||||
uint8_t screen;
|
||||
char digits[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_calling_sub {
|
||||
uint8_t type;
|
||||
char digits[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_calling_name {
|
||||
char name[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_calling_interface {
|
||||
char name[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_network {
|
||||
uint8_t type;
|
||||
char id[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_bearer {
|
||||
uint8_t coding;
|
||||
uint8_t capability;
|
||||
uint8_t mode;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_redir {
|
||||
uint8_t type;
|
||||
uint8_t plan;
|
||||
uint8_t present;
|
||||
uint8_t screen;
|
||||
uint8_t redir_reason;
|
||||
char digits[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_dtmf {
|
||||
uint8_t duration_ms;
|
||||
uint8_t pause_ms;
|
||||
uint8_t dtmf_mode;
|
||||
char digits[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_keypad {
|
||||
char digits[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_progress {
|
||||
uint8_t coding;
|
||||
uint8_t location;
|
||||
uint8_t progress;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_notify {
|
||||
uint8_t notify;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_cause {
|
||||
uint8_t location;
|
||||
uint8_t isdn_cause;
|
||||
uint16_t sip_cause_networkorder;
|
||||
uint8_t socket_cause;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_display {
|
||||
char text[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_sdp {
|
||||
char sdp[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_socket_address {
|
||||
char address[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_private {
|
||||
uint32_t unique_networkorder;
|
||||
uint8_t data[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
uint32_t osmo_cc_new_callref(void);
|
||||
osmo_cc_msg_t *osmo_cc_new_msg(uint8_t msg_type);
|
||||
osmo_cc_msg_t *osmo_cc_clone_msg(osmo_cc_msg_t *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 *osmo_cc_msg_list_enqueue(osmo_cc_msg_list_t **mlp, osmo_cc_msg_t *msg, uint32_t callref);
|
||||
void osmo_cc_free_msg(osmo_cc_msg_t *msg);
|
||||
void osmo_cc_debug_ie(osmo_cc_msg_t *msg, int level);
|
||||
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);
|
||||
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);
|
||||
int osmo_cc_has_ie(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat);
|
||||
int osmo_cc_remove_ie(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat);
|
||||
void *osmo_cc_add_ie(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_len);
|
||||
void *osmo_cc_msg_sep_ie(osmo_cc_msg_t *msg, void **iep, uint8_t *ie_type, uint16_t *ie_length);
|
||||
|
||||
void osmo_cc_add_ie_called(osmo_cc_msg_t *msg, uint8_t type, uint8_t plan, const char *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);
|
||||
void osmo_cc_add_ie_called_sub(osmo_cc_msg_t *msg, uint8_t type, const char *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);
|
||||
void osmo_cc_add_ie_called_name(osmo_cc_msg_t *msg, const char *name);
|
||||
int osmo_cc_get_ie_called_name(osmo_cc_msg_t *msg, int ie_repeat, char *name, size_t name_size);
|
||||
void osmo_cc_add_ie_called_interface(osmo_cc_msg_t *msg, const char *interface);
|
||||
int osmo_cc_get_ie_called_interface(osmo_cc_msg_t *msg, int ie_repeat, char *interface, size_t interface_size);
|
||||
void osmo_cc_add_ie_complete(osmo_cc_msg_t *msg);
|
||||
int osmo_cc_get_ie_complete(osmo_cc_msg_t *msg, int ie_repeat);
|
||||
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);
|
||||
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);
|
||||
void osmo_cc_add_ie_calling_sub(osmo_cc_msg_t *msg, uint8_t type, const char *callerid);
|
||||
int osmo_cc_get_ie_calling_sub(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, char *callerid, size_t callerid_size);
|
||||
void osmo_cc_add_ie_calling_name(osmo_cc_msg_t *msg, const char *name);
|
||||
int osmo_cc_get_ie_calling_name(osmo_cc_msg_t *msg, int ie_repeat, char *name, size_t name_size);
|
||||
void osmo_cc_add_ie_calling_interface(osmo_cc_msg_t *msg, const char *interface);
|
||||
int osmo_cc_get_ie_calling_interface(osmo_cc_msg_t *msg, int ie_repeat, char *interface, size_t interface_size);
|
||||
void osmo_cc_add_ie_calling_network(osmo_cc_msg_t *msg, uint8_t type, const char *networkid);
|
||||
int osmo_cc_get_ie_calling_network(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, char *networkid,
|
||||
size_t networkid_size);
|
||||
void osmo_cc_add_ie_bearer(osmo_cc_msg_t *msg, uint8_t coding, uint8_t capability, uint8_t mode);
|
||||
int osmo_cc_get_ie_bearer(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *coding, uint8_t *capability, uint8_t *mode);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
void osmo_cc_add_ie_keypad(osmo_cc_msg_t *msg, const char *digits);
|
||||
int osmo_cc_get_ie_keypad(osmo_cc_msg_t *msg, int ie_repeat, char *digits, size_t digits_size);
|
||||
void osmo_cc_add_ie_progress(osmo_cc_msg_t *msg, uint8_t coding, uint8_t location, uint8_t progress);
|
||||
int osmo_cc_get_ie_progress(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *coding, uint8_t *location, uint8_t *progress);
|
||||
void osmo_cc_add_ie_notify(osmo_cc_msg_t *msg, uint8_t notify);
|
||||
int osmo_cc_get_ie_notify(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *notify);
|
||||
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);
|
||||
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);
|
||||
void osmo_cc_add_ie_display(osmo_cc_msg_t *msg, const char *text);
|
||||
int osmo_cc_get_ie_display(osmo_cc_msg_t *msg, int ie_repeat, char *text, size_t text_size);
|
||||
void osmo_cc_add_ie_sdp(osmo_cc_msg_t *msg, const char *sdp);
|
||||
int osmo_cc_get_ie_sdp(osmo_cc_msg_t *msg, int ie_repeat, char *sdp, size_t sdp_size);
|
||||
void osmo_cc_add_ie_socket_address(osmo_cc_msg_t *msg, const char *address);
|
||||
int osmo_cc_get_ie_socket_address(osmo_cc_msg_t *msg, int ie_repeat, char *address, size_t address_size);
|
||||
void osmo_cc_add_ie_private(osmo_cc_msg_t *msg, uint32_t unique, const uint8_t *data, size_t data_size);
|
||||
int osmo_cc_get_ie_private(osmo_cc_msg_t *msg, int ie_repeat, uint32_t *unique, uint8_t *data, size_t data_size);
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
void osmo_cc_set_log_cat(int cc_log_cat);
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
void osmo_cc_set_rtp_ports(osmo_cc_session_config_t *conf, uint16_t from, uint16_t to);
|
||||
int osmo_cc_rtp_open(osmo_cc_session_media_t *media);
|
||||
int osmo_cc_rtp_connect(osmo_cc_session_media_t *media);
|
||||
void osmo_cc_rtp_send(osmo_cc_session_codec_t *codec, uint8_t *data, int len, uint8_t marker, int inc_sequence,
|
||||
int inc_timestamp, void *priv);
|
||||
void osmo_cc_rtp_send_ts(osmo_cc_session_codec_t *codec, uint8_t *data, int len, uint8_t marker, uint16_t tx_sequence,
|
||||
uint32_t tx_timestamp, void *priv);
|
||||
void osmo_cc_rtp_close(osmo_cc_session_media_t *media);
|
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
void osmo_cc_help_screen(void);
|
||||
char *osmo_cc_strtok_quotes(const char **text_p);
|
||||
int osmo_cc_add_screen(osmo_cc_endpoint_t *ep, const char *text);
|
||||
void osmo_cc_flush_screen(osmo_cc_screen_list_t *list);
|
||||
osmo_cc_msg_t *osmo_cc_screen_msg(osmo_cc_endpoint_t *ep, osmo_cc_msg_t *old_msg, int in, const char **routing_p);
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
char *osmo_cc_session_gensdp(struct osmo_cc_session *session, int accepted_only);
|
||||
struct osmo_cc_session *osmo_cc_session_parsesdp(osmo_cc_session_config_t *conf, void *priv, const char *_sdp);
|
||||
int osmo_cc_payload_type_by_attrs(uint8_t *fmt, const char *name, uint32_t *rate, int *channels);
|
||||
void osmo_cc_debug_sdp(const char *sdp);
|
|
@ -0,0 +1,153 @@
|
|||
#pragma once
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
/* configuration */
|
||||
|
||||
enum osmo_cc_session_nettype {
|
||||
osmo_cc_session_nettype_unknown = 0,
|
||||
osmo_cc_session_nettype_inet,
|
||||
};
|
||||
|
||||
enum osmo_cc_session_addrtype {
|
||||
osmo_cc_session_addrtype_unknown = 0,
|
||||
osmo_cc_session_addrtype_ipv4,
|
||||
osmo_cc_session_addrtype_ipv6,
|
||||
};
|
||||
|
||||
typedef struct osmo_cc_session_config {
|
||||
enum osmo_cc_session_nettype default_nettype;
|
||||
enum osmo_cc_session_addrtype default_addrtype;
|
||||
const char *default_unicast_address;
|
||||
uint16_t rtp_port_next;
|
||||
uint16_t rtp_port_from;
|
||||
uint16_t rtp_port_to;
|
||||
} osmo_cc_session_config_t;
|
||||
|
||||
/* session description, global part: */
|
||||
|
||||
typedef struct osmo_cc_session_origin {
|
||||
const char *username;
|
||||
const char *sess_id;
|
||||
const char *sess_version;
|
||||
const char *nettype;
|
||||
const char *addrtype;
|
||||
const char *unicast_address;
|
||||
} osmo_cc_session_origin_t;
|
||||
|
||||
/* session instance */
|
||||
typedef struct osmo_cc_session {
|
||||
osmo_cc_session_config_t *config;
|
||||
void *priv;
|
||||
osmo_cc_session_origin_t origin_local, origin_remote;
|
||||
const char *name;
|
||||
struct osmo_cc_session_media *media_list;
|
||||
} osmo_cc_session_t;
|
||||
|
||||
/* connection description: */
|
||||
|
||||
typedef struct osmo_cc_session_connection_data {
|
||||
enum osmo_cc_session_nettype nettype;
|
||||
const char *nettype_name;
|
||||
enum osmo_cc_session_addrtype addrtype;
|
||||
const char *addrtype_name;
|
||||
const char *address;
|
||||
} osmo_cc_session_connection_data_t;
|
||||
|
||||
/* one media of session description: */
|
||||
|
||||
enum osmo_cc_session_media_type {
|
||||
osmo_cc_session_media_type_unknown,
|
||||
osmo_cc_session_media_type_audio,
|
||||
osmo_cc_session_media_type_video,
|
||||
};
|
||||
|
||||
enum osmo_cc_session_media_proto {
|
||||
osmo_cc_session_media_proto_unknown,
|
||||
osmo_cc_session_media_proto_rtp,
|
||||
};
|
||||
|
||||
typedef struct osmo_cc_session_media_description {
|
||||
enum osmo_cc_session_media_type type;
|
||||
const char *type_name;
|
||||
uint16_t port_local, port_remote;
|
||||
enum osmo_cc_session_media_proto proto;
|
||||
const char *proto_name;
|
||||
} osmo_cc_session_media_description_t;
|
||||
|
||||
/* media entry */
|
||||
typedef struct osmo_cc_session_media {
|
||||
struct osmo_cc_session_media *next;
|
||||
osmo_cc_session_t *session;
|
||||
osmo_cc_session_media_description_t description;
|
||||
osmo_cc_session_connection_data_t connection_data_local, connection_data_remote;
|
||||
struct osmo_cc_session_codec *codec_list;
|
||||
int send, receive;
|
||||
void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number,
|
||||
uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len);
|
||||
struct sockaddr_storage rtp_sa, rtcp_sa;
|
||||
socklen_t rtp_slen, rtcp_slen;
|
||||
uint16_t *rtp_sport, *rtcp_sport; // pointers to the port inside sa sockaddr
|
||||
struct osmo_fd rtp_ofd;
|
||||
struct osmo_fd rtcp_ofd;
|
||||
uint32_t tx_ssrc, rx_ssrc;
|
||||
uint16_t tx_sequence, rx_sequence;
|
||||
uint32_t tx_timestamp, rx_timestamp;
|
||||
int accepted;
|
||||
} osmo_cc_session_media_t;
|
||||
|
||||
/* codec entry */
|
||||
typedef struct osmo_cc_session_codec {
|
||||
struct osmo_cc_session_codec *next;
|
||||
osmo_cc_session_media_t *media;
|
||||
uint8_t payload_type_local, payload_type_remote; /* local = towards local, remote = toward remote */
|
||||
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 *priv);
|
||||
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
int accepted;
|
||||
} osmo_cc_session_codec_t;
|
||||
|
||||
#define osmo_cc_session_for_each_media(head, m) \
|
||||
for (m = (head); m; m = m->next)
|
||||
|
||||
#define osmo_cc_session_for_each_codec(head, c) \
|
||||
for (c = (head); c; c = c->next)
|
||||
|
||||
void osmo_cc_set_local_peer(osmo_cc_session_config_t *conf, enum osmo_cc_session_nettype nettype,
|
||||
enum osmo_cc_session_addrtype addrtype, const char *address);
|
||||
osmo_cc_session_t *osmo_cc_new_session(osmo_cc_session_config_t *conf, void *priv, const char *username,
|
||||
const char *sess_id, const char *sess_version, enum osmo_cc_session_nettype nettype,
|
||||
enum osmo_cc_session_addrtype addrtype, const char *unicast_address, const char *session_name, int debug);
|
||||
void osmo_cc_free_session(osmo_cc_session_t *session);
|
||||
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype,
|
||||
enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type,
|
||||
uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive,
|
||||
void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number,
|
||||
uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len),
|
||||
int debug);
|
||||
void osmo_cc_free_media(osmo_cc_session_media_t *media);
|
||||
osmo_cc_session_codec_t *osmo_cc_add_codec(osmo_cc_session_media_t *media, const char *playload_name,
|
||||
uint32_t playload_rate, int playload_channels,
|
||||
void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv),
|
||||
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv),
|
||||
int debug);
|
||||
void osmo_cc_free_codec(osmo_cc_session_codec_t *codec);
|
||||
int osmo_cc_session_check(struct osmo_cc_session *session, int remote);
|
||||
const char *osmo_cc_session_send_offer(osmo_cc_session_t *session);
|
||||
osmo_cc_session_t *osmo_cc_session_receive_offer(osmo_cc_session_config_t *conf, void *priv, const char *sdp);
|
||||
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype,
|
||||
enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive,
|
||||
void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number,
|
||||
uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len));
|
||||
void osmo_cc_session_accept_codec(osmo_cc_session_codec_t *codec,
|
||||
void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv),
|
||||
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv));
|
||||
const char *osmo_cc_session_send_answer(osmo_cc_session_t *session);
|
||||
int osmo_cc_session_receive_answer(osmo_cc_session_t *session, const char *sdp);
|
||||
const char *osmo_cc_session_nettype2string(enum osmo_cc_session_nettype nettype);
|
||||
const char *osmo_cc_session_addrtype2string(enum osmo_cc_session_addrtype addrtype);
|
||||
const char *osmo_cc_session_media_type2string(enum osmo_cc_session_media_type media_type);
|
||||
const char *osmo_cc_session_media_proto2string(enum osmo_cc_session_media_proto media_proto);
|
||||
int osmo_cc_session_if_codec(osmo_cc_session_codec_t *codec, const char *name, uint32_t rate, int channels);
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#define OSMO_CC_DEFAULT_PORT 4200
|
||||
#define OSMO_CC_DEFAULT_PORT_MAX 4219
|
||||
|
||||
#define OSMO_CC_SOCKET_TX_KEEPALIVE 10,0
|
||||
#define OSMO_CC_SOCKET_RX_KEEPALIVE 20,0
|
||||
|
||||
struct osmo_cc_socket;
|
||||
|
||||
typedef struct osmo_cc_conn {
|
||||
struct osmo_cc_conn *next;
|
||||
struct osmo_cc_socket *os;
|
||||
struct osmo_fd ofd;
|
||||
uint32_t callref;
|
||||
int read_setup;
|
||||
int read_version;
|
||||
char read_version_string[sizeof(OSMO_CC_VERSION)]; /* must include 0-termination */
|
||||
int read_version_pos;
|
||||
int write_version;
|
||||
osmo_cc_msg_t read_hdr;
|
||||
osmo_cc_msg_t *read_msg;
|
||||
int read_pos;
|
||||
osmo_cc_msg_list_t *write_list;
|
||||
struct osmo_timer_list tx_keepalive_timer;
|
||||
struct osmo_timer_list rx_keepalive_timer;
|
||||
} osmo_cc_conn_t;
|
||||
|
||||
typedef struct osmo_cc_socket {
|
||||
struct osmo_fd ofd;
|
||||
osmo_cc_conn_t *conn_list;
|
||||
osmo_cc_msg_list_t *write_list;
|
||||
void (*recv_msg_cb)(void *priv, uint32_t callref, osmo_cc_msg_t *msg);
|
||||
void *priv;
|
||||
uint8_t location;
|
||||
} osmo_cc_socket_t;
|
||||
|
||||
int osmo_cc_open_socket(osmo_cc_socket_t *os, const char *host, uint16_t port, void *priv,
|
||||
void (*recv_msg_cb)(void *priv, uint32_t callref, osmo_cc_msg_t *msg), uint8_t location);
|
||||
void osmo_cc_close_socket(osmo_cc_socket_t *os);
|
||||
int osmo_cc_sock_send_msg(osmo_cc_socket_t *os, uint32_t callref, osmo_cc_msg_t *msg, const char *host, uint16_t port);
|
||||
int osmo_cc_handle_socket(osmo_cc_socket_t *os);
|
|
@ -0,0 +1,11 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: Osmo CC Library
|
||||
Description: C Utility Library
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -losmocc
|
||||
Cflags: -I${includedir}/
|
||||
Requires: libosmocore
|
|
@ -0,0 +1,20 @@
|
|||
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes) -I../include
|
||||
|
||||
lib_LTLIBRARIES = libosmocc.la
|
||||
|
||||
libosmocc_la_LDFLAGS = $(AM_LDFLAGS) \
|
||||
-no-undefined \
|
||||
$(NULL)
|
||||
libosmocc_la_LIBADD = $(COMMONLIBS)
|
||||
libosmocc_la_SOURCES = \
|
||||
message.c \
|
||||
socket.c \
|
||||
cause.c \
|
||||
screen.c \
|
||||
endpoint.c \
|
||||
session.c \
|
||||
sdp.c \
|
||||
rtp.c \
|
||||
helper.c \
|
||||
g711.c \
|
||||
misc.c
|
|
@ -0,0 +1,251 @@
|
|||
/* 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 <osmocom/cc/message.h>
|
||||
#include <osmocom/cc/cause.h>
|
||||
|
||||
|
||||
/* stolen from freeswitch, did some corrections */
|
||||
/* 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:
|
||||
case 485:
|
||||
case 604:
|
||||
return 1; //SWITCH_CAUSE_UNALLOCATED_NUMBER;
|
||||
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 18; //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 65; //SWITCH_CAUSE_BERER_CAPABILITY_NOT_IMPLEMENTED;
|
||||
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; (not specified)
|
||||
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;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,552 @@
|
|||
/*****************************************************************************\
|
||||
** **
|
||||
** PBX4Linux **
|
||||
** **
|
||||
**---------------------------------------------------------------------------**
|
||||
** Copyright: Andreas Eversberg (GPL) **
|
||||
** **
|
||||
** audio conversions for alaw and ulaw **
|
||||
** **
|
||||
\*****************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <osmocom/cc/g711.h>
|
||||
|
||||
/* ulaw -> signed 16-bit */
|
||||
static int16_t g711_ulaw_to_linear[256] =
|
||||
{
|
||||
0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84,
|
||||
0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84,
|
||||
0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84,
|
||||
0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84,
|
||||
0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804,
|
||||
0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004,
|
||||
0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444,
|
||||
0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844,
|
||||
0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64,
|
||||
0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64,
|
||||
0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74,
|
||||
0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74,
|
||||
0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc,
|
||||
0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c,
|
||||
0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0,
|
||||
0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0xffff,
|
||||
0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c,
|
||||
0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c,
|
||||
0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c,
|
||||
0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c,
|
||||
0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc,
|
||||
0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc,
|
||||
0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc,
|
||||
0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc,
|
||||
0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c,
|
||||
0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c,
|
||||
0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c,
|
||||
0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c,
|
||||
0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104,
|
||||
0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084,
|
||||
0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040,
|
||||
0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
|
||||
};
|
||||
|
||||
/* alaw -> signed 16-bit */
|
||||
static int16_t g711_alaw_flipped_to_linear[256] =
|
||||
{
|
||||
0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4,
|
||||
0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74,
|
||||
0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4,
|
||||
0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64,
|
||||
0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4,
|
||||
0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4,
|
||||
0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4,
|
||||
0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4,
|
||||
0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64,
|
||||
0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34,
|
||||
0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844,
|
||||
0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24,
|
||||
0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64,
|
||||
0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4,
|
||||
0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964,
|
||||
0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4,
|
||||
0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24,
|
||||
0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94,
|
||||
0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924,
|
||||
0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94,
|
||||
0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24,
|
||||
0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14,
|
||||
0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24,
|
||||
0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14,
|
||||
0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4,
|
||||
0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54,
|
||||
0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4,
|
||||
0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64,
|
||||
0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4,
|
||||
0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4,
|
||||
0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4,
|
||||
0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4
|
||||
};
|
||||
|
||||
/* Xlaw -> signed 16-bit */
|
||||
static int16_t g711_alaw_to_linear[256];
|
||||
static int16_t g711_ulaw_flipped_to_linear[256];
|
||||
|
||||
/* signed 16-bit -> Xlaw */
|
||||
static uint8_t g711_linear_to_alaw_flipped[65536];
|
||||
static uint8_t g711_linear_to_ulaw_flipped[65536];
|
||||
static uint8_t g711_linear_to_alaw[65536];
|
||||
static uint8_t g711_linear_to_ulaw[65536];
|
||||
|
||||
/* transcode */
|
||||
static uint8_t g711_alaw_to_ulaw[256];
|
||||
static uint8_t g711_ulaw_to_alaw[256];
|
||||
static uint8_t g711_alaw_flipped_to_ulaw[256];
|
||||
static uint8_t g711_ulaw_flipped_to_alaw[256];
|
||||
static uint8_t g711_alaw_to_ulaw_flipped[256];
|
||||
static uint8_t g711_ulaw_to_alaw_flipped[256];
|
||||
|
||||
/* table is used to generate linear_to_alaw */
|
||||
static int16_t g711_alaw_relations[] =
|
||||
{
|
||||
0x8684, 0x55, 0x8a84, 0xd5, 0x8e84, 0x15, 0x9284, 0x95,
|
||||
0x9684, 0x75, 0x9a84, 0xf5, 0x9e84, 0x35, 0xa284, 0xb5,
|
||||
0xa684, 0x45, 0xaa84, 0xc5, 0xae84, 0x05, 0xb284, 0x85,
|
||||
0xb684, 0x65, 0xba84, 0xe5, 0xbe84, 0x25, 0xc184, 0xa5,
|
||||
0xc384, 0x5d, 0xc584, 0xdd, 0xc784, 0x1d, 0xc984, 0x9d,
|
||||
0xcb84, 0x7d, 0xcd84, 0xfd, 0xcf84, 0x3d, 0xd184, 0xbd,
|
||||
0xd384, 0x4d, 0xd584, 0xcd, 0xd784, 0x0d, 0xd984, 0x8d,
|
||||
0xdb84, 0x6d, 0xdd84, 0xed, 0xdf84, 0x2d, 0xe104, 0xad,
|
||||
0xe204, 0x51, 0xe304, 0xd1, 0xe404, 0x11, 0xe504, 0x91,
|
||||
0xe604, 0x71, 0xe704, 0xf1, 0xe804, 0x31, 0xe904, 0xb1,
|
||||
0xea04, 0x41, 0xeb04, 0xc1, 0xec04, 0x01, 0xed04, 0x81,
|
||||
0xee04, 0x61, 0xef04, 0xe1, 0xf004, 0x21, 0xf0c4, 0x59,
|
||||
0xf0c4, 0xa1, 0xf144, 0xd9, 0xf1c4, 0x19, 0xf244, 0x99,
|
||||
0xf2c4, 0x79, 0xf344, 0xf9, 0xf3c4, 0x39, 0xf444, 0xb9,
|
||||
0xf4c4, 0x49, 0xf544, 0xc9, 0xf5c4, 0x09, 0xf644, 0x89,
|
||||
0xf6c4, 0x69, 0xf744, 0xe9, 0xf7c4, 0x29, 0xf844, 0x57,
|
||||
0xf844, 0xa9, 0xf8a4, 0xd7, 0xf8e4, 0x17, 0xf924, 0x97,
|
||||
0xf964, 0x77, 0xf9a4, 0xf7, 0xf9e4, 0x37, 0xfa24, 0xb7,
|
||||
0xfa64, 0x47, 0xfaa4, 0xc7, 0xfae4, 0x07, 0xfb24, 0x87,
|
||||
0xfb64, 0x67, 0xfba4, 0xe7, 0xfbe4, 0x27, 0xfc24, 0x5f,
|
||||
0xfc24, 0xa7, 0xfc64, 0x1f, 0xfc64, 0xdf, 0xfc94, 0x9f,
|
||||
0xfcb4, 0x7f, 0xfcd4, 0xff, 0xfcf4, 0x3f, 0xfd14, 0xbf,
|
||||
0xfd34, 0x4f, 0xfd54, 0xcf, 0xfd74, 0x0f, 0xfd94, 0x8f,
|
||||
0xfdb4, 0x6f, 0xfdd4, 0xef, 0xfdf4, 0x2f, 0xfe14, 0x53,
|
||||
0xfe14, 0xaf, 0xfe34, 0x13, 0xfe34, 0xd3, 0xfe54, 0x73,
|
||||
0xfe54, 0x93, 0xfe74, 0x33, 0xfe74, 0xf3, 0xfe8c, 0xb3,
|
||||
0xfe9c, 0x43, 0xfeac, 0xc3, 0xfebc, 0x03, 0xfecc, 0x83,
|
||||
0xfedc, 0x63, 0xfeec, 0xe3, 0xfefc, 0x23, 0xff0c, 0xa3,
|
||||
0xff1c, 0x5b, 0xff2c, 0xdb, 0xff3c, 0x1b, 0xff4c, 0x9b,
|
||||
0xff5c, 0x7b, 0xff6c, 0xfb, 0xff7c, 0x3b, 0xff88, 0xbb,
|
||||
0xff98, 0x4b, 0xffa8, 0xcb, 0xffb8, 0x0b, 0xffc8, 0x8b,
|
||||
0xffd8, 0x6b, 0xffe8, 0xeb, 0xfff8, 0x2b, 0xfff8, 0xab,
|
||||
0x0008, 0x2a, 0x0008, 0xaa, 0x0018, 0xea, 0x0028, 0x6a,
|
||||
0x0038, 0x8a, 0x0048, 0x0a, 0x0058, 0xca, 0x0068, 0x4a,
|
||||
0x0078, 0xba, 0x0084, 0x3a, 0x0094, 0xfa, 0x00a4, 0x7a,
|
||||
0x00b4, 0x9a, 0x00c4, 0x1a, 0x00d4, 0xda, 0x00e4, 0x5a,
|
||||
0x00f4, 0xa2, 0x0104, 0x22, 0x0114, 0xe2, 0x0124, 0x62,
|
||||
0x0134, 0x82, 0x0144, 0x02, 0x0154, 0xc2, 0x0164, 0x42,
|
||||
0x0174, 0xb2, 0x018c, 0x32, 0x018c, 0xf2, 0x01ac, 0x72,
|
||||
0x01ac, 0x92, 0x01cc, 0x12, 0x01cc, 0xd2, 0x01ec, 0x52,
|
||||
0x01ec, 0xae, 0x020c, 0x2e, 0x022c, 0xee, 0x024c, 0x6e,
|
||||
0x026c, 0x8e, 0x028c, 0x0e, 0x02ac, 0xce, 0x02cc, 0x4e,
|
||||
0x02ec, 0xbe, 0x030c, 0x3e, 0x032c, 0xfe, 0x034c, 0x7e,
|
||||
0x036c, 0x9e, 0x039c, 0x1e, 0x039c, 0xde, 0x03dc, 0x5e,
|
||||
0x03dc, 0xa6, 0x041c, 0x26, 0x045c, 0xe6, 0x049c, 0x66,
|
||||
0x04dc, 0x86, 0x051c, 0x06, 0x055c, 0xc6, 0x059c, 0x46,
|
||||
0x05dc, 0xb6, 0x061c, 0x36, 0x065c, 0xf6, 0x069c, 0x76,
|
||||
0x06dc, 0x96, 0x071c, 0x16, 0x075c, 0xd6, 0x07bc, 0x56,
|
||||
0x07bc, 0xa8, 0x083c, 0x28, 0x08bc, 0xe8, 0x093c, 0x68,
|
||||
0x09bc, 0x88, 0x0a3c, 0x08, 0x0abc, 0xc8, 0x0b3c, 0x48,
|
||||
0x0bbc, 0xb8, 0x0c3c, 0x38, 0x0cbc, 0xf8, 0x0d3c, 0x78,
|
||||
0x0dbc, 0x98, 0x0e3c, 0x18, 0x0ebc, 0xd8, 0x0f3c, 0x58,
|
||||
0x0f3c, 0xa0, 0x0ffc, 0x20, 0x10fc, 0xe0, 0x11fc, 0x60,
|
||||
0x12fc, 0x80, 0x13fc, 0x00, 0x14fc, 0xc0, 0x15fc, 0x40,
|
||||
0x16fc, 0xb0, 0x17fc, 0x30, 0x18fc, 0xf0, 0x19fc, 0x70,
|
||||
0x1afc, 0x90, 0x1bfc, 0x10, 0x1cfc, 0xd0, 0x1dfc, 0x50,
|
||||
0x1efc, 0xac, 0x207c, 0x2c, 0x227c, 0xec, 0x247c, 0x6c,
|
||||
0x267c, 0x8c, 0x287c, 0x0c, 0x2a7c, 0xcc, 0x2c7c, 0x4c,
|
||||
0x2e7c, 0xbc, 0x307c, 0x3c, 0x327c, 0xfc, 0x347c, 0x7c,
|
||||
0x367c, 0x9c, 0x387c, 0x1c, 0x3a7c, 0xdc, 0x3c7c, 0x5c,
|
||||
0x3e7c, 0xa4, 0x417c, 0x24, 0x457c, 0xe4, 0x497c, 0x64,
|
||||
0x4d7c, 0x84, 0x517c, 0x04, 0x557c, 0xc4, 0x597c, 0x44,
|
||||
0x5d7c, 0xb4, 0x617c, 0x34, 0x657c, 0xf4, 0x697c, 0x74,
|
||||
0x6d7c, 0x94, 0x717c, 0x14, 0x757c, 0xd4, 0x797c, 0x54
|
||||
};
|
||||
|
||||
uint8_t g711_flip[256];
|
||||
|
||||
static int g711_initialized = 0;
|
||||
|
||||
/* generate tables
|
||||
*/
|
||||
void g711_init(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
/* flip tables */
|
||||
for (i = 0; i < 256; i++) {
|
||||
g711_flip[i]
|
||||
= ((i & 1) << 7)
|
||||
+ ((i & 2) << 5)
|
||||
+ ((i & 4) << 3)
|
||||
+ ((i & 8) << 1)
|
||||
+ ((i & 16) >> 1)
|
||||
+ ((i & 32) >> 3)
|
||||
+ ((i & 64) >> 5)
|
||||
+ ((i & 128) >> 7);
|
||||
g711_alaw_to_linear[i] = g711_alaw_flipped_to_linear[g711_flip[i]];
|
||||
g711_ulaw_flipped_to_linear[i] = g711_ulaw_to_linear[g711_flip[i]];
|
||||
}
|
||||
|
||||
/* linear to alaw tables */
|
||||
i = j = 0;
|
||||
while(i < 65536) {
|
||||
if (i - 32768 > g711_alaw_relations[j << 1])
|
||||
j++;
|
||||
if (j > 255)
|
||||
j = 255;
|
||||
g711_linear_to_alaw_flipped[(i - 32768) & 0xffff] = g711_alaw_relations[(j << 1) | 1];
|
||||
g711_linear_to_alaw[(i - 32768) & 0xffff] = g711_flip[g711_alaw_relations[(j << 1) | 1]];
|
||||
i++;
|
||||
}
|
||||
|
||||
/* linear to ulaw tables */
|
||||
i = j = 0;
|
||||
while(i < 32768) {
|
||||
if (i - 32768 > g711_ulaw_to_linear[j])
|
||||
j++;
|
||||
g711_linear_to_ulaw[(i - 32768) & 0xffff] = j;
|
||||
g711_linear_to_ulaw_flipped[(i - 32768) & 0xffff] = g711_flip[j];
|
||||
i++;
|
||||
}
|
||||
j = 255;
|
||||
while(i < 65536) {
|
||||
if (i - 32768 > g711_ulaw_to_linear[j])
|
||||
j--;
|
||||
g711_linear_to_ulaw[(i - 32768) & 0xffff] = j;
|
||||
g711_linear_to_ulaw_flipped[(i - 32768) & 0xffff] = g711_flip[j];
|
||||
i++;
|
||||
}
|
||||
|
||||
/* transcode */
|
||||
for (i = 0; i < 256; i++) {
|
||||
g711_alaw_to_ulaw[i] = g711_linear_to_ulaw[(uint16_t)g711_alaw_to_linear[i]];
|
||||
g711_ulaw_to_alaw[i] = g711_linear_to_alaw[(uint16_t)g711_ulaw_to_linear[i]];
|
||||
g711_alaw_flipped_to_ulaw[i] = g711_linear_to_ulaw[(uint16_t)g711_alaw_to_linear[g711_flip[i]]];
|
||||
g711_ulaw_flipped_to_alaw[i] = g711_linear_to_alaw[(uint16_t)g711_ulaw_to_linear[g711_flip[i]]];
|
||||
g711_alaw_to_ulaw_flipped[i] = g711_flip[g711_linear_to_ulaw[(uint16_t)g711_alaw_to_linear[i]]];
|
||||
g711_ulaw_to_alaw_flipped[i] = g711_flip[g711_linear_to_alaw[(uint16_t)g711_ulaw_to_linear[i]]];
|
||||
}
|
||||
|
||||
g711_initialized = 1;
|
||||
}
|
||||
|
||||
void g711_encode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len,
|
||||
void __attribute__((unused)) *priv)
|
||||
{
|
||||
int16_t *src = (int16_t *)src_data;
|
||||
uint8_t *dst;
|
||||
int len = src_len / 2, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_linear_to_alaw_flipped[(uint16_t)src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_encode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len,
|
||||
void __attribute__((unused)) *priv)
|
||||
{
|
||||
int16_t *src = (int16_t *)src_data;
|
||||
uint8_t *dst;
|
||||
int len = src_len / 2, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_linear_to_ulaw_flipped[(uint16_t)src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_decode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len,
|
||||
void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data;
|
||||
int16_t *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len * 2);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_alaw_flipped_to_linear[src[i]];
|
||||
*dst_data = (uint8_t *)dst;
|
||||
*dst_len = len * 2;
|
||||
}
|
||||
|
||||
void g711_decode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len,
|
||||
void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data;
|
||||
int16_t *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len * 2);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_ulaw_flipped_to_linear[src[i]];
|
||||
*dst_data = (uint8_t *)dst;
|
||||
*dst_len = len * 2;
|
||||
}
|
||||
|
||||
void g711_encode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len,
|
||||
void __attribute__((unused)) *priv)
|
||||
{
|
||||
int16_t *src = (int16_t *)src_data;
|
||||
uint8_t *dst;
|
||||
int len = src_len / 2, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_linear_to_alaw[(uint16_t)src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_encode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len,
|
||||
void __attribute__((unused)) *priv)
|
||||
{
|
||||
int16_t *src = (int16_t *)src_data;
|
||||
uint8_t *dst;
|
||||
int len = src_len / 2, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_linear_to_ulaw[(uint16_t)src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_decode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len,
|
||||
void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data;
|
||||
int16_t *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len * 2);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_alaw_to_linear[src[i]];
|
||||
*dst_data = (uint8_t *)dst;
|
||||
*dst_len = len * 2;
|
||||
}
|
||||
|
||||
void g711_decode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len,
|
||||
void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data;
|
||||
int16_t *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len * 2);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_ulaw_to_linear[src[i]];
|
||||
*dst_data = (uint8_t *)dst;
|
||||
*dst_len = len * 2;
|
||||
}
|
||||
|
||||
void g711_transcode_alaw_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len,
|
||||
void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data, *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_alaw_to_ulaw[src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_transcode_alaw_flipped_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len,
|
||||
void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data, *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_alaw_flipped_to_ulaw[src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_transcode_alaw_to_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len,
|
||||
void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data, *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_alaw_to_ulaw_flipped[src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_transcode_ulaw_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len,
|
||||
void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data, *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_ulaw_to_alaw[src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_transcode_ulaw_flipped_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len,
|
||||
void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data, *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_ulaw_flipped_to_alaw[src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_transcode_ulaw_to_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len,
|
||||
void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data, *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_ulaw_to_alaw_flipped[src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_transcode_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len,
|
||||
void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data, *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_flip[src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
|
@ -0,0 +1,245 @@
|
|||
/* 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 <osmocom/core/logging.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/cc/session.h>
|
||||
#include <osmocom/cc/message.h>
|
||||
#include <osmocom/cc/rtp.h>
|
||||
#include <osmocom/cc/helper.h>
|
||||
|
||||
extern int DLCC;
|
||||
|
||||
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv,
|
||||
struct osmo_cc_helper_audio_codecs *codecs,
|
||||
void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number,
|
||||
uint32_t timestamp, uint32_t ssrc, 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(conf, 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(osmo_cc_session_config_t *conf, void *priv,
|
||||
struct osmo_cc_helper_audio_codecs *codecs,
|
||||
void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number,
|
||||
uint32_t timestamp, uint32_t ssrc, 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)
|
||||
{
|
||||
return osmo_cc_helper_audio_accept_te(conf, priv, codecs, receiver, msg, session_p, codec_p, NULL,
|
||||
force_our_codec);
|
||||
}
|
||||
|
||||
const char *osmo_cc_helper_audio_accept_te(osmo_cc_session_config_t *conf, void *priv,
|
||||
struct osmo_cc_helper_audio_codecs *codecs,
|
||||
void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number,
|
||||
uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len),
|
||||
osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p,
|
||||
osmo_cc_session_codec_t **telephone_event_p, int force_our_codec)
|
||||
{
|
||||
char offer_sdp[65536];
|
||||
const char *accept_sdp;
|
||||
osmo_cc_session_media_t *media, *selected_media;
|
||||
osmo_cc_session_codec_t *codec, *selected_codec, *telephone_event;
|
||||
int rc;
|
||||
int i, selected_codec_i, telephone_event_i;
|
||||
|
||||
if (*session_p) {
|
||||
LOGP(DLCC, LOGL_ERROR, "Session already set, please fix!\n");
|
||||
abort();
|
||||
}
|
||||
if (*codec_p) {
|
||||
LOGP(DLCC, LOGL_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) {
|
||||
LOGP(DLCC, LOGL_ERROR, "There is no SDP included in setup request.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*session_p = osmo_cc_session_receive_offer(conf, priv, offer_sdp);
|
||||
if (!*session_p) {
|
||||
LOGP(DLCC, LOGL_ERROR, "Failed to parse SDP.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
selected_media = NULL;
|
||||
osmo_cc_session_for_each_media((*session_p)->media_list, media) {
|
||||
/* only audio */
|
||||
if (media->description.type != osmo_cc_session_media_type_audio)
|
||||
continue;
|
||||
selected_codec_i = -1;
|
||||
selected_codec = NULL;
|
||||
telephone_event_i = -1;
|
||||
telephone_event = NULL;
|
||||
osmo_cc_session_for_each_codec(media->codec_list, codec) {
|
||||
if (!!strcasecmp(codec->payload_name, "telephone-event")) {
|
||||
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_codec_i < 0 || i < selected_codec_i) {
|
||||
selected_codec = codec;
|
||||
selected_codec_i = i;
|
||||
selected_media = media;
|
||||
}
|
||||
/* if we don't force our preferred codec,
|
||||
* use the preferred one from the remote */
|
||||
if (!force_our_codec)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* special case: add telephone-event, if supported */
|
||||
for (i = 0; codecs[i].payload_name; i++) {
|
||||
if (!!strcasecmp(codecs[i].payload_name, "telephone-event"))
|
||||
continue;
|
||||
telephone_event = codec;
|
||||
telephone_event_i = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* codec is selected within this media, we are done */
|
||||
if (selected_codec)
|
||||
break;
|
||||
}
|
||||
if (!selected_codec) {
|
||||
LOGP(DLCC, LOGL_ERROR, "No codec found in setup message that we support.\n");
|
||||
osmo_cc_free_session(*session_p);
|
||||
*session_p = NULL;
|
||||
return NULL;
|
||||
}
|
||||
osmo_cc_session_accept_codec(selected_codec, codecs[selected_codec_i].encoder,
|
||||
codecs[selected_codec_i].decoder);
|
||||
if (telephone_event)
|
||||
osmo_cc_session_accept_codec(telephone_event, codecs[telephone_event_i].encoder,
|
||||
codecs[telephone_event_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;
|
||||
if (telephone_event_p)
|
||||
*telephone_event_p = telephone_event;
|
||||
|
||||
accept_sdp = osmo_cc_session_send_answer(*session_p);
|
||||
if (!accept_sdp) {
|
||||
osmo_cc_free_session(*session_p);
|
||||
*session_p = NULL;
|
||||
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)
|
||||
{
|
||||
return osmo_cc_helper_audio_negotiate_te(msg, session_p, codec_p, NULL);
|
||||
}
|
||||
|
||||
int osmo_cc_helper_audio_negotiate_te(osmo_cc_msg_t *msg, osmo_cc_session_t **session_p,
|
||||
osmo_cc_session_codec_t **codec_p, osmo_cc_session_codec_t **telephone_event_p)
|
||||
{
|
||||
char sdp[65536];
|
||||
osmo_cc_session_media_t *media;
|
||||
osmo_cc_session_codec_t *codec;
|
||||
int rc;
|
||||
|
||||
if (!(*session_p)) {
|
||||
LOGP(DLCC, LOGL_ERROR, "Session not set, please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
/* skip not accepted medias */
|
||||
if (!media->accepted)
|
||||
continue;
|
||||
/* only audio */
|
||||
if (media->description.type != osmo_cc_session_media_type_audio)
|
||||
continue;
|
||||
osmo_cc_session_for_each_codec(media->codec_list, codec) {
|
||||
/* skip not accepted codecs */
|
||||
if (!codec->accepted)
|
||||
continue;
|
||||
if (!!strcasecmp(codec->payload_name, "telephone-event")) {
|
||||
/* select first codec, if one was accpeted */
|
||||
if (!(*codec_p)) {
|
||||
LOGP(DLCC, LOGL_DEBUG, "Select codec '%s'.\n", codec->payload_name);
|
||||
*codec_p = codec;
|
||||
}
|
||||
} else {
|
||||
if (telephone_event_p && !(*telephone_event_p)) {
|
||||
LOGP(DLCC, LOGL_DEBUG, "select telephone event codec.\n");
|
||||
*telephone_event_p = codec;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (*codec_p) {
|
||||
osmo_cc_rtp_connect(media);
|
||||
/* no more media streams */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!(*codec_p)) {
|
||||
LOGP(DLCC, LOGL_ERROR, "No codec found in setup reply message that we support.\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,27 @@
|
|||
/* Endpoint and call process handling
|
||||
*
|
||||
* (C) 2024 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 <osmocom/core/logging.h>
|
||||
|
||||
int DLCC = DLGLOBAL;
|
||||
|
||||
void osmo_cc_set_log_cat(int cc_log_cat)
|
||||
{
|
||||
DLCC = cc_log_cat;
|
||||
}
|
|
@ -0,0 +1,527 @@
|
|||
/* Osmo-CC: RTP 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 <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/cc/endpoint.h>
|
||||
#include <osmocom/cc/rtp.h>
|
||||
|
||||
extern int DLCC;
|
||||
|
||||
#define RTP_VERSION 2
|
||||
|
||||
void osmo_cc_set_rtp_ports(osmo_cc_session_config_t *conf, uint16_t from, uint16_t to)
|
||||
{
|
||||
conf->rtp_port_next = from;
|
||||
conf->rtp_port_from = from;
|
||||
conf->rtp_port_to = to;
|
||||
}
|
||||
|
||||
struct rtp_hdr {
|
||||
uint8_t byte0;
|
||||
uint8_t byte1;
|
||||
uint16_t sequence;
|
||||
uint32_t timestamp;
|
||||
uint32_t ssrc;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct rtp_x_hdr {
|
||||
uint16_t by_profile;
|
||||
uint16_t length;
|
||||
} __attribute__((packed));
|
||||
|
||||
static int rtp_receive(struct sockaddr_storage *sa, socklen_t *slen, int sock, uint8_t **payload_p, int *payload_len_p,
|
||||
uint8_t *marker_p, uint8_t *pt_p, uint16_t *sequence_p, uint32_t *timestamp_p, uint32_t *ssrc_p)
|
||||
{
|
||||
static uint8_t data[2048];
|
||||
int len;
|
||||
struct rtp_hdr *rtph = (struct rtp_hdr *)data;
|
||||
uint8_t version, padding, extension, csrc_count, marker, payload_type;
|
||||
struct rtp_x_hdr *rtpxh;
|
||||
uint8_t *payload;
|
||||
int payload_len;
|
||||
int x_len;
|
||||
|
||||
len = recvfrom(sock, data, sizeof(data), 0, (struct sockaddr *)sa, slen);
|
||||
if (len < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return -EAGAIN;
|
||||
LOGP(DLCC, LOGL_DEBUG, "Read errno = %d (%s)\n", errno, strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
if (len < 12) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Received RTP frame too short (len = %d).\n", len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
version = rtph->byte0 >> 6;
|
||||
padding = (rtph->byte0 >> 5) & 1;
|
||||
extension = (rtph->byte0 >> 4) & 1;
|
||||
csrc_count = rtph->byte0 & 0x0f;
|
||||
marker = rtph->byte1 >> 7;
|
||||
payload_type = rtph->byte1 & 0x7f;
|
||||
*sequence_p = ntohs(rtph->sequence);
|
||||
*timestamp_p = ntohl(rtph->timestamp);
|
||||
*ssrc_p = ntohl(rtph->ssrc);
|
||||
|
||||
if (version != RTP_VERSION) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Received RTP version %d not supported.\n", version);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
payload = data + sizeof(*rtph) + (csrc_count << 2);
|
||||
payload_len = len - sizeof(*rtph) - (csrc_count << 2);
|
||||
if (payload_len < 0) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Received RTP frame too short (len = %d, csrc count = %d).\n", len, csrc_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (extension) {
|
||||
if (payload_len < (int)sizeof(*rtpxh)) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Received RTP frame too short for extension header.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
rtpxh = (struct rtp_x_hdr *)payload;
|
||||
x_len = ntohs(rtpxh->length) * 4 + sizeof(*rtpxh);
|
||||
payload += x_len;
|
||||
payload_len -= x_len;
|
||||
if (payload_len < (int)sizeof(*rtpxh)) {
|
||||
LOGP(DLCC, LOGL_NOTICE,
|
||||
"Received RTP frame too short, extension header exceeds frame length.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (padding) {
|
||||
if (payload_len < 1) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Received RTP frame too short for padding length.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
payload_len -= payload[payload_len - 1];
|
||||
if (payload_len < 0) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Received RTP frame padding is greater than payload.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
*payload_p = payload;
|
||||
*payload_len_p = payload_len;
|
||||
*marker_p = marker;
|
||||
*pt_p = payload_type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtcp_receive(struct sockaddr_storage *sa, socklen_t *slen, int sock)
|
||||
{
|
||||
static uint8_t data[2048];
|
||||
int len;
|
||||
|
||||
len = recvfrom(sock, data, sizeof(data), 0, (struct sockaddr *)sa, slen);
|
||||
if (len < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return -EAGAIN;
|
||||
LOGP(DLCC, LOGL_DEBUG, "Read errno = %d (%s)\n", errno, strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rtp_send(struct sockaddr_storage *sa, socklen_t slen, int sock, uint8_t *payload, int payload_len,
|
||||
uint8_t marker, uint8_t pt, uint16_t sequence, uint32_t timestamp, uint32_t ssrc)
|
||||
{
|
||||
struct rtp_hdr *rtph;
|
||||
char data[sizeof(*rtph) + payload_len];
|
||||
int len, rc;
|
||||
|
||||
rtph = (struct rtp_hdr *)data;
|
||||
len = sizeof(*rtph);
|
||||
rtph->byte0 = RTP_VERSION << 6;
|
||||
rtph->byte1 = pt | (marker << 7);
|
||||
rtph->sequence = htons(sequence);
|
||||
rtph->timestamp = htonl(timestamp);
|
||||
rtph->ssrc = htonl(ssrc);
|
||||
len += payload_len;
|
||||
if (len > (int)sizeof(data)) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Buffer overflow, please fix!.\n");
|
||||
abort();
|
||||
}
|
||||
memcpy(data + sizeof(*rtph), payload, payload_len);
|
||||
|
||||
rc = sendto(sock, data, len, 0, (struct sockaddr *)sa, slen);
|
||||
if (rc < 0)
|
||||
LOGP(DLCC, LOGL_DEBUG, "sendto errno = %d (%s)\n", errno, strerror(errno));
|
||||
}
|
||||
|
||||
static int rtp_listen_cb(struct osmo_fd *ofd, unsigned int when);
|
||||
static int rtcp_listen_cb(struct osmo_fd *ofd, unsigned int when);
|
||||
|
||||
/* open and bind RTP
|
||||
* set local port to what we bound
|
||||
*/
|
||||
int osmo_cc_rtp_open(osmo_cc_session_media_t *media)
|
||||
{
|
||||
osmo_cc_session_config_t *conf = media->session->config;
|
||||
int domain = 0; // make GCC happy
|
||||
uint16_t start_port;
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t slen = 0; // make GCC happy
|
||||
struct sockaddr_in6 *sa6;
|
||||
struct sockaddr_in *sa4;
|
||||
uint16_t *sport;
|
||||
int flags;
|
||||
int rc;
|
||||
|
||||
media->tx_ssrc = rand();
|
||||
|
||||
osmo_cc_rtp_close(media);
|
||||
|
||||
switch (media->connection_data_local.addrtype) {
|
||||
case osmo_cc_session_addrtype_ipv4:
|
||||
domain = AF_INET;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa4 = (struct sockaddr_in *)&sa;
|
||||
sa4->sin_family = domain;
|
||||
rc = inet_pton(AF_INET, media->connection_data_local.address, &sa4->sin_addr);
|
||||
if (rc < 1) {
|
||||
pton_error:
|
||||
LOGP(DLCC, LOGL_NOTICE, "Cannot bind to address '%s'.\n", media->connection_data_local.address);
|
||||
return -EINVAL;
|
||||
}
|
||||
sport = &sa4->sin_port;
|
||||
slen = sizeof(*sa4);
|
||||
break;
|
||||
case osmo_cc_session_addrtype_ipv6:
|
||||
domain = AF_INET6;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa6 = (struct sockaddr_in6 *)&sa;
|
||||
sa6->sin6_family = domain;
|
||||
rc = inet_pton(AF_INET6, media->connection_data_local.address, &sa6->sin6_addr);
|
||||
if (rc < 1)
|
||||
goto pton_error;
|
||||
sport = &sa6->sin6_port;
|
||||
slen = sizeof(*sa6);
|
||||
break;
|
||||
case osmo_cc_session_addrtype_unknown:
|
||||
LOGP(DLCC, LOGL_NOTICE, "Unsupported address type '%s'.\n", media->connection_data_local.addrtype_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* rtp_port_from/rtp_port_to may be changed at run time, so rtp_port_next can become out of range. */
|
||||
if (conf->rtp_port_next < conf->rtp_port_from || conf->rtp_port_next > conf->rtp_port_to)
|
||||
conf->rtp_port_next = conf->rtp_port_from;
|
||||
start_port = conf->rtp_port_next;
|
||||
while (1) {
|
||||
/* open sockets */
|
||||
rc = socket(domain, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (rc < 0) {
|
||||
socket_error:
|
||||
LOGP(DLCC, LOGL_ERROR, "Cannot create socket (domain=%d, errno=%d(%s))\n", domain, errno,
|
||||
strerror(errno));
|
||||
osmo_cc_rtp_close(media);
|
||||
return -EIO;
|
||||
}
|
||||
media->rtp_ofd.fd = rc;
|
||||
media->rtp_ofd.cb = rtp_listen_cb;
|
||||
media->rtp_ofd.data = media;
|
||||
media->rtp_ofd.when = OSMO_FD_READ;
|
||||
osmo_fd_register(&media->rtp_ofd);
|
||||
rc = socket(domain, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (rc < 0)
|
||||
goto socket_error;
|
||||
media->rtcp_ofd.fd = rc;
|
||||
media->rtcp_ofd.cb = rtcp_listen_cb;
|
||||
media->rtcp_ofd.data = media;
|
||||
media->rtcp_ofd.when = OSMO_FD_READ;
|
||||
osmo_fd_register(&media->rtcp_ofd);
|
||||
|
||||
/* bind sockets */
|
||||
*sport = htons(conf->rtp_port_next);
|
||||
rc = bind(media->rtp_ofd.fd, (struct sockaddr *)&sa, slen);
|
||||
if (rc < 0) {
|
||||
bind_error:
|
||||
osmo_cc_rtp_close(media);
|
||||
conf->rtp_port_next = (conf->rtp_port_next + 2 > conf->rtp_port_to) ? conf->rtp_port_from
|
||||
: (conf->rtp_port_next + 2);
|
||||
if (conf->rtp_port_next == start_port) {
|
||||
LOGP(DLCC, LOGL_ERROR, "Cannot bind socket (errno=%d(%s))\n", errno, strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
*sport = htons(conf->rtp_port_next + 1);
|
||||
rc = bind(media->rtcp_ofd.fd, (struct sockaddr *)&sa, slen);
|
||||
if (rc < 0)
|
||||
goto bind_error;
|
||||
media->description.port_local = conf->rtp_port_next;
|
||||
conf->rtp_port_next = (conf->rtp_port_next + 2 > conf->rtp_port_to) ? conf->rtp_port_from
|
||||
: (conf->rtp_port_next + 2);
|
||||
/* set nonblocking io, to prevent write to block */
|
||||
flags = fcntl(media->rtp_ofd.fd, F_GETFL);
|
||||
flags |= O_NONBLOCK;
|
||||
fcntl(media->rtp_ofd.fd, F_SETFL, flags);
|
||||
flags = fcntl(media->rtcp_ofd.fd, F_GETFL);
|
||||
flags |= O_NONBLOCK;
|
||||
fcntl(media->rtcp_ofd.fd, F_SETFL, flags);
|
||||
break;
|
||||
}
|
||||
|
||||
LOGP(DLCC, LOGL_DEBUG, "Opening media port %d\n", media->description.port_local);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* connect RTP
|
||||
* use remote port to connect to
|
||||
*/
|
||||
int osmo_cc_rtp_connect(osmo_cc_session_media_t *media)
|
||||
{
|
||||
struct sockaddr_in6 *sa6;
|
||||
struct sockaddr_in *sa4;
|
||||
int rc;
|
||||
|
||||
LOGP(DLCC, LOGL_DEBUG, "Connecting media port %d->%d (remote %s)\n",
|
||||
media->description.port_local, media->description.port_remote, media->connection_data_remote.address);
|
||||
|
||||
switch (media->connection_data_remote.addrtype) {
|
||||
case osmo_cc_session_addrtype_ipv4:
|
||||
memset(&media->rtp_sa, 0, sizeof(media->rtp_sa));
|
||||
sa4 = (struct sockaddr_in *)&media->rtp_sa;
|
||||
sa4->sin_family = AF_INET;
|
||||
rc = inet_pton(AF_INET, media->connection_data_remote.address, &sa4->sin_addr);
|
||||
if (rc < 1) {
|
||||
pton_error:
|
||||
LOGP(DLCC, LOGL_NOTICE, "Cannot connect to address '%s'.\n",
|
||||
media->connection_data_remote.address);
|
||||
return -EINVAL;
|
||||
}
|
||||
media->rtp_sport = &sa4->sin_port;
|
||||
media->rtp_slen = sizeof(*sa4);
|
||||
memcpy(&media->rtcp_sa, &media->rtp_sa, sizeof(*sa4));
|
||||
sa4 = (struct sockaddr_in *)&media->rtcp_sa;
|
||||
media->rtcp_sport = &sa4->sin_port;
|
||||
media->rtcp_slen = sizeof(*sa4);
|
||||
break;
|
||||
case osmo_cc_session_addrtype_ipv6:
|
||||
memset(&media->rtp_sa, 0, sizeof(media->rtp_sa));
|
||||
sa6 = (struct sockaddr_in6 *)&media->rtp_sa;
|
||||
sa6->sin6_family = AF_INET6;
|
||||
rc = inet_pton(AF_INET6, media->connection_data_remote.address, &sa6->sin6_addr);
|
||||
if (rc < 1)
|
||||
goto pton_error;
|
||||
media->rtp_sport = &sa6->sin6_port;
|
||||
media->rtp_slen = sizeof(*sa6);
|
||||
memcpy(&media->rtcp_sa, &media->rtp_sa, sizeof(*sa6));
|
||||
sa6 = (struct sockaddr_in6 *)&media->rtcp_sa;
|
||||
media->rtcp_sport = &sa6->sin6_port;
|
||||
media->rtcp_slen = sizeof(*sa6);
|
||||
break;
|
||||
case osmo_cc_session_addrtype_unknown:
|
||||
LOGP(DLCC, LOGL_NOTICE, "Unsupported address type '%s'.\n", media->connection_data_local.addrtype_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*media->rtp_sport = htons(media->description.port_remote);
|
||||
*media->rtcp_sport = htons(media->description.port_remote + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* send rtp data with given codec */
|
||||
void osmo_cc_rtp_send(osmo_cc_session_codec_t *codec, uint8_t *data, int len, uint8_t marker, int inc_sequence,
|
||||
int inc_timestamp, void *priv)
|
||||
{
|
||||
uint8_t *payload = NULL;
|
||||
int payload_len = 0;
|
||||
|
||||
if (!codec || !codec->media->rtp_ofd.fd)
|
||||
return;
|
||||
|
||||
if (codec->encoder)
|
||||
codec->encoder(data, len, &payload, &payload_len, priv);
|
||||
else {
|
||||
payload = data;
|
||||
payload_len = len;
|
||||
}
|
||||
|
||||
rtp_send(&codec->media->rtp_sa, codec->media->rtp_slen, codec->media->rtp_ofd.fd, payload, payload_len, marker,
|
||||
codec->payload_type_remote, codec->media->tx_sequence, codec->media->tx_timestamp,
|
||||
codec->media->tx_ssrc);
|
||||
codec->media->tx_sequence += inc_sequence;
|
||||
codec->media->tx_timestamp += inc_timestamp;
|
||||
|
||||
if (codec->encoder)
|
||||
free(payload);
|
||||
}
|
||||
|
||||
/* dito, but with absolute sequence and timestamp */
|
||||
void osmo_cc_rtp_send_ts(osmo_cc_session_codec_t *codec, uint8_t *data, int len, uint8_t marker, uint16_t tx_sequence,
|
||||
uint32_t tx_timestamp, void *priv)
|
||||
{
|
||||
uint8_t *payload = NULL;
|
||||
int payload_len = 0;
|
||||
|
||||
if (!codec || !codec->media->rtp_ofd.fd)
|
||||
return;
|
||||
|
||||
if (codec->encoder)
|
||||
codec->encoder(data, len, &payload, &payload_len, priv);
|
||||
else {
|
||||
payload = data;
|
||||
payload_len = len;
|
||||
}
|
||||
|
||||
rtp_send(&codec->media->rtp_sa, codec->media->rtp_slen, codec->media->rtp_ofd.fd, payload, payload_len, marker,
|
||||
codec->payload_type_remote, tx_sequence, tx_timestamp, codec->media->tx_ssrc);
|
||||
codec->media->tx_sequence = tx_sequence;
|
||||
codec->media->tx_timestamp = tx_timestamp;
|
||||
|
||||
if (codec->encoder)
|
||||
free(payload);
|
||||
}
|
||||
|
||||
static void check_port_translation(struct sockaddr_storage *sa, struct sockaddr_storage *media_sa, const char *what)
|
||||
{
|
||||
struct sockaddr_in6 *sa6, *sa6_2;
|
||||
struct sockaddr_in *sa4, *sa4_2;
|
||||
int from = 0, to = 0;
|
||||
|
||||
if (sa->ss_family != media_sa->ss_family)
|
||||
return;
|
||||
|
||||
switch (sa->ss_family) {
|
||||
case AF_INET:
|
||||
sa4 = (struct sockaddr_in *)sa;
|
||||
sa4_2 = (struct sockaddr_in *)media_sa;
|
||||
if (sa4->sin_port != sa4_2->sin_port) {
|
||||
if (!!memcmp(&sa4->sin_addr, &sa4_2->sin_addr, sizeof(struct in_addr)))
|
||||
break;
|
||||
from = ntohs(sa4_2->sin_port);
|
||||
to = ntohs(sa4->sin_port);
|
||||
sa4_2->sin_port = sa4->sin_port;
|
||||
}
|
||||
break;
|
||||
case AF_INET6:
|
||||
sa6 = (struct sockaddr_in6 *)sa;
|
||||
sa6_2 = (struct sockaddr_in6 *)media_sa;
|
||||
if (sa6->sin6_port != sa6_2->sin6_port) {
|
||||
if (!!memcmp(&sa6->sin6_addr, &sa6_2->sin6_addr, sizeof(struct in6_addr)))
|
||||
break;
|
||||
from = ntohs(sa6_2->sin6_port);
|
||||
to = ntohs(sa6->sin6_port);
|
||||
sa6_2->sin6_port = sa6->sin6_port;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (from)
|
||||
LOGP(DLCC, LOGL_NOTICE, "Remote sends with different %s port, changing from %d to %d!\n",
|
||||
what, from, to);
|
||||
}
|
||||
|
||||
static int rtp_listen_cb(struct osmo_fd *ofd, unsigned int when)
|
||||
{
|
||||
osmo_cc_session_media_t *media = ofd->data;
|
||||
int rc;
|
||||
uint8_t *payload = NULL;
|
||||
int payload_len = 0;
|
||||
uint8_t marker;
|
||||
uint8_t payload_type;
|
||||
osmo_cc_session_codec_t *codec;
|
||||
uint8_t *data;
|
||||
int len;
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t slen = sizeof(sa); // must be initialized and will be overwritten
|
||||
|
||||
if (when & OSMO_FD_READ) {
|
||||
rc = rtp_receive(&sa, &slen, media->rtp_ofd.fd, &payload, &payload_len, &marker, &payload_type,
|
||||
&media->rx_sequence, &media->rx_timestamp, &media->rx_ssrc);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
check_port_translation(&sa, &media->rtp_sa, "RTP");
|
||||
|
||||
/* search for codec */
|
||||
for (codec = media->codec_list; codec; codec = codec->next) {
|
||||
if (codec->payload_type_local == payload_type)
|
||||
break;
|
||||
}
|
||||
if (!codec) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Received RTP frame for unknown codec (payload_type = %d).\n",
|
||||
payload_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (codec->decoder)
|
||||
codec->decoder(payload, payload_len, &data, &len, media->session->priv);
|
||||
else {
|
||||
data = payload;
|
||||
len = payload_len;
|
||||
}
|
||||
|
||||
if (codec->media->receive)
|
||||
codec->media->receiver(codec, marker, media->rx_sequence, media->rx_timestamp, media->rx_ssrc,
|
||||
data, len);
|
||||
|
||||
if (codec->decoder)
|
||||
free(data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtcp_listen_cb(struct osmo_fd *ofd, unsigned int when)
|
||||
{
|
||||
osmo_cc_session_media_t *media = ofd->data;
|
||||
int rc;
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t slen = sizeof(sa); // must be initialized and will be overwritten
|
||||
|
||||
if (when & OSMO_FD_READ) {
|
||||
rc = rtcp_receive(&sa, &slen, media->rtcp_ofd.fd);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
check_port_translation(&sa, &media->rtcp_sa, "RTCP");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void osmo_cc_rtp_close(osmo_cc_session_media_t *media)
|
||||
{
|
||||
if (media->rtp_ofd.fd) {
|
||||
osmo_fd_unregister(&media->rtp_ofd);
|
||||
close(media->rtp_ofd.fd);
|
||||
media->rtp_ofd.fd = 0;
|
||||
}
|
||||
if (media->rtcp_ofd.fd) {
|
||||
osmo_fd_unregister(&media->rtcp_ofd);
|
||||
close(media->rtcp_ofd.fd);
|
||||
media->rtcp_ofd.fd = 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,716 @@
|
|||
/* Endpoint and call process handling
|
||||
*
|
||||
* (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 <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/cc/endpoint.h>
|
||||
#include <osmocom/cc/screen.h>
|
||||
|
||||
extern int DLCC;
|
||||
|
||||
#define SCREEN_QUESTIONMARK 1
|
||||
#define SCREEN_STAR 2
|
||||
#define SCREEN_AT 3
|
||||
|
||||
void osmo_cc_help_screen(void)
|
||||
{
|
||||
printf("Screening options:\n\n");
|
||||
|
||||
printf("screen-calling-in [attrs] <current caller ID> [attrs] <new caller ID>\n");
|
||||
printf("screen-called-in [attrs] <current dialed number> [attrs] <new dialed number>\n");
|
||||
printf("screen-calling-out [attrs] <current caller ID> [attrs] <new caller ID>\n");
|
||||
printf("screen-called-out [attrs] <current dialed number> [attrs] <new dialed number>\n\n");
|
||||
|
||||
printf("These options allow to screen an incoming or outgoing caller ID or dialed\n");
|
||||
printf("number. If 'the current caller ID' or 'current dialed number' matches, it will\n");
|
||||
printf("be replaced by 'new caller ID' or 'new dialed number'. 'incoming' means from\n");
|
||||
printf(" the interface and 'outgoing' means towards the interface.\n\n");
|
||||
|
||||
printf("Attributes prior 'current caller ID' or 'new dialed number' may be used to\n");
|
||||
printf("perform screening only if the attribute match. Attributes prior\n");
|
||||
printf("'new caller ID' or 'new dialed number' may be used to alter them. Attribute to\n");
|
||||
printf("define the type of number can be: 'unknown', 'international', 'national',\n");
|
||||
printf("'network', 'subscriber', 'abbreviated' Attribute to define the restriction of a\n");
|
||||
printf("caller ID: 'allowed', 'restricted'\n\n");
|
||||
|
||||
printf("The current caller ID or dialed number may contain one or more '?', to allow\n");
|
||||
printf("any digit to match. The current caller ID or dialed number may contain a '*',\n");
|
||||
printf("to allow any suffix to match from now on. The new caller ID or dialed number\n");
|
||||
printf("may contain a '*', to append the suffix from the current caller ID or dialed\n");
|
||||
printf("number.\n\n");
|
||||
|
||||
printf("When screening an incoming caller ID or dialed number, the '@' can be appended\n");
|
||||
printf("to the 'new caller ID', followed by a 'host:port', to route call to a special\n");
|
||||
printf("Osmo-CC endpoint. This way it is possible to do simple routing.\n\n");
|
||||
}
|
||||
|
||||
char *osmo_cc_strtok_quotes(const char **text_p)
|
||||
{
|
||||
static char token[1024];
|
||||
const char *text = *text_p;
|
||||
int i, quote;
|
||||
|
||||
/* skip spaces */
|
||||
while (*text) {
|
||||
if (*text > 32)
|
||||
break;
|
||||
text++;
|
||||
}
|
||||
|
||||
/* if eol, return NULL */
|
||||
if (!(*text))
|
||||
return NULL;
|
||||
|
||||
i = 0;
|
||||
quote = 0;
|
||||
while (*text) {
|
||||
/* escape allows all following characters */
|
||||
if (*text == '\\') {
|
||||
text++;
|
||||
if (*text)
|
||||
token[i++] = *text++;
|
||||
continue;
|
||||
}
|
||||
/* no quote, check for them or break on white space */
|
||||
if (quote == 0) {
|
||||
if (*text == '\'') {
|
||||
quote = 1;
|
||||
text++;
|
||||
continue;
|
||||
}
|
||||
if (*text == '\"') {
|
||||
quote = 2;
|
||||
text++;
|
||||
continue;
|
||||
}
|
||||
if (*text <= ' ')
|
||||
break;
|
||||
}
|
||||
/* single quote, check for unquote */
|
||||
if (quote == 1 && *text == '\'') {
|
||||
quote = 0;
|
||||
text++;
|
||||
continue;
|
||||
}
|
||||
/* double quote, check for unquote */
|
||||
if (quote == 2 && *text == '\"') {
|
||||
quote = 0;
|
||||
text++;
|
||||
continue;
|
||||
}
|
||||
/* copy character */
|
||||
token[i++] = *text++;
|
||||
}
|
||||
token[i] = '\0';
|
||||
|
||||
*text_p = text;
|
||||
return token;
|
||||
}
|
||||
|
||||
int osmo_cc_add_screen(osmo_cc_endpoint_t *ep, const char *text)
|
||||
{
|
||||
osmo_cc_screen_list_t **list_p = NULL, *list;
|
||||
const char *token;
|
||||
int no_present = 0, calling_in = 0, star_used, at_used;
|
||||
int i, j;
|
||||
|
||||
star_used = 0;
|
||||
if (!strncasecmp(text, "screen-calling-in", 17)) {
|
||||
text += 17;
|
||||
list_p = &ep->screen_calling_in;
|
||||
no_present = 1;
|
||||
calling_in = 1;
|
||||
} else if (!strncasecmp(text, "screen-called-in", 16)) {
|
||||
text += 16;
|
||||
list_p = &ep->screen_called_in;
|
||||
calling_in = 1;
|
||||
} else if (!strncasecmp(text, "screen-calling-out", 18)) {
|
||||
text += 18;
|
||||
list_p = &ep->screen_calling_out;
|
||||
no_present = 1;
|
||||
} else if (!strncasecmp(text, "screen-called-out", 17)) {
|
||||
text += 17;
|
||||
list_p = &ep->screen_called_out;
|
||||
} else {
|
||||
LOGP(DLCC, LOGL_ERROR, "Invalid screening definition \"%s\". It must start with 'screen-calling-in' "
|
||||
"or 'screen-called-in' or 'screen-calling-out' or 'screen-called-out'\n", text);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* skip space behind screen list string */
|
||||
while (*text) {
|
||||
if (*text > 32)
|
||||
break;
|
||||
text++;
|
||||
}
|
||||
|
||||
list = calloc(1, sizeof(*list));
|
||||
if (!list)
|
||||
return -ENOMEM;
|
||||
|
||||
next_from:
|
||||
token = osmo_cc_strtok_quotes(&text);
|
||||
if (!token) {
|
||||
free(list);
|
||||
LOGP(DLCC, LOGL_ERROR, "Missing 'from' string in screening definition \"%s\". If the string shall be "
|
||||
"empty, use double quotes. (\'\' or \"\")\n", text);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!strcasecmp(token, "unknown")) {
|
||||
list->has_from_type = 1;
|
||||
list->from_type = OSMO_CC_TYPE_UNKNOWN;
|
||||
goto next_from;
|
||||
} else
|
||||
if (!strcasecmp(token, "international")) {
|
||||
list->has_from_type = 1;
|
||||
list->from_type = OSMO_CC_TYPE_INTERNATIONAL;
|
||||
goto next_from;
|
||||
} else
|
||||
if (!strcasecmp(token, "national")) {
|
||||
list->has_from_type = 1;
|
||||
list->from_type = OSMO_CC_TYPE_NATIONAL;
|
||||
goto next_from;
|
||||
} else
|
||||
if (!strcasecmp(token, "network")) {
|
||||
list->has_from_type = 1;
|
||||
list->from_type = OSMO_CC_TYPE_NETWORK;
|
||||
goto next_from;
|
||||
} else
|
||||
if (!strcasecmp(token, "subscriber")) {
|
||||
list->has_from_type = 1;
|
||||
list->from_type = OSMO_CC_TYPE_SUBSCRIBER;
|
||||
goto next_from;
|
||||
} else
|
||||
if (!strcasecmp(token, "abbreviated")) {
|
||||
list->has_from_type = 1;
|
||||
list->from_type = OSMO_CC_TYPE_ABBREVIATED;
|
||||
goto next_from;
|
||||
} else
|
||||
if (!strcasecmp(token, "allowed")) {
|
||||
if (no_present) {
|
||||
no_present_error:
|
||||
free(list);
|
||||
LOGP(DLCC, LOGL_ERROR, "Error in screening definition '%s'.\n", text);
|
||||
LOGP(DLCC, LOGL_ERROR, "Keyword '%s' not allowed in screen entry for called number\n", token);
|
||||
return -EINVAL;
|
||||
}
|
||||
list->has_from_present = 1;
|
||||
list->from_present = OSMO_CC_PRESENT_ALLOWED;
|
||||
goto next_from;
|
||||
} else
|
||||
if (!strcasecmp(token, "restricted")) {
|
||||
if (no_present)
|
||||
goto no_present_error;
|
||||
list->has_from_present = 1;
|
||||
list->from_present = OSMO_CC_PRESENT_RESTRICTED;
|
||||
goto next_from;
|
||||
} else {
|
||||
star_used = 0;
|
||||
for (i = j = 0; token[i] && j < (int)sizeof(list->from) - 1; i++, j++) {
|
||||
if (token[i] == '?')
|
||||
list->from[j] = SCREEN_QUESTIONMARK;
|
||||
else
|
||||
if (token[i] == '*') {
|
||||
if (star_used) {
|
||||
free(list);
|
||||
LOGP(DLCC, LOGL_ERROR, "Error in screening definition '%s'.\n", text);
|
||||
LOGP(DLCC, LOGL_ERROR, "The '*' may be used only once.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
list->from[j] = SCREEN_STAR;
|
||||
star_used = 1;
|
||||
} else
|
||||
if (token[i] == '\\' && token[i + 1] != '\0')
|
||||
list->from[j] = token[++i];
|
||||
else
|
||||
list->from[j] = token[i];
|
||||
}
|
||||
list->from[j] = '\0';
|
||||
}
|
||||
|
||||
next_to:
|
||||
token = osmo_cc_strtok_quotes(&text);
|
||||
if (!token) {
|
||||
free(list);
|
||||
LOGP(DLCC, LOGL_ERROR, "Error in screening definition '%s'.\n", text);
|
||||
LOGP(DLCC, LOGL_ERROR,
|
||||
"Missing screening result. If the string shall be empty, use double quotes. (\'\' or \"\")\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!strcasecmp(token, "unknown")) {
|
||||
list->has_to_type = 1;
|
||||
list->to_type = OSMO_CC_TYPE_UNKNOWN;
|
||||
goto next_to;
|
||||
} else
|
||||
if (!strcasecmp(token, "international")) {
|
||||
list->has_to_type = 1;
|
||||
list->to_type = OSMO_CC_TYPE_INTERNATIONAL;
|
||||
goto next_to;
|
||||
} else
|
||||
if (!strcasecmp(token, "national")) {
|
||||
list->has_to_type = 1;
|
||||
list->to_type = OSMO_CC_TYPE_NATIONAL;
|
||||
goto next_to;
|
||||
} else
|
||||
if (!strcasecmp(token, "network")) {
|
||||
list->has_to_type = 1;
|
||||
list->to_type = OSMO_CC_TYPE_NETWORK;
|
||||
goto next_to;
|
||||
} else
|
||||
if (!strcasecmp(token, "subscriber")) {
|
||||
list->has_to_type = 1;
|
||||
list->to_type = OSMO_CC_TYPE_SUBSCRIBER;
|
||||
goto next_to;
|
||||
} else
|
||||
if (!strcasecmp(token, "abbreviated")) {
|
||||
list->has_to_type = 1;
|
||||
list->to_type = OSMO_CC_TYPE_ABBREVIATED;
|
||||
goto next_to;
|
||||
} else
|
||||
if (!strcasecmp(token, "allowed")) {
|
||||
if (no_present)
|
||||
goto no_present_error;
|
||||
list->has_to_present = 1;
|
||||
list->to_present = OSMO_CC_PRESENT_ALLOWED;
|
||||
goto next_to;
|
||||
} else
|
||||
if (!strcasecmp(token, "restricted")) {
|
||||
if (no_present)
|
||||
goto no_present_error;
|
||||
list->has_to_present = 1;
|
||||
list->to_present = OSMO_CC_PRESENT_RESTRICTED;
|
||||
goto next_to;
|
||||
} else {
|
||||
at_used = star_used = 0;
|
||||
for (i = j = 0; token[i] && j < (int)sizeof(list->to) - 1; i++, j++) {
|
||||
if (token[i] == '*') {
|
||||
if (star_used) {
|
||||
free(list);
|
||||
LOGP(DLCC, LOGL_ERROR, "Error in screening definition '%s'.\n", text);
|
||||
LOGP(DLCC, LOGL_ERROR, "The '*' may be used only once.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
list->to[j] = SCREEN_STAR;
|
||||
star_used = 1;
|
||||
} else
|
||||
if (token[i] == '@') {
|
||||
if (!calling_in) {
|
||||
free(list);
|
||||
LOGP(DLCC, LOGL_ERROR, "Error in screening definition '%s'.\n", text);
|
||||
LOGP(DLCC, LOGL_ERROR,
|
||||
"The '@' may be used only for incoming calls from interface.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (at_used) {
|
||||
free(list);
|
||||
LOGP(DLCC, LOGL_ERROR, "Error in screening definition '%s'.\n", text);
|
||||
LOGP(DLCC, LOGL_ERROR, "The '@' may be used only once.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
list->to[j] = SCREEN_AT;
|
||||
at_used = 1;
|
||||
} else
|
||||
if (token[i] == '\\' && token[i + 1] != '\0')
|
||||
list->to[j] = token[++i];
|
||||
else
|
||||
list->to[j] = token[i];
|
||||
}
|
||||
list->to[j] = '\0';
|
||||
}
|
||||
|
||||
token = osmo_cc_strtok_quotes(&text);
|
||||
if (token) {
|
||||
free(list);
|
||||
LOGP(DLCC, LOGL_ERROR, "Error in screening definition '%s'.\n", text);
|
||||
LOGP(DLCC, LOGL_ERROR, "Got garbage behind screening result.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* attach screen entry to list */
|
||||
while (*list_p)
|
||||
list_p = &((*list_p)->next);
|
||||
*list_p = list;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void osmo_cc_flush_screen(osmo_cc_screen_list_t *list)
|
||||
{
|
||||
osmo_cc_screen_list_t *temp;
|
||||
|
||||
while (list) {
|
||||
temp = list;
|
||||
list = list->next;
|
||||
free(temp);
|
||||
}
|
||||
}
|
||||
|
||||
const char *print_rule_string(const char *input)
|
||||
{
|
||||
static char output[256];
|
||||
int i;
|
||||
|
||||
for (i = 0; *input && i < (int)sizeof(output) - 1; i++, input++) {
|
||||
switch (*input) {
|
||||
case SCREEN_QUESTIONMARK:
|
||||
output[i] = '?';
|
||||
break;
|
||||
case SCREEN_STAR:
|
||||
output[i] = '*';
|
||||
break;
|
||||
case SCREEN_AT:
|
||||
output[i] = '@';
|
||||
break;
|
||||
default:
|
||||
output[i] = *input;
|
||||
}
|
||||
}
|
||||
|
||||
output[i] = '\0';
|
||||
return output;
|
||||
}
|
||||
|
||||
static int osmo_cc_screen(const char *what, osmo_cc_screen_list_t *list, uint8_t *type, uint8_t *present, char *id_to,
|
||||
int id_to_size, const char *id_from, const char **routing_p)
|
||||
{
|
||||
const char *suffix;
|
||||
int i, j, rule;
|
||||
|
||||
LOGP(DLCC, LOGL_INFO, "Screening %s '%s':\n", what, id_from);
|
||||
switch (*type) {
|
||||
case OSMO_CC_TYPE_UNKNOWN:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = unknown\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_INTERNATIONAL:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = international\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_NATIONAL:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = national\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_NETWORK:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = network\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_SUBSCRIBER:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = subscriber\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_ABBREVIATED:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = abbreviated\n");
|
||||
break;
|
||||
}
|
||||
if (present) switch (*present) {
|
||||
case OSMO_CC_PRESENT_ALLOWED:
|
||||
LOGP(DLCC, LOGL_INFO, " -> present = allowed\n");
|
||||
break;
|
||||
case OSMO_CC_PRESENT_RESTRICTED:
|
||||
LOGP(DLCC, LOGL_INFO, " -> present = restricted\n");
|
||||
break;
|
||||
}
|
||||
|
||||
rule = 0;
|
||||
while (list) {
|
||||
rule++;
|
||||
LOGP(DLCC, LOGL_INFO, "Comparing with rule #%d: '%s':\n", rule, print_rule_string(list->from));
|
||||
if (list->has_from_type) switch (list->from_type) {
|
||||
case OSMO_CC_TYPE_UNKNOWN:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = unknown\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_INTERNATIONAL:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = international\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_NATIONAL:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = national\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_NETWORK:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = network\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_SUBSCRIBER:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = subscriber\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_ABBREVIATED:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = abbreviated\n");
|
||||
break;
|
||||
}
|
||||
if (list->has_from_present) switch (list->from_present) {
|
||||
case OSMO_CC_PRESENT_ALLOWED:
|
||||
LOGP(DLCC, LOGL_INFO, " -> present = allowed\n");
|
||||
break;
|
||||
case OSMO_CC_PRESENT_RESTRICTED:
|
||||
LOGP(DLCC, LOGL_INFO, " -> present = restricted\n");
|
||||
break;
|
||||
}
|
||||
suffix = NULL;
|
||||
/* attributes do not match */
|
||||
if (list->has_from_type && list->from_type != *type) {
|
||||
LOGP(DLCC, LOGL_INFO, "Rule does not match, because 'type' is different.\n");
|
||||
continue;
|
||||
}
|
||||
if (present && list->has_from_present && list->from_present != *present) {
|
||||
LOGP(DLCC, LOGL_INFO, "Rule does not match, because 'present' is different.\n");
|
||||
continue;
|
||||
}
|
||||
for (i = 0; list->from[i] && id_from[i]; i++) {
|
||||
/* '?' means: any digit, so it machtes */
|
||||
if (list->from[i] == SCREEN_QUESTIONMARK) {
|
||||
continue;
|
||||
}
|
||||
/* '*' means: anything may follow, so it machtes */
|
||||
if (list->from[i] == SCREEN_STAR) {
|
||||
suffix = id_from + i;
|
||||
break;
|
||||
}
|
||||
/* check if digit doesn't matches */
|
||||
if (list->from[i] != id_from[i])
|
||||
break;
|
||||
}
|
||||
/* if last checked digit is '*', we have a match */
|
||||
/* also if we hit EOL at id_from and next check digit is '*' */
|
||||
if (list->from[i] == SCREEN_STAR)
|
||||
break;
|
||||
/* if all digits have matched */
|
||||
if (list->from[i] == '\0' && id_from[i] == '\0')
|
||||
break;
|
||||
LOGP(DLCC, LOGL_INFO, "Rule does not match, because %s is different.\n", what);
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
/* if no list entry matches */
|
||||
if (!list)
|
||||
return -1;
|
||||
|
||||
/* replace ID */
|
||||
if (list->has_to_type) {
|
||||
*type = list->to_type;
|
||||
}
|
||||
if (present && list->has_to_present) {
|
||||
*present = list->to_present;
|
||||
}
|
||||
for (i = j = 0; list->to[i]; i++) {
|
||||
if (j == id_to_size - 1)
|
||||
break;
|
||||
/* '*' means to use suffix of input string */
|
||||
if (list->to[i] == SCREEN_STAR && suffix) {
|
||||
while (*suffix) {
|
||||
id_to[j++] = *suffix++;
|
||||
if (j == id_to_size - 1)
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
/* '@' means to stop and return routing also */
|
||||
} else if (list->to[i] == SCREEN_AT) {
|
||||
if (routing_p)
|
||||
*routing_p = &list->to[i + 1];
|
||||
break;
|
||||
}
|
||||
/* copy output digit */
|
||||
id_to[j++] = list->to[i];
|
||||
}
|
||||
id_to[j] = '\0';
|
||||
|
||||
LOGP(DLCC, LOGL_INFO, "Rule matches, changing %s to '%s'.\n", what, print_rule_string(id_to));
|
||||
if (list->has_to_type) switch (list->to_type) {
|
||||
case OSMO_CC_TYPE_UNKNOWN:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = unknown\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_INTERNATIONAL:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = international\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_NATIONAL:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = national\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_NETWORK:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = network\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_SUBSCRIBER:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = subscriber\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_ABBREVIATED:
|
||||
LOGP(DLCC, LOGL_INFO, " -> type = abbreviated\n");
|
||||
break;
|
||||
}
|
||||
if (list->has_to_present) switch (list->to_present) {
|
||||
case OSMO_CC_PRESENT_ALLOWED:
|
||||
LOGP(DLCC, LOGL_INFO, " -> present = allowed\n");
|
||||
break;
|
||||
case OSMO_CC_PRESENT_RESTRICTED:
|
||||
LOGP(DLCC, LOGL_INFO, " -> present = restricted\n");
|
||||
break;
|
||||
}
|
||||
if (routing_p && *routing_p)
|
||||
LOGP(DLCC, LOGL_INFO, " -> remote = %s\n", *routing_p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
osmo_cc_msg_t *osmo_cc_screen_msg(osmo_cc_endpoint_t *ep, osmo_cc_msg_t *old_msg, int in, const char **routing_p)
|
||||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
char id[256], calling[256], called[256], redir[256];
|
||||
uint8_t calling_type, calling_plan, calling_present, calling_screen;
|
||||
uint8_t called_type, called_plan;
|
||||
uint8_t redir_type, redir_plan, redir_present, redir_screen, redir_reason;
|
||||
int calling_status = 0, called_status = 0, redir_status = 0;
|
||||
int rc;
|
||||
void *ie, *to_ie;
|
||||
uint8_t ie_type;
|
||||
uint16_t ie_length;
|
||||
void *ie_value;
|
||||
|
||||
if (in && ep->screen_calling_in) {
|
||||
rc = osmo_cc_get_ie_calling(old_msg, 0, &calling_type, &calling_plan, &calling_present, &calling_screen,
|
||||
id, sizeof(id));
|
||||
if (rc >= 0) {
|
||||
rc = osmo_cc_screen("incoming caller ID", ep->screen_calling_in, &calling_type,
|
||||
&calling_present, calling, sizeof(calling), id, routing_p);
|
||||
if (rc >= 0)
|
||||
calling_status = 1;
|
||||
} else {
|
||||
calling_type = OSMO_CC_TYPE_UNKNOWN;
|
||||
calling_plan = OSMO_CC_PLAN_TELEPHONY;
|
||||
calling_present = OSMO_CC_PRESENT_ALLOWED;
|
||||
calling_screen = OSMO_CC_SCREEN_NETWORK;
|
||||
rc = osmo_cc_screen("incoming caller ID", ep->screen_calling_in, &calling_type,
|
||||
&calling_present, calling, sizeof(calling), "", routing_p);
|
||||
if (rc >= 0)
|
||||
calling_status = 1;
|
||||
}
|
||||
rc = osmo_cc_get_ie_redir(old_msg, 0, &redir_type, &redir_plan, &redir_present, &redir_screen,
|
||||
&redir_reason, id, sizeof(id));
|
||||
if (rc >= 0) {
|
||||
rc = osmo_cc_screen("incoming redirecting number", ep->screen_calling_in, &redir_type,
|
||||
&redir_present, redir, sizeof(redir), id, NULL);
|
||||
if (rc >= 0)
|
||||
redir_status = 1;
|
||||
}
|
||||
}
|
||||
if (in && ep->screen_called_in) {
|
||||
rc = osmo_cc_get_ie_called(old_msg, 0, &called_type, &called_plan, id, sizeof(id));
|
||||
if (rc >= 0) {
|
||||
rc = osmo_cc_screen("incoming dialed number", ep->screen_called_in, &called_type, NULL, called,
|
||||
sizeof(called), id, routing_p);
|
||||
if (rc >= 0)
|
||||
called_status = 1;
|
||||
} else {
|
||||
called_type = OSMO_CC_TYPE_UNKNOWN;
|
||||
called_plan = OSMO_CC_PLAN_TELEPHONY;
|
||||
rc = osmo_cc_screen("incoming dialed number", ep->screen_called_in, &called_type, NULL, called,
|
||||
sizeof(called), "", routing_p);
|
||||
if (rc >= 0)
|
||||
called_status = 1;
|
||||
}
|
||||
}
|
||||
if (!in && ep->screen_calling_out) {
|
||||
rc = osmo_cc_get_ie_calling(old_msg, 0, &calling_type, &calling_plan, &calling_present, &calling_screen,
|
||||
id, sizeof(id));
|
||||
if (rc >= 0) {
|
||||
rc = osmo_cc_screen("outgoing caller ID", ep->screen_calling_out, &calling_type,
|
||||
&calling_present, calling, sizeof(calling), id, NULL);
|
||||
if (rc >= 0)
|
||||
calling_status = 1;
|
||||
} else {
|
||||
calling_type = OSMO_CC_TYPE_UNKNOWN;
|
||||
calling_plan = OSMO_CC_PLAN_TELEPHONY;
|
||||
calling_present = OSMO_CC_PRESENT_ALLOWED;
|
||||
calling_screen = OSMO_CC_SCREEN_NETWORK;
|
||||
rc = osmo_cc_screen("outgoing caller ID", ep->screen_calling_out, &calling_type,
|
||||
&calling_present, calling, sizeof(calling), "", NULL);
|
||||
if (rc >= 0)
|
||||
calling_status = 1;
|
||||
}
|
||||
rc = osmo_cc_get_ie_redir(old_msg, 0, &redir_type, &redir_plan, &redir_present, &redir_screen,
|
||||
&redir_reason, id, sizeof(id));
|
||||
if (rc >= 0) {
|
||||
rc = osmo_cc_screen("outgoing redirecting number", ep->screen_calling_out, &redir_type,
|
||||
&redir_present, redir, sizeof(redir), id, NULL);
|
||||
if (rc >= 0)
|
||||
redir_status = 1;
|
||||
}
|
||||
}
|
||||
if (!in && ep->screen_called_out) {
|
||||
rc = osmo_cc_get_ie_called(old_msg, 0, &called_type, &called_plan, id, sizeof(id));
|
||||
if (rc >= 0) {
|
||||
rc = osmo_cc_screen("outgoing dialed number", ep->screen_called_out, &called_type, NULL,
|
||||
called, sizeof(called), id, NULL);
|
||||
if (rc >= 0)
|
||||
called_status = 1;
|
||||
} else {
|
||||
called_type = OSMO_CC_TYPE_UNKNOWN;
|
||||
called_plan = OSMO_CC_PLAN_TELEPHONY;
|
||||
rc = osmo_cc_screen("outgoing dialed number", ep->screen_called_out, &called_type, NULL,
|
||||
called, sizeof(called), "", NULL);
|
||||
if (rc >= 0)
|
||||
called_status = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* nothing screened */
|
||||
if (!calling_status && !called_status && !redir_status)
|
||||
return old_msg;
|
||||
|
||||
new_msg = osmo_cc_new_msg(old_msg->type);
|
||||
|
||||
/* copy and replace */
|
||||
ie = old_msg->data;
|
||||
while ((ie_value = osmo_cc_msg_sep_ie(old_msg, &ie, &ie_type, &ie_length))) {
|
||||
switch (ie_type) {
|
||||
case OSMO_CC_IE_CALLING:
|
||||
if (calling_status) {
|
||||
osmo_cc_add_ie_calling(new_msg, calling_type, calling_plan, calling_present,
|
||||
calling_screen, calling);
|
||||
calling_status = 0;
|
||||
break;
|
||||
}
|
||||
goto copy;
|
||||
case OSMO_CC_IE_CALLED:
|
||||
if (called_status) {
|
||||
osmo_cc_add_ie_called(new_msg, called_type, called_plan, called);
|
||||
called_status = 0;
|
||||
break;
|
||||
}
|
||||
goto copy;
|
||||
case OSMO_CC_IE_REDIR:
|
||||
if (redir_status) {
|
||||
osmo_cc_add_ie_redir(new_msg, redir_type, redir_plan, redir_present, redir_screen,
|
||||
redir_reason, redir);
|
||||
redir_status = 0;
|
||||
break;
|
||||
}
|
||||
goto copy;
|
||||
default:
|
||||
copy:
|
||||
to_ie = osmo_cc_add_ie(new_msg, ie_type, ie_length);
|
||||
memcpy(to_ie, ie_value, ie_length);
|
||||
}
|
||||
}
|
||||
|
||||
/* applend, if not yet in message (except redir, since it must exist) */
|
||||
if (calling_status)
|
||||
osmo_cc_add_ie_calling(new_msg, calling_type, calling_plan, calling_present, calling_screen, calling);
|
||||
if (called_status)
|
||||
osmo_cc_add_ie_called(new_msg, called_type, called_plan, called);
|
||||
|
||||
free(old_msg);
|
||||
return new_msg;
|
||||
}
|
|
@ -0,0 +1,558 @@
|
|||
/* Session Description Protocol parsing and generator
|
||||
* This shall be simple and is incomplete.
|
||||
*
|
||||
* (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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/cc/endpoint.h>
|
||||
#include <osmocom/cc/sdp.h>
|
||||
|
||||
extern int DLCC;
|
||||
|
||||
#define strncat_printf(sdp, fmt, arg...) \
|
||||
{ \
|
||||
snprintf(sdp + strlen(sdp), sizeof(sdp) - strlen(sdp), fmt, ## arg); \
|
||||
sdp[sizeof(sdp) - 1] = '\0'; \
|
||||
}
|
||||
|
||||
/* generate SDP from session structure */
|
||||
char *osmo_cc_session_gensdp(osmo_cc_session_t *session, int accepted_only)
|
||||
{
|
||||
/* calc max size of SDP: quick an dirty (close to max UDP payload size) */
|
||||
static char sdp[65000];
|
||||
const char *username, *sess_id, *sess_version, *nettype, *addrtype, *unicast_address;
|
||||
const char *session_name;
|
||||
int individual_connection_data = 1; /* in case there is no media, there is no connection data */
|
||||
int individual_send_receive = 1; /* in case there is no media, there is no send/receive attribute */
|
||||
struct osmo_cc_session_media *media;
|
||||
struct osmo_cc_session_codec *codec;
|
||||
|
||||
sdp[0] = 0;
|
||||
|
||||
/* Version */
|
||||
strncat_printf(sdp, "v=0\r\n");
|
||||
|
||||
/* Origin */
|
||||
username = session->origin_local.username;
|
||||
sess_id = session->origin_local.sess_id;
|
||||
sess_version = session->origin_local.sess_version;
|
||||
nettype = session->origin_local.nettype;
|
||||
addrtype = session->origin_local.addrtype;
|
||||
unicast_address = session->origin_local.unicast_address;
|
||||
strncat_printf(sdp, "o=%s %s %s %s %s %s\r\n", username, sess_id, sess_version, nettype, addrtype, unicast_address);
|
||||
|
||||
/* Session */
|
||||
session_name = session->name;
|
||||
strncat_printf(sdp, "s=%s\r\n", session_name);
|
||||
|
||||
/* Connection Data (if all media have the same data) */
|
||||
if (session->media_list) {
|
||||
osmo_cc_session_for_each_media(session->media_list->next, media) {
|
||||
if (accepted_only && !media->accepted)
|
||||
continue;
|
||||
if (session->media_list->connection_data_local.nettype != media->connection_data_local.nettype)
|
||||
break;
|
||||
if (session->media_list->connection_data_local.addrtype != media->connection_data_local.addrtype)
|
||||
break;
|
||||
if (!!strcmp(session->media_list->connection_data_local.address, media->connection_data_local.address))
|
||||
break;
|
||||
}
|
||||
if (!media)
|
||||
individual_connection_data = 0;
|
||||
}
|
||||
if (!individual_connection_data)
|
||||
strncat_printf(sdp, "c=%s %s %s\r\n", osmo_cc_session_nettype2string(session->media_list->connection_data_local.nettype), osmo_cc_session_addrtype2string(session->media_list->connection_data_local.addrtype), session->media_list->connection_data_local.address);
|
||||
|
||||
/* timestamp */
|
||||
strncat_printf(sdp, "t=0 0\r\n");
|
||||
|
||||
/* sendonly /recvonly (if all media have the same data) */
|
||||
if (session->media_list) {
|
||||
osmo_cc_session_for_each_media(session->media_list->next, media) {
|
||||
if (accepted_only && !media->accepted)
|
||||
continue;
|
||||
if (session->media_list->send != media->send)
|
||||
break;
|
||||
if (session->media_list->receive != media->receive)
|
||||
break;
|
||||
}
|
||||
if (!media)
|
||||
individual_send_receive = 0;
|
||||
}
|
||||
if (!individual_send_receive) {
|
||||
if (session->media_list->send && !session->media_list->receive)
|
||||
strncat_printf(sdp, "a=sendonly\r\n");
|
||||
if (!session->media_list->send && session->media_list->receive)
|
||||
strncat_printf(sdp, "a=recvonly\r\n");
|
||||
if (!session->media_list->send && !session->media_list->receive)
|
||||
strncat_printf(sdp, "a=inactive\r\n");
|
||||
}
|
||||
|
||||
/* media */
|
||||
osmo_cc_session_for_each_media(session->media_list, media) {
|
||||
if (accepted_only && !media->accepted)
|
||||
continue;
|
||||
strncat_printf(sdp, "m=%s %u %s",
|
||||
osmo_cc_session_media_type2string(media->description.type) ? : media->description.type_name,
|
||||
media->description.port_local,
|
||||
osmo_cc_session_media_proto2string(media->description.proto) ? : media->description.proto_name);
|
||||
osmo_cc_session_for_each_codec(media->codec_list, codec) {
|
||||
if (accepted_only && !codec->accepted)
|
||||
continue;
|
||||
strncat_printf(sdp, " %u", codec->payload_type_local);
|
||||
}
|
||||
strncat_printf(sdp, "\r\n");
|
||||
/* don't list rtpmap when session was canceled by setting port to 0 */
|
||||
if (media->description.port_local == 0)
|
||||
continue;
|
||||
if (individual_connection_data)
|
||||
strncat_printf(sdp, "c=%s %s %s\r\n", osmo_cc_session_nettype2string(media->connection_data_local.nettype), osmo_cc_session_addrtype2string(media->connection_data_local.addrtype), media->connection_data_local.address);
|
||||
osmo_cc_session_for_each_codec(media->codec_list, codec) {
|
||||
if (accepted_only && !codec->accepted)
|
||||
continue;
|
||||
strncat_printf(sdp, "a=rtpmap:%u %s/%d", codec->payload_type_local, codec->payload_name, codec->payload_rate);
|
||||
if (codec->payload_channels >= 2)
|
||||
strncat_printf(sdp, "/%d", codec->payload_channels);
|
||||
strncat_printf(sdp, "\r\n");
|
||||
}
|
||||
if (individual_send_receive) {
|
||||
if (media->send && !media->receive)
|
||||
strncat_printf(sdp, "a=sendonly\r\n");
|
||||
if (!media->send && media->receive)
|
||||
strncat_printf(sdp, "a=recvonly\r\n");
|
||||
if (!media->send && !media->receive)
|
||||
strncat_printf(sdp, "a=inactive\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* check for overflow and return */
|
||||
if (strlen(sdp) == sizeof(sdp) - 1) {
|
||||
LOGP(DLCC, LOGL_ERROR, "Fatal error: Allocated SDP buffer with %d bytes is too small, please fix!\n", (int)sizeof(sdp));
|
||||
return NULL;
|
||||
}
|
||||
return sdp;
|
||||
}
|
||||
|
||||
/* separate a word from string that is delimited with one or more space characters */
|
||||
static char *wordsep(char **text_p)
|
||||
{
|
||||
char *text = *text_p;
|
||||
static char word[256];
|
||||
int i;
|
||||
|
||||
/* no text */
|
||||
if (text == NULL || *text == '\0')
|
||||
return NULL;
|
||||
/* skip spaces before text */
|
||||
while (*text && *text <= ' ')
|
||||
text++;
|
||||
/* copy content */
|
||||
i = 0;
|
||||
while (*text > ' ' && i < (int)sizeof(word))
|
||||
word[i++] = *text++;
|
||||
word[i] = '\0';
|
||||
/* set next */
|
||||
*text_p = text;
|
||||
return word;
|
||||
}
|
||||
|
||||
/*
|
||||
* codecs and their default values
|
||||
*
|
||||
* if format is -1, payload type is dynamic
|
||||
* if rate is 0, rate may be any rate
|
||||
*/
|
||||
struct codec_defaults {
|
||||
int fmt;
|
||||
char *name;
|
||||
uint32_t rate;
|
||||
int channels;
|
||||
} codec_defaults[] = {
|
||||
{ 0, "PCMU", 8000, 1 },
|
||||
{ 3, "GSM", 8000, 1 },
|
||||
{ 4, "G723", 8000, 1 },
|
||||
{ 5, "DVI4", 8000, 1 },
|
||||
{ 6, "DVI4", 16000, 1 },
|
||||
{ 7, "LPC", 8000, 1 },
|
||||
{ 8, "PCMA", 8000, 1 },
|
||||
{ 9, "G722", 8000, 1 },
|
||||
{ 10, "L16", 44100, 2 },
|
||||
{ 11, "L16", 44100, 1 },
|
||||
{ 12, "QCELP", 8000, 1 },
|
||||
{ 13, "CN", 8000, 1 },
|
||||
{ 14, "MPA", 90000, 1 },
|
||||
{ 15, "G728", 8000, 1 },
|
||||
{ 16, "DVI4", 11025, 1 },
|
||||
{ 17, "DVI4", 22050, 1 },
|
||||
{ 18, "G729", 8000, 1 },
|
||||
{ 25, "CELB", 90000, 0 },
|
||||
{ 26, "JPEG", 90000, 0 },
|
||||
{ 28, "nv", 90000, 0 },
|
||||
{ 31, "H261", 90000, 0 },
|
||||
{ 32, "MPV", 90000, 0 },
|
||||
{ 33, "MP2T", 90000, 0 },
|
||||
{ 34, "H263", 90000, 0 },
|
||||
{ -1, NULL, 0, 0 },
|
||||
};
|
||||
|
||||
static void complete_codec_by_fmt(uint8_t fmt, const char **name, uint32_t *rate, int *channels)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; codec_defaults[i].name; i++) {
|
||||
if (codec_defaults[i].fmt == fmt)
|
||||
break;
|
||||
}
|
||||
if (!codec_defaults[i].name)
|
||||
return;
|
||||
|
||||
free((char *)*name);
|
||||
*name = strdup(codec_defaults[i].name);
|
||||
*rate = codec_defaults[i].rate;
|
||||
*channels = codec_defaults[i].channels;
|
||||
}
|
||||
|
||||
int osmo_cc_payload_type_by_attrs(uint8_t *fmt, const char *name, uint32_t *rate, int *channels)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; codec_defaults[i].name; i++) {
|
||||
if (!strcmp(codec_defaults[i].name, name)
|
||||
&& (*rate == 0 || codec_defaults[i].rate == *rate)
|
||||
&& (*channels == 0 || codec_defaults[i].channels == *channels))
|
||||
break;
|
||||
}
|
||||
if (!codec_defaults[i].name)
|
||||
return -EINVAL;
|
||||
|
||||
*fmt = codec_defaults[i].fmt;
|
||||
*rate = codec_defaults[i].rate;
|
||||
*channels = codec_defaults[i].channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* parses data and codec list from SDP
|
||||
*
|
||||
* sdp = given SDP text
|
||||
* return: SDP session description structure */
|
||||
struct osmo_cc_session *osmo_cc_session_parsesdp(osmo_cc_session_config_t *conf, void *priv, const char *_sdp)
|
||||
{
|
||||
char buffer[strlen(_sdp) + 1], *sdp = buffer;
|
||||
char *line, *p, *word, *next_word;
|
||||
int line_no = 0;
|
||||
struct osmo_cc_session_connection_data ccd, *cd;
|
||||
int csend = 1, creceive = 1; /* common default */
|
||||
struct osmo_cc_session *session = NULL;
|
||||
struct osmo_cc_session_media *media = NULL;
|
||||
struct osmo_cc_session_codec *codec = NULL;
|
||||
|
||||
/* prepare data */
|
||||
strcpy(sdp, _sdp);
|
||||
memset(&ccd, 0, sizeof(ccd));
|
||||
|
||||
/* create SDP session description */
|
||||
session = osmo_cc_new_session(conf, priv, NULL, NULL, NULL, 0, 0, NULL, NULL, 0);
|
||||
|
||||
/* check every line of SDP and parse its data */
|
||||
while(*sdp) {
|
||||
if ((p = strchr(sdp, '\r'))) {
|
||||
*p++ = '\0';
|
||||
if (*p == '\n')
|
||||
p++;
|
||||
line = sdp;
|
||||
sdp = p;
|
||||
} else if ((p = strchr(sdp, '\n'))) {
|
||||
*p++ = '\0';
|
||||
line = sdp;
|
||||
sdp = p;
|
||||
} else {
|
||||
line = sdp;
|
||||
sdp = strchr(sdp, '\0');
|
||||
}
|
||||
next_word = line + 2;
|
||||
line_no++;
|
||||
|
||||
if (line[0] == '\0')
|
||||
continue;
|
||||
|
||||
if (line[1] != '=') {
|
||||
LOGP(DLCC, LOGL_NOTICE, "SDP line %d = '%s' is garbage, expecting '=' as second character.\n", line_no, line);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(line[0]) {
|
||||
case 'v':
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> Version: %s\n", next_word);
|
||||
if (atoi(next_word) != 0) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "SDP line %d = '%s' describes unsupported version.\n", line_no, line);
|
||||
osmo_cc_free_session(session);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> Originator: %s\n", next_word);
|
||||
/* Originator */
|
||||
word = wordsep(&next_word);
|
||||
if (!word)
|
||||
break;
|
||||
free((char *)session->origin_remote.username); // if already set
|
||||
session->origin_remote.username = strdup(word);
|
||||
word = wordsep(&next_word);
|
||||
if (!word)
|
||||
break;
|
||||
free((char *)session->origin_remote.sess_id); // if already set
|
||||
session->origin_remote.sess_id = strdup(word);
|
||||
word = wordsep(&next_word);
|
||||
if (!word)
|
||||
break;
|
||||
free((char *)session->origin_remote.sess_version); // if already set
|
||||
session->origin_remote.sess_version = strdup(word);
|
||||
word = wordsep(&next_word);
|
||||
if (!word)
|
||||
break;
|
||||
free((char *)session->origin_remote.nettype); // if already set
|
||||
session->origin_remote.nettype = strdup(word);
|
||||
word = wordsep(&next_word);
|
||||
if (!word)
|
||||
break;
|
||||
free((char *)session->origin_remote.addrtype); // if already set
|
||||
session->origin_remote.addrtype = strdup(word);
|
||||
word = wordsep(&next_word);
|
||||
if (!word)
|
||||
break;
|
||||
free((char *)session->origin_remote.unicast_address); // if already set
|
||||
session->origin_remote.unicast_address = strdup(word);
|
||||
break;
|
||||
case 's':
|
||||
/* Session Name */
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> Session Name: %s\n", next_word);
|
||||
free((char *)session->name); // if already set
|
||||
session->name = strdup(next_word);
|
||||
break;
|
||||
case 'c': /* Connection Data */
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> Connection Data: %s\n", next_word);
|
||||
if (media)
|
||||
cd = &media->connection_data_remote;
|
||||
else
|
||||
cd = &ccd;
|
||||
/* network type */
|
||||
if (!(word = wordsep(&next_word)))
|
||||
break;
|
||||
if (!strcmp(word, "IN"))
|
||||
cd->nettype = osmo_cc_session_nettype_inet;
|
||||
else {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Unsupported network type '%s' in SDP line %d = '%s'\n", word, line_no, line);
|
||||
break;
|
||||
}
|
||||
/* address type */
|
||||
if (!(word = wordsep(&next_word)))
|
||||
break;
|
||||
if (!strcmp(word, "IP4")) {
|
||||
cd->addrtype = osmo_cc_session_addrtype_ipv4;
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> Address Type = IPv4\n");
|
||||
} else
|
||||
if (!strcmp(word, "IP6")) {
|
||||
cd->addrtype = osmo_cc_session_addrtype_ipv6;
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> Address Type = IPv6\n");
|
||||
} else {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Unsupported address type '%s' in SDP line %d = '%s'\n", word, line_no, line);
|
||||
break;
|
||||
}
|
||||
/* connection address */
|
||||
if (!(word = wordsep(&next_word)))
|
||||
break;
|
||||
if ((p = strchr(word, '/')))
|
||||
*p++ = '\0';
|
||||
free((char *)cd->address); // in case of multiple lines of 'c'
|
||||
cd->address = strdup(word);
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> Address = %s\n", word);
|
||||
break;
|
||||
case 'm': /* Media Description */
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> Media Description: %s\n", next_word);
|
||||
/* add media description */
|
||||
media = osmo_cc_add_media(session, 0, 0, NULL, 0, 0, 0, csend, creceive, NULL, 0);
|
||||
/* copy common connection data from common connection, if exists */
|
||||
cd = &media->connection_data_remote;
|
||||
memcpy(cd, &ccd, sizeof(*cd));
|
||||
/* media type */
|
||||
if (!(word = wordsep(&next_word)))
|
||||
break;
|
||||
if (!strcmp(word, "audio"))
|
||||
media->description.type = osmo_cc_session_media_type_audio;
|
||||
else
|
||||
if (!strcmp(word, "video"))
|
||||
media->description.type = osmo_cc_session_media_type_video;
|
||||
else {
|
||||
media->description.type = osmo_cc_session_media_type_unknown;
|
||||
media->description.type_name = strdup(word);
|
||||
LOGP(DLCC, LOGL_DEBUG, "Unsupported media type in SDP line %d = '%s'\n", line_no, line);
|
||||
}
|
||||
/* port */
|
||||
if (!(word = wordsep(&next_word)))
|
||||
break;
|
||||
media->description.port_remote = atoi(word);
|
||||
/* proto */
|
||||
if (!(word = wordsep(&next_word)))
|
||||
break;
|
||||
if (!strcmp(word, "RTP/AVP"))
|
||||
media->description.proto = osmo_cc_session_media_proto_rtp;
|
||||
else {
|
||||
media->description.proto = osmo_cc_session_media_proto_unknown;
|
||||
media->description.proto_name = strdup(word);
|
||||
LOGP(DLCC, LOGL_NOTICE, "Unsupported protocol type in SDP line %d = '%s'\n", line_no, line);
|
||||
break;
|
||||
}
|
||||
/* create codec description for each codec and link */
|
||||
while ((word = wordsep(&next_word))) {
|
||||
/* create codec */
|
||||
codec = osmo_cc_add_codec(media, NULL, 0, 1, NULL, NULL, 0);
|
||||
/* fmt */
|
||||
codec->payload_type_remote = atoi(word);
|
||||
complete_codec_by_fmt(codec->payload_type_remote, &codec->payload_name, &codec->payload_rate, &codec->payload_channels);
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> payload type = %d\n", codec->payload_type_remote);
|
||||
if (codec->payload_name)
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> payload name = %s\n", codec->payload_name);
|
||||
if (codec->payload_rate)
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> payload rate = %d\n", codec->payload_rate);
|
||||
if (codec->payload_channels)
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> payload channels = %d\n", codec->payload_channels);
|
||||
}
|
||||
break;
|
||||
case 'a':
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> Attribute: %s\n", next_word);
|
||||
word = wordsep(&next_word);
|
||||
if (!strcmp(word, "sendrecv")) {
|
||||
if (media) {
|
||||
media->receive = 1;
|
||||
media->send = 1;
|
||||
} else {
|
||||
creceive = 1;
|
||||
csend = 1;
|
||||
}
|
||||
break;
|
||||
} else
|
||||
if (!strcmp(word, "recvonly")) {
|
||||
if (media) {
|
||||
media->receive = 1;
|
||||
media->send = 0;
|
||||
} else {
|
||||
creceive = 1;
|
||||
csend = 0;
|
||||
}
|
||||
break;
|
||||
} else
|
||||
if (!strcmp(word, "sendonly")) {
|
||||
if (media) {
|
||||
media->receive = 0;
|
||||
media->send = 1;
|
||||
} else {
|
||||
creceive = 0;
|
||||
csend = 1;
|
||||
}
|
||||
break;
|
||||
} else
|
||||
if (!strcmp(word, "inactive")) {
|
||||
if (media) {
|
||||
media->receive = 0;
|
||||
media->send = 0;
|
||||
} else {
|
||||
creceive = 0;
|
||||
csend = 0;
|
||||
}
|
||||
break;
|
||||
} else
|
||||
if (!media) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Attribute without previously defined media in SDP line %d = '%s'\n", line_no, line);
|
||||
break;
|
||||
}
|
||||
if (!strncmp(word, "rtpmap:", 7)) {
|
||||
int fmt = atoi(word + 7);
|
||||
osmo_cc_session_for_each_codec(media->codec_list, codec) {
|
||||
if (codec->payload_type_remote == fmt)
|
||||
break;
|
||||
}
|
||||
if (!codec) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Attribute without previously defined codec in SDP line %d = '%s'\n", line_no, line);
|
||||
break;
|
||||
}
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> (rtpmap) payload type = %d\n", codec->payload_type_remote);
|
||||
if (!(word = wordsep(&next_word)))
|
||||
goto rtpmap_done;
|
||||
if ((p = strchr(word, '/')))
|
||||
*p++ = '\0';
|
||||
free((char *)codec->payload_name); // in case it is already set above
|
||||
codec->payload_name = strdup(word);
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> (rtpmap) payload name = %s\n", codec->payload_name);
|
||||
if (!(word = p))
|
||||
goto rtpmap_done;
|
||||
if ((p = strchr(word, '/')))
|
||||
*p++ = '\0';
|
||||
codec->payload_rate = atoi(word);
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> (rtpmap) payload rate = %d\n", codec->payload_rate);
|
||||
if (!(word = p)) {
|
||||
/* if no channel is given and no default was specified, we must set 1 channel */
|
||||
if (!codec->payload_channels)
|
||||
codec->payload_channels = 1;
|
||||
goto rtpmap_done;
|
||||
}
|
||||
codec->payload_channels = atoi(word);
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> (rtpmap) payload channels = %d\n", codec->payload_channels);
|
||||
rtpmap_done:
|
||||
if (!codec->payload_name || !codec->payload_rate || !codec->payload_channels) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Broken 'rtpmap' definition in SDP line %d = '%s' Skipping codec!\n", line_no, line);
|
||||
osmo_cc_free_codec(codec);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if something is incomplete, abort here */
|
||||
if (osmo_cc_session_check(session, 1)) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Parsing SDP failed.\n");
|
||||
osmo_cc_free_session(session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
void osmo_cc_debug_sdp(const char *_sdp)
|
||||
{
|
||||
const unsigned char *sdp = (const unsigned char *)_sdp;
|
||||
char text[256];
|
||||
int i;
|
||||
|
||||
while (*sdp) {
|
||||
for (i = 0; *sdp > 0 && *sdp >= 32 && i < (int)sizeof(text) - 1; i++)
|
||||
text[i] = *sdp++;
|
||||
text[i] = '\0';
|
||||
LOGP(DLCC, LOGL_DEBUG, " | %s\n", text);
|
||||
while (*sdp > 0 && *sdp < 32)
|
||||
sdp++;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,652 @@
|
|||
/* Osmo-CC: Media Session 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
#include <inttypes.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/cc/endpoint.h>
|
||||
#include <osmocom/cc/sdp.h>
|
||||
#include <osmocom/cc/rtp.h>
|
||||
|
||||
extern int DLCC;
|
||||
|
||||
#define NTP_OFFSET 2208988800U
|
||||
|
||||
void osmo_cc_set_local_peer(osmo_cc_session_config_t *conf, enum osmo_cc_session_nettype nettype,
|
||||
enum osmo_cc_session_addrtype addrtype, const char *address)
|
||||
{
|
||||
conf->default_nettype = nettype;
|
||||
conf->default_addrtype = addrtype;
|
||||
free((void *)conf->default_unicast_address);
|
||||
conf->default_unicast_address = strdup(address);
|
||||
}
|
||||
|
||||
osmo_cc_session_t *osmo_cc_new_session(osmo_cc_session_config_t *conf, void *priv, const char *username,
|
||||
const char *sess_id, const char *sess_version, enum osmo_cc_session_nettype nettype,
|
||||
enum osmo_cc_session_addrtype addrtype, const char *unicast_address, const char *session_name, int debug)
|
||||
{
|
||||
osmo_cc_session_t *session;
|
||||
|
||||
if (debug)
|
||||
LOGP(DLCC, LOGL_DEBUG, "Creating session structure.\n");
|
||||
|
||||
session = calloc(1, sizeof(*session));
|
||||
if (!session) {
|
||||
LOGP(DLCC, LOGL_ERROR, "No mem!\n");
|
||||
abort();
|
||||
}
|
||||
session->config = conf;
|
||||
session->priv = priv;
|
||||
if (username) {
|
||||
int i;
|
||||
for (i = 0; username[i]; i++) {
|
||||
if ((uint8_t)username[i] < 33) {
|
||||
LOGP(DLCC, LOGL_ERROR,
|
||||
"Fatal error: SDP's originator (username) uses invalid characters, please fix!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
session->origin_local.username = strdup(username);
|
||||
}
|
||||
if (!username)
|
||||
session->origin_local.username = strdup("-");
|
||||
if (debug)
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> user name = %s\n", session->origin_local.username);
|
||||
if (sess_id)
|
||||
session->origin_local.sess_id = strdup(sess_id);
|
||||
if (sess_version)
|
||||
session->origin_local.sess_version = strdup(sess_version);
|
||||
if (!sess_id || !sess_version) {
|
||||
struct timeval tv;
|
||||
char ntp_timestamp[32];
|
||||
/* get time NTP format time stamp (time since 1900) */
|
||||
gettimeofday(&tv, NULL);
|
||||
sprintf(ntp_timestamp, "%" PRIu64, (uint64_t)tv.tv_sec + NTP_OFFSET);
|
||||
if (!sess_id)
|
||||
session->origin_local.sess_id = strdup(ntp_timestamp);
|
||||
if (!sess_version)
|
||||
session->origin_local.sess_version = strdup(ntp_timestamp);
|
||||
}
|
||||
if (debug) {
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> session ID = %s\n", session->origin_local.sess_id);
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> session version = %s\n", session->origin_local.sess_version);
|
||||
}
|
||||
if (nettype)
|
||||
session->origin_local.nettype = strdup(osmo_cc_session_nettype2string(nettype));
|
||||
else
|
||||
session->origin_local.nettype = strdup(osmo_cc_session_nettype2string(conf->default_nettype));
|
||||
if (debug)
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> network type = %s\n", session->origin_local.nettype);
|
||||
if (addrtype)
|
||||
session->origin_local.addrtype = strdup(osmo_cc_session_addrtype2string(addrtype));
|
||||
else
|
||||
session->origin_local.addrtype = strdup(osmo_cc_session_addrtype2string(conf->default_addrtype));
|
||||
if (debug)
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> address type = %s\n", session->origin_local.addrtype);
|
||||
if (unicast_address)
|
||||
session->origin_local.unicast_address = strdup(unicast_address);
|
||||
else
|
||||
session->origin_local.unicast_address = strdup(conf->default_unicast_address);
|
||||
if (debug)
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> unicast address = %s\n", session->origin_local.unicast_address);
|
||||
if (session_name)
|
||||
session->name = strdup(session_name);
|
||||
if (!session_name)
|
||||
session->name = strdup("-");
|
||||
if (debug)
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> session name = %s\n", session->name);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
void osmo_cc_free_session(osmo_cc_session_t *session)
|
||||
{
|
||||
LOGP(DLCC, LOGL_DEBUG, "Free session structure.\n");
|
||||
|
||||
free((char *)session->origin_local.username);
|
||||
free((char *)session->origin_local.sess_id);
|
||||
free((char *)session->origin_local.sess_version);
|
||||
free((char *)session->origin_local.nettype);
|
||||
free((char *)session->origin_local.addrtype);
|
||||
free((char *)session->origin_local.unicast_address);
|
||||
free((char *)session->origin_remote.username);
|
||||
free((char *)session->origin_remote.sess_id);
|
||||
free((char *)session->origin_remote.sess_version);
|
||||
free((char *)session->origin_remote.nettype);
|
||||
free((char *)session->origin_remote.addrtype);
|
||||
free((char *)session->origin_remote.unicast_address);
|
||||
free((char *)session->name);
|
||||
while (session->media_list)
|
||||
osmo_cc_free_media(session->media_list);
|
||||
free(session);
|
||||
}
|
||||
|
||||
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype,
|
||||
enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type,
|
||||
uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive,
|
||||
void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number,
|
||||
uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len),
|
||||
int debug)
|
||||
{
|
||||
osmo_cc_session_config_t *conf = session->config;
|
||||
osmo_cc_session_media_t *media, **mediap;
|
||||
|
||||
media = calloc(1, sizeof(*media));
|
||||
if (!media) {
|
||||
LOGP(DLCC, LOGL_ERROR, "No mem!\n");
|
||||
abort();
|
||||
}
|
||||
media->session = session;
|
||||
if (nettype)
|
||||
media->connection_data_local.nettype = nettype;
|
||||
else
|
||||
media->connection_data_local.nettype = conf->default_nettype;
|
||||
if (addrtype)
|
||||
media->connection_data_local.addrtype = addrtype;
|
||||
else
|
||||
media->connection_data_local.addrtype = conf->default_addrtype;
|
||||
if (address)
|
||||
media->connection_data_local.address = strdup(address);
|
||||
else
|
||||
media->connection_data_local.address = strdup(conf->default_unicast_address);
|
||||
media->description.type = type;
|
||||
media->description.port_local = port;
|
||||
media->description.proto = proto;
|
||||
media->send = send;
|
||||
media->receive = receive;
|
||||
media->receiver = receiver;
|
||||
media->tx_sequence = random();
|
||||
media->tx_timestamp = random();
|
||||
mediap = &media->session->media_list;
|
||||
while (*mediap)
|
||||
mediap = &((*mediap)->next);
|
||||
*mediap = media;
|
||||
|
||||
if (debug) {
|
||||
LOGP(DLCC, LOGL_DEBUG, "Adding session media.\n");
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> network type = %s\n",
|
||||
osmo_cc_session_nettype2string(media->connection_data_local.nettype));
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> address type = %s\n",
|
||||
osmo_cc_session_addrtype2string(media->connection_data_local.addrtype));
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> address = %s\n", media->connection_data_local.address);
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> media type = %s\n",
|
||||
osmo_cc_session_media_type2string(media->description.type));
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> media port = %d\n", media->description.port_local);
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> media proto = %s\n",
|
||||
osmo_cc_session_media_proto2string(media->description.proto));
|
||||
LOGP(DLCC, LOGL_DEBUG, "Opening and binding media port %d\n", media->description.port_local);
|
||||
}
|
||||
|
||||
return media;
|
||||
}
|
||||
|
||||
void osmo_cc_free_media(osmo_cc_session_media_t *media)
|
||||
{
|
||||
osmo_cc_session_media_t **mediap;
|
||||
|
||||
LOGP(DLCC, LOGL_DEBUG, "Free session media.\n");
|
||||
|
||||
osmo_cc_rtp_close(media);
|
||||
free((char *)media->connection_data_local.nettype_name);
|
||||
free((char *)media->connection_data_local.addrtype_name);
|
||||
free((char *)media->connection_data_local.address);
|
||||
free((char *)media->connection_data_remote.nettype_name);
|
||||
free((char *)media->connection_data_remote.addrtype_name);
|
||||
free((char *)media->connection_data_remote.address);
|
||||
while (media->codec_list)
|
||||
osmo_cc_free_codec(media->codec_list);
|
||||
mediap = &media->session->media_list;
|
||||
while (*mediap != media)
|
||||
mediap = &((*mediap)->next);
|
||||
*mediap = media->next;
|
||||
free(media);
|
||||
}
|
||||
|
||||
osmo_cc_session_codec_t *osmo_cc_add_codec(osmo_cc_session_media_t *media, 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 *priv),
|
||||
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), int debug)
|
||||
{
|
||||
osmo_cc_session_codec_t *codec, **codecp;
|
||||
int rc;
|
||||
|
||||
codec = calloc(1, sizeof(*codec));
|
||||
if (!codec) {
|
||||
LOGP(DLCC, LOGL_ERROR, "No mem!\n");
|
||||
abort();
|
||||
}
|
||||
codec->media = media;
|
||||
if (payload_name) {
|
||||
codec->payload_name = strdup(payload_name);
|
||||
codec->payload_rate = payload_rate;
|
||||
codec->payload_channels = payload_channels;
|
||||
rc = osmo_cc_payload_type_by_attrs(&codec->payload_type_local, payload_name, &payload_rate,
|
||||
&payload_channels);
|
||||
if (rc < 0) {
|
||||
/* hunt for next free dynamic payload type */
|
||||
uint8_t fmt = 96;
|
||||
osmo_cc_session_codec_t *c;
|
||||
osmo_cc_session_for_each_codec(media->codec_list, c) {
|
||||
if (c->payload_type_local >= fmt)
|
||||
fmt = c->payload_type_local + 1;
|
||||
}
|
||||
codec->payload_type_local = fmt;
|
||||
}
|
||||
}
|
||||
codec->encoder = encoder;
|
||||
codec->decoder = decoder;
|
||||
codecp = &codec->media->codec_list;
|
||||
while (*codecp)
|
||||
codecp = &((*codecp)->next);
|
||||
*codecp = codec;
|
||||
|
||||
if (debug) {
|
||||
LOGP(DLCC, LOGL_DEBUG, "Adding session codec.\n");
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> payload type = %d\n", codec->payload_type_local);
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> payload name = %s\n", codec->payload_name);
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> payload rate = %d\n", codec->payload_rate);
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> payload channels = %d\n", codec->payload_channels);
|
||||
}
|
||||
|
||||
return codec;
|
||||
}
|
||||
|
||||
void osmo_cc_free_codec(osmo_cc_session_codec_t *codec)
|
||||
{
|
||||
osmo_cc_session_codec_t **codecp;
|
||||
|
||||
LOGP(DLCC, LOGL_DEBUG, "Free session codec.\n");
|
||||
|
||||
free((char *)codec->payload_name);
|
||||
codecp = &codec->media->codec_list;
|
||||
while (*codecp != codec)
|
||||
codecp = &((*codecp)->next);
|
||||
*codecp = codec->next;
|
||||
free(codec);
|
||||
}
|
||||
|
||||
int osmo_cc_session_check(osmo_cc_session_t *session, int remote)
|
||||
{
|
||||
struct osmo_cc_session_origin *orig;
|
||||
struct osmo_cc_session_media *media;
|
||||
struct osmo_cc_session_connection_data *cd;
|
||||
struct osmo_cc_session_media_description *md;
|
||||
struct osmo_cc_session_codec *codec;
|
||||
int i, j;
|
||||
|
||||
if (remote)
|
||||
orig = &session->origin_remote;
|
||||
else
|
||||
orig = &session->origin_local;
|
||||
if (!orig->username
|
||||
|| !orig->sess_id
|
||||
|| !orig->sess_version
|
||||
|| !orig->nettype
|
||||
|| !orig->addrtype
|
||||
|| !orig->unicast_address) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Missing data in session origin\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!session->name) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Missing data in session origin\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!session->media_list) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Missing media session\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
i = 0;
|
||||
osmo_cc_session_for_each_media(session->media_list, media) {
|
||||
i++;
|
||||
if (remote)
|
||||
cd = &media->connection_data_remote;
|
||||
else
|
||||
cd = &media->connection_data_local;
|
||||
if (!cd->nettype && !cd->nettype_name) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Session with media #%d is missing connection network type\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!cd->addrtype && !cd->addrtype_name) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Session with media #%d is missing connection address type\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!cd->address) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Session with media #%d is missing connection address\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
md = &media->description;
|
||||
if (!md->type && !md->type_name) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Session with media #%d is missing media type\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!md->proto && !md->proto_name) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Session with media #%d is missing protocol\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
j = 0;
|
||||
osmo_cc_session_for_each_codec(media->codec_list, codec) {
|
||||
j++;
|
||||
if (!codec->payload_name) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Session with media #%d, codec #%d is missing name\n", i, j);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!codec->payload_rate) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Session with media #%d, codec #%d is missing rate\n", i, j);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!codec->payload_channels) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Session with media #%d, codec #%d is missing channel count\n",
|
||||
i, j);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check session description and generate SDP */
|
||||
const char *osmo_cc_session_send_offer(osmo_cc_session_t *session)
|
||||
{
|
||||
const char *sdp;
|
||||
int rc;
|
||||
|
||||
LOGP(DLCC, LOGL_DEBUG, "Generating session offer and opening RTP stream.\n");
|
||||
|
||||
rc = osmo_cc_session_check(session, 0);
|
||||
if (rc < 0) {
|
||||
LOGP(DLCC, LOGL_ERROR, "Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
sdp = osmo_cc_session_gensdp(session, 0);
|
||||
osmo_cc_debug_sdp(sdp);
|
||||
|
||||
return sdp;
|
||||
}
|
||||
|
||||
osmo_cc_session_t *osmo_cc_session_receive_offer(osmo_cc_session_config_t *conf, void *priv, const char *sdp)
|
||||
{
|
||||
osmo_cc_session_t *session;
|
||||
int rc;
|
||||
|
||||
LOGP(DLCC, LOGL_DEBUG, "Parsing session offer.\n");
|
||||
|
||||
osmo_cc_debug_sdp(sdp);
|
||||
session = osmo_cc_session_parsesdp(conf, priv, sdp);
|
||||
if (!session)
|
||||
return NULL;
|
||||
|
||||
rc = osmo_cc_session_check(session, 0);
|
||||
if (rc < 0) {
|
||||
osmo_cc_free_session(session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype,
|
||||
enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive,
|
||||
void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number,
|
||||
uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len))
|
||||
{
|
||||
osmo_cc_session_config_t *conf = media->session->config;
|
||||
|
||||
media->accepted = 1;
|
||||
if (nettype)
|
||||
media->connection_data_local.nettype = nettype;
|
||||
else
|
||||
media->connection_data_local.nettype = conf->default_nettype;
|
||||
if (addrtype)
|
||||
media->connection_data_local.addrtype = addrtype;
|
||||
else
|
||||
media->connection_data_local.addrtype = conf->default_addrtype;
|
||||
free((char *)media->connection_data_local.address);
|
||||
if (address)
|
||||
media->connection_data_local.address = strdup(address);
|
||||
else
|
||||
media->connection_data_local.address = strdup(conf->default_unicast_address);
|
||||
media->send = send;
|
||||
media->receive = receive;
|
||||
media->receiver = receiver;
|
||||
|
||||
LOGP(DLCC, LOGL_DEBUG, "Accepting session media.\n");
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> network type = %s\n",
|
||||
osmo_cc_session_nettype2string(media->connection_data_local.nettype));
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> address type = %s\n",
|
||||
osmo_cc_session_addrtype2string(media->connection_data_local.addrtype));
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> address = %s\n", media->connection_data_local.address);
|
||||
}
|
||||
|
||||
|
||||
void osmo_cc_session_accept_codec(osmo_cc_session_codec_t *codec,
|
||||
void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv),
|
||||
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv))
|
||||
{
|
||||
codec->accepted = 1;
|
||||
codec->encoder = encoder;
|
||||
codec->decoder = decoder;
|
||||
/* when we accept a codec, we just use the same payload type as the remote */
|
||||
codec->payload_type_local = codec->payload_type_remote;
|
||||
|
||||
LOGP(DLCC, LOGL_DEBUG, "Accepting session codec.\n");
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> payload type = %d\n", codec->payload_type_local);
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> payload name = %s\n", codec->payload_name);
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> payload rate = %d\n", codec->payload_rate);
|
||||
LOGP(DLCC, LOGL_DEBUG, " -> payload channels = %d\n", codec->payload_channels);
|
||||
}
|
||||
|
||||
const char *osmo_cc_session_send_answer(osmo_cc_session_t *session)
|
||||
{
|
||||
const char *sdp;
|
||||
int rc;
|
||||
|
||||
LOGP(DLCC, LOGL_DEBUG, "Generating session answer.\n");
|
||||
|
||||
rc = osmo_cc_session_check(session, 0);
|
||||
if (rc < 0) {
|
||||
LOGP(DLCC, LOGL_ERROR, "Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
sdp = osmo_cc_session_gensdp(session, 1);
|
||||
osmo_cc_debug_sdp(sdp);
|
||||
|
||||
return sdp;
|
||||
}
|
||||
|
||||
/* Apply remote session description to local session description.
|
||||
* If remote media's port is 0, remove from local session description.
|
||||
* If codecs in the remote session description are missing, remove from local session description.
|
||||
*/
|
||||
static int osmo_cc_session_negotiate(osmo_cc_session_t *session_local, struct osmo_cc_session *session_remote)
|
||||
{
|
||||
osmo_cc_session_media_t *media_local, *media_remote, **media_local_p;
|
||||
osmo_cc_session_codec_t *codec_local, *codec_remote, **codec_local_p;
|
||||
int rc;
|
||||
|
||||
LOGP(DLCC, LOGL_DEBUG, "Negotiating session.\n");
|
||||
|
||||
/* copy remote session information */
|
||||
session_local->origin_remote.username = strdup(session_remote->origin_remote.username);
|
||||
session_local->origin_remote.sess_id = strdup(session_remote->origin_remote.sess_id);
|
||||
session_local->origin_remote.sess_version = strdup(session_remote->origin_remote.sess_version);
|
||||
session_local->origin_remote.nettype = strdup(session_remote->origin_remote.nettype);
|
||||
session_local->origin_remote.addrtype = strdup(session_remote->origin_remote.addrtype);
|
||||
session_local->origin_remote.unicast_address = strdup(session_remote->origin_remote.unicast_address);
|
||||
|
||||
/* loop all media */
|
||||
for (media_local = session_local->media_list, media_remote = session_remote->media_list;
|
||||
media_local && media_remote; media_local = media_local->next, media_remote = media_remote->next) {
|
||||
/* copy remote media information */
|
||||
media_local->connection_data_remote.nettype = media_remote->connection_data_remote.nettype;
|
||||
if (media_remote->connection_data_remote.nettype_name)
|
||||
media_local->connection_data_remote.nettype_name =
|
||||
strdup(media_remote->connection_data_remote.nettype_name);
|
||||
media_local->connection_data_remote.addrtype = media_remote->connection_data_remote.addrtype;
|
||||
if (media_remote->connection_data_remote.addrtype_name)
|
||||
media_local->connection_data_remote.addrtype_name =
|
||||
strdup(media_remote->connection_data_remote.addrtype_name);
|
||||
if (media_remote->connection_data_remote.address)
|
||||
media_local->connection_data_remote.address =
|
||||
strdup(media_remote->connection_data_remote.address);
|
||||
media_local->description.port_remote = media_remote->description.port_remote;
|
||||
media_local->send = media_remote->send;
|
||||
media_local->receive = media_remote->receive;
|
||||
/* loop all codecs and remove if they are not found in local session description */
|
||||
codec_local_p = &media_local->codec_list;
|
||||
codec_local = *codec_local_p;
|
||||
while (codec_local) {
|
||||
/* search for equal codec, payload type may differe for each direction */
|
||||
osmo_cc_session_for_each_codec(media_remote->codec_list, codec_remote) {
|
||||
if (!strcmp(codec_local->payload_name, codec_remote->payload_name)
|
||||
&& codec_local->payload_rate == codec_remote->payload_rate
|
||||
&& codec_local->payload_channels == codec_remote->payload_channels)
|
||||
break;
|
||||
}
|
||||
if (codec_remote) {
|
||||
/* mark as accepted, copy remote codec information */
|
||||
codec_local->accepted = 1;
|
||||
codec_local->payload_type_remote = codec_remote->payload_type_remote;
|
||||
} else {
|
||||
/* mark as not accepted, in case it was accepted in earlier response */
|
||||
codec_local->accepted = 0;
|
||||
}
|
||||
codec_local_p = &codec_local->next;
|
||||
codec_local = *codec_local_p;
|
||||
}
|
||||
}
|
||||
if (media_local) {
|
||||
LOGP(DLCC, LOGL_NOTICE,
|
||||
"Negotiation failed, because remote endpoint returns less media streams than we offered.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (media_remote) {
|
||||
LOGP(DLCC, LOGL_NOTICE,
|
||||
"Negotiation failed, because remote endpoint returns more media streams than we offered.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* mark media as accepted, if there is a remote port and there is at least one codec */
|
||||
media_local_p = &session_local->media_list;
|
||||
media_local = *media_local_p;
|
||||
while (media_local) {
|
||||
if (media_local->description.port_remote && media_local->codec_list) {
|
||||
/* mark as accepted */
|
||||
media_local->accepted = 1;
|
||||
} else {
|
||||
/* mark as not accepted, in case it was accepted in earlier response */
|
||||
media_local->accepted = 0;
|
||||
}
|
||||
media_local_p = &media_local->next;
|
||||
media_local = *media_local_p;
|
||||
}
|
||||
|
||||
rc = osmo_cc_session_check(session_local, 1);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_cc_session_receive_answer(osmo_cc_session_t *session, const char *sdp)
|
||||
{
|
||||
osmo_cc_session_t *session_remote;
|
||||
int rc;
|
||||
|
||||
LOGP(DLCC, LOGL_DEBUG, "Parsing session answer.\n");
|
||||
|
||||
osmo_cc_debug_sdp(sdp);
|
||||
session_remote = osmo_cc_session_parsesdp(session->config, NULL, sdp);
|
||||
if (!session_remote)
|
||||
return -EINVAL;
|
||||
|
||||
rc = osmo_cc_session_check(session_remote, 1);
|
||||
if (rc < 0) {
|
||||
osmo_cc_free_session(session_remote);
|
||||
return rc;
|
||||
}
|
||||
rc = osmo_cc_session_negotiate(session, session_remote);
|
||||
if (rc < 0) {
|
||||
osmo_cc_free_session(session_remote);
|
||||
return rc;
|
||||
}
|
||||
osmo_cc_free_session(session_remote);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *osmo_cc_session_nettype2string(enum osmo_cc_session_nettype nettype)
|
||||
{
|
||||
switch (nettype) {
|
||||
case osmo_cc_session_nettype_inet:
|
||||
return "IN";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const char *osmo_cc_session_addrtype2string(enum osmo_cc_session_addrtype addrtype)
|
||||
{
|
||||
switch (addrtype) {
|
||||
case osmo_cc_session_addrtype_ipv4:
|
||||
return "IP4";
|
||||
case osmo_cc_session_addrtype_ipv6:
|
||||
return "IP6";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const char *osmo_cc_session_media_type2string(enum osmo_cc_session_media_type media_type)
|
||||
{
|
||||
switch (media_type) {
|
||||
case osmo_cc_session_media_type_audio:
|
||||
return "audio";
|
||||
case osmo_cc_session_media_type_video:
|
||||
return "video";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const char *osmo_cc_session_media_proto2string(enum osmo_cc_session_media_proto media_proto)
|
||||
{
|
||||
switch (media_proto) {
|
||||
case osmo_cc_session_media_proto_rtp:
|
||||
return "RTP/AVP";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int osmo_cc_session_if_codec(osmo_cc_session_codec_t *codec, const char *name, uint32_t rate, int channels)
|
||||
{
|
||||
return (!strcmp(codec->payload_name, name)
|
||||
&& codec->payload_rate == rate
|
||||
&& codec->payload_channels == channels);
|
||||
}
|
|
@ -0,0 +1,628 @@
|
|||
/* Osmo-CC: Socket 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 <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/cc/message.h>
|
||||
#include <osmocom/cc/cause.h>
|
||||
#include <osmocom/cc/socket.h>
|
||||
|
||||
extern int DLCC;
|
||||
|
||||
static const char version_string[] = OSMO_CC_VERSION;
|
||||
|
||||
static int _getaddrinfo(const char *host, uint16_t port, struct addrinfo **result)
|
||||
{
|
||||
char portstr[8];
|
||||
struct addrinfo hints;
|
||||
int rc;
|
||||
|
||||
sprintf(portstr, "%d", port);
|
||||
|
||||
/* bind socket */
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
hints.ai_protocol = 0;
|
||||
hints.ai_canonname = NULL;
|
||||
hints.ai_addr = NULL;
|
||||
hints.ai_next = NULL;
|
||||
|
||||
rc = getaddrinfo(host, portstr, &hints, result);
|
||||
if (rc < 0) {
|
||||
LOGP(DLCC, LOGL_ERROR, "Failed to create socket for host '%s', port '%d': %s.\n", host, port,
|
||||
gai_strerror(rc));
|
||||
return rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* send a reject message toward CC process.
|
||||
* the CC process will change the reject message to a release message when not in INIT_IN state
|
||||
*/
|
||||
static void rej_msg(osmo_cc_socket_t *os, uint32_t callref, uint8_t socket_cause, uint8_t isdn_cause,
|
||||
uint16_t sip_cause)
|
||||
{
|
||||
osmo_cc_msg_t *msg;
|
||||
|
||||
/* create message */
|
||||
msg = osmo_cc_new_msg(OSMO_CC_MSG_REJ_REQ);
|
||||
if (!msg)
|
||||
abort();
|
||||
|
||||
/* add cause */
|
||||
osmo_cc_add_ie_cause(msg, os->location, isdn_cause, sip_cause, socket_cause);
|
||||
osmo_cc_convert_cause_msg(msg);
|
||||
|
||||
/* message down */
|
||||
os->recv_msg_cb(os->priv, callref, msg);
|
||||
}
|
||||
|
||||
static void tx_keepalive_timeout(void *data)
|
||||
{
|
||||
osmo_cc_conn_t *conn = data;
|
||||
osmo_cc_msg_t *msg;
|
||||
|
||||
/* send keepalive message */
|
||||
msg = osmo_cc_new_msg(OSMO_CC_MSG_DUMMY_REQ);
|
||||
osmo_cc_msg_list_enqueue(&conn->os->write_list, msg, conn->callref);
|
||||
osmo_timer_schedule(&conn->tx_keepalive_timer, OSMO_CC_SOCKET_TX_KEEPALIVE);
|
||||
}
|
||||
|
||||
static void close_conn(osmo_cc_conn_t *conn, uint8_t socket_cause);
|
||||
|
||||
static void rx_keepalive_timeout(void *data)
|
||||
{
|
||||
osmo_cc_conn_t *conn = data;
|
||||
|
||||
LOGP(DLCC, LOGL_ERROR, "OsmoCC-Socket failed due to timeout.\n");
|
||||
close_conn(conn, OSMO_CC_SOCKET_CAUSE_TIMEOUT);
|
||||
}
|
||||
|
||||
static int socket_listen_cb(struct osmo_fd *ofd, unsigned int when);
|
||||
|
||||
/* create socket process and bind socket */
|
||||
int osmo_cc_open_socket(osmo_cc_socket_t *os, const char *host, uint16_t port, void *priv,
|
||||
void (*recv_msg_cb)(void *priv, uint32_t callref, osmo_cc_msg_t *msg), uint8_t location)
|
||||
{
|
||||
int try = 0, auto_port = 0;
|
||||
struct addrinfo *result, *rp;
|
||||
int rc, sock;
|
||||
|
||||
memset(os, 0, sizeof(*os));
|
||||
|
||||
try_again:
|
||||
/* check for given port, if NULL, autoselect port */
|
||||
if (!port || auto_port) {
|
||||
port = OSMO_CC_DEFAULT_PORT + try;
|
||||
try++;
|
||||
auto_port = 1;
|
||||
}
|
||||
|
||||
LOGP(DLCC, LOGL_DEBUG, "Create socket for host %s port %d.\n", host, port);
|
||||
|
||||
rc = _getaddrinfo(host, port, &result);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
for (rp = result; rp; rp = rp->ai_next) {
|
||||
int on = 1;
|
||||
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (sock < 0)
|
||||
continue;
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (unsigned char *)&on, sizeof(on));
|
||||
rc = bind(sock, rp->ai_addr, rp->ai_addrlen);
|
||||
if (rc == 0)
|
||||
break;
|
||||
close(sock);
|
||||
}
|
||||
freeaddrinfo(result);
|
||||
if (rp == NULL) {
|
||||
if (auto_port && port < OSMO_CC_DEFAULT_PORT_MAX) {
|
||||
LOGP(DLCC, LOGL_DEBUG, "Failed to bind host %s port %d, trying again.\n", host, port);
|
||||
goto try_again;
|
||||
}
|
||||
LOGP(DLCC, LOGL_ERROR, "Failed to bind given host %s port %d.\n", host, port);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* listen to socket */
|
||||
rc = listen(sock, 10);
|
||||
if (rc < 0) {
|
||||
LOGP(DLCC, LOGL_ERROR, "Failed to listen on socket.\n");
|
||||
close(sock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* register */
|
||||
os->ofd.fd = sock;
|
||||
os->ofd.cb = socket_listen_cb;
|
||||
os->ofd.data = os;
|
||||
os->ofd.when = OSMO_FD_READ;
|
||||
osmo_fd_register(&os->ofd);
|
||||
os->recv_msg_cb = recv_msg_cb;
|
||||
os->priv = priv;
|
||||
os->location = location;
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
static int socket_conn_cb(struct osmo_fd *ofd, unsigned int when);
|
||||
|
||||
/* create a connection */
|
||||
static osmo_cc_conn_t *open_conn(osmo_cc_socket_t *os, int sock, uint32_t callref, int read_setup)
|
||||
{
|
||||
osmo_cc_conn_t *conn, **connp;
|
||||
|
||||
/* create connection */
|
||||
conn = calloc(1, sizeof(*conn));
|
||||
if (!conn) {
|
||||
LOGP(DLCC, LOGL_ERROR, "No mem!\n");
|
||||
abort();
|
||||
}
|
||||
conn->os = os;
|
||||
conn->ofd.fd = sock;
|
||||
conn->ofd.cb = socket_conn_cb;
|
||||
conn->ofd.data = conn;
|
||||
conn->ofd.when = OSMO_FD_READ;
|
||||
osmo_fd_register(&conn->ofd);
|
||||
conn->read_version = 1;
|
||||
conn->write_version = 1;
|
||||
conn->read_setup = read_setup;
|
||||
if (callref)
|
||||
conn->callref = callref;
|
||||
else
|
||||
conn->callref = osmo_cc_new_callref();
|
||||
|
||||
osmo_timer_setup(&conn->tx_keepalive_timer, tx_keepalive_timeout, conn);
|
||||
osmo_timer_setup(&conn->rx_keepalive_timer, rx_keepalive_timeout, conn);
|
||||
osmo_timer_schedule(&conn->tx_keepalive_timer, OSMO_CC_SOCKET_TX_KEEPALIVE);
|
||||
osmo_timer_schedule(&conn->rx_keepalive_timer, OSMO_CC_SOCKET_RX_KEEPALIVE);
|
||||
|
||||
LOGP(DLCC, LOGL_DEBUG, "New socket connection (callref %d).\n", conn->callref);
|
||||
|
||||
/* attach to list */
|
||||
connp = &os->conn_list;
|
||||
while (*connp)
|
||||
connp = &((*connp)->next);
|
||||
*connp = conn;
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
/* remove a connection */
|
||||
static void close_conn(osmo_cc_conn_t *conn, uint8_t socket_cause)
|
||||
{
|
||||
osmo_cc_conn_t **connp;
|
||||
osmo_cc_msg_list_t *ml;
|
||||
|
||||
/* detach connection first, to prevent a destruction during message handling (double free) */
|
||||
connp = &conn->os->conn_list;
|
||||
while (*connp != conn)
|
||||
connp = &((*connp)->next);
|
||||
*connp = conn->next;
|
||||
/* send reject message, if socket_cause is set */
|
||||
if (socket_cause && !conn->read_setup) {
|
||||
/* receive a release or reject (depending on state), but only if we sent a setup */
|
||||
rej_msg(conn->os, conn->callref, socket_cause, 0, 0);
|
||||
}
|
||||
|
||||
LOGP(DLCC, LOGL_DEBUG, "Destroy socket connection (callref %d).\n", conn->callref);
|
||||
|
||||
/* close socket */
|
||||
if (conn->ofd.fd) {
|
||||
osmo_fd_unregister(&conn->ofd);
|
||||
close(conn->ofd.fd);
|
||||
}
|
||||
/* free partly received message */
|
||||
if (conn->read_msg)
|
||||
osmo_cc_free_msg(conn->read_msg);
|
||||
/* free send queue */
|
||||
while ((ml = conn->write_list)) {
|
||||
osmo_cc_free_msg(ml->msg);
|
||||
conn->write_list = ml->next;
|
||||
free(ml);
|
||||
}
|
||||
/* free timers */
|
||||
osmo_timer_del(&conn->tx_keepalive_timer);
|
||||
osmo_timer_del(&conn->rx_keepalive_timer);
|
||||
/* free connection (already detached above) */
|
||||
free(conn);
|
||||
}
|
||||
|
||||
/* close socket and remove */
|
||||
void osmo_cc_close_socket(osmo_cc_socket_t *os)
|
||||
{
|
||||
osmo_cc_msg_list_t *ml;
|
||||
|
||||
LOGP(DLCC, LOGL_DEBUG, "Destroy socket.\n");
|
||||
|
||||
/* free all connections */
|
||||
while (os->conn_list)
|
||||
close_conn(os->conn_list, 0);
|
||||
/* close socket */
|
||||
if (os->ofd.fd > 0) {
|
||||
osmo_fd_unregister(&os->ofd);
|
||||
close(os->ofd.fd);
|
||||
os->ofd.fd = 0;
|
||||
}
|
||||
/* free send queue */
|
||||
while ((ml = os->write_list)) {
|
||||
osmo_cc_free_msg(ml->msg);
|
||||
os->write_list = ml->next;
|
||||
free(ml);
|
||||
}
|
||||
}
|
||||
|
||||
/* send message to send_queue of sock instance */
|
||||
int osmo_cc_sock_send_msg(osmo_cc_socket_t *os, uint32_t callref, osmo_cc_msg_t *msg, const char *host, uint16_t port)
|
||||
{
|
||||
osmo_cc_msg_list_t *ml;
|
||||
|
||||
/* turn _IND into _REQ and _CNF into _RSP */
|
||||
msg->type &= ~1;
|
||||
|
||||
/* create list entry */
|
||||
ml = osmo_cc_msg_list_enqueue(&os->write_list, msg, callref);
|
||||
if (host)
|
||||
strncpy(ml->host, host, sizeof(ml->host) - 1);
|
||||
ml->port = port;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* receive message
|
||||
* return 1 if work was done.
|
||||
*/
|
||||
static int receive_conn(osmo_cc_conn_t *conn)
|
||||
{
|
||||
uint8_t socket_cause = OSMO_CC_SOCKET_CAUSE_BROKEN_PIPE;
|
||||
int rc;
|
||||
osmo_cc_msg_t *msg;
|
||||
uint8_t msg_type;
|
||||
int len;
|
||||
int work = 0;
|
||||
|
||||
/* get version from remote */
|
||||
if (conn->read_version) {
|
||||
rc = recv(conn->ofd.fd, conn->read_version_string + conn->read_version_pos,
|
||||
strlen(version_string) - conn->read_version_pos, 0);
|
||||
if (rc < 0 && errno == EAGAIN)
|
||||
return work;
|
||||
work = 1;
|
||||
if (rc <= 0) {
|
||||
goto close;
|
||||
}
|
||||
conn->read_version_pos += rc;
|
||||
if (conn->read_version_pos == strlen(version_string)) {
|
||||
conn->read_version = 0;
|
||||
if (!!memcmp(conn->read_version_string, version_string, strlen(version_string) - 1)) {
|
||||
LOGP(DLCC, LOGL_NOTICE, "Remote does not seem to be an Osmo-CC socket, rejecting!\n");
|
||||
socket_cause = OSMO_CC_SOCKET_CAUSE_FAILED;
|
||||
goto close;
|
||||
}
|
||||
if (conn->read_version_string[strlen(version_string) - 1] !=
|
||||
version_string[strlen(version_string) - 1]) {
|
||||
LOGP(DLCC, LOGL_NOTICE,
|
||||
"Remote Osmo-CC socket has wrong version (local=%s, remote=%s), rejecting!\n",
|
||||
version_string, conn->read_version_string);
|
||||
socket_cause = OSMO_CC_SOCKET_CAUSE_VERSION_MISMATCH;
|
||||
goto close;
|
||||
}
|
||||
} else
|
||||
return work;
|
||||
}
|
||||
|
||||
try_next_message:
|
||||
/* read message header from remote */
|
||||
if (!conn->read_msg) {
|
||||
rc = recv(conn->ofd.fd, ((uint8_t *)&conn->read_hdr) + conn->read_pos,
|
||||
sizeof(conn->read_hdr) - conn->read_pos, 0);
|
||||
if (rc < 0 && errno == EAGAIN)
|
||||
return work;
|
||||
work = 1;
|
||||
if (rc <= 0) {
|
||||
goto close;
|
||||
}
|
||||
conn->read_pos += rc;
|
||||
if (conn->read_pos == sizeof(conn->read_hdr)) {
|
||||
conn->read_msg = osmo_cc_new_msg(conn->read_hdr.type);
|
||||
if (!conn->read_msg)
|
||||
abort();
|
||||
conn->read_msg->length_networkorder = conn->read_hdr.length_networkorder;
|
||||
/* prepare for reading message */
|
||||
conn->read_pos = 0;
|
||||
} else
|
||||
return work;
|
||||
}
|
||||
|
||||
/* read message data from remote */
|
||||
msg = conn->read_msg;
|
||||
len = ntohs(msg->length_networkorder);
|
||||
if (len == 0)
|
||||
goto empty_message;
|
||||
rc = recv(conn->ofd.fd, msg->data + conn->read_pos, len - conn->read_pos, 0);
|
||||
if (rc < 0 && errno == EAGAIN)
|
||||
return work;
|
||||
work = 1;
|
||||
if (rc <= 0) {
|
||||
goto close;
|
||||
}
|
||||
conn->read_pos += rc;
|
||||
if (conn->read_pos == len) {
|
||||
empty_message:
|
||||
/* start RX keepalive timeer, if not already */
|
||||
osmo_timer_schedule(&conn->rx_keepalive_timer, OSMO_CC_SOCKET_RX_KEEPALIVE);
|
||||
/* we got our setup message, so we clear the flag */
|
||||
conn->read_setup = 0;
|
||||
/* prepare for reading header */
|
||||
conn->read_pos = 0;
|
||||
/* detach message first, because the connection might be destroyed during message handling */
|
||||
msg_type = conn->read_msg->type;
|
||||
conn->read_msg = NULL;
|
||||
/* drop dummy or forward message */
|
||||
if (msg_type == OSMO_CC_MSG_DUMMY_REQ)
|
||||
osmo_cc_free_msg(msg);
|
||||
else
|
||||
conn->os->recv_msg_cb(conn->os->priv, conn->callref, msg);
|
||||
if (msg_type == OSMO_CC_MSG_REL_REQ || msg_type == OSMO_CC_MSG_REJ_REQ) {
|
||||
LOGP(DLCC, LOGL_DEBUG, "closing socket because we received a release or reject message.\n");
|
||||
close_conn(conn, 0);
|
||||
return 1; /* conn removed */
|
||||
}
|
||||
goto try_next_message;
|
||||
}
|
||||
return work;
|
||||
|
||||
close:
|
||||
LOGP(DLCC, LOGL_ERROR, "OsmoCC-Socket failed, socket cause %d.\n", socket_cause);
|
||||
close_conn(conn, socket_cause);
|
||||
return work; /* conn removed */
|
||||
}
|
||||
|
||||
/* transmit message
|
||||
* return 1 if work was done.
|
||||
*/
|
||||
static int transmit_conn(osmo_cc_conn_t *conn)
|
||||
{
|
||||
uint8_t socket_cause = OSMO_CC_SOCKET_CAUSE_BROKEN_PIPE;
|
||||
int rc;
|
||||
osmo_cc_msg_t *msg;
|
||||
int len;
|
||||
osmo_cc_msg_list_t *ml;
|
||||
int work = 0;
|
||||
|
||||
/* send socket version to remote */
|
||||
if (conn->write_version) {
|
||||
rc = write(conn->ofd.fd, version_string, strlen(version_string));
|
||||
if (rc < 0 && errno == EAGAIN)
|
||||
return work;
|
||||
work = 1;
|
||||
if (rc <= 0) {
|
||||
goto close;
|
||||
}
|
||||
if (rc != strlen(version_string)) {
|
||||
LOGP(DLCC, LOGL_ERROR, "short write, please fix handling!\n");
|
||||
abort();
|
||||
}
|
||||
conn->write_version = 0;
|
||||
}
|
||||
|
||||
/* send message to remote */
|
||||
while (conn->write_list) {
|
||||
osmo_timer_del(&conn->tx_keepalive_timer);
|
||||
msg = conn->write_list->msg;
|
||||
len = sizeof(*msg) + ntohs(msg->length_networkorder);
|
||||
rc = write(conn->ofd.fd, msg, len);
|
||||
if (rc < 0 && errno == EAGAIN)
|
||||
return work;
|
||||
work = 1;
|
||||
if (rc <= 0) {
|
||||
goto close;
|
||||
}
|
||||
if (rc != len) {
|
||||
LOGP(DLCC, LOGL_ERROR, "short write, please fix handling!\n");
|
||||
abort();
|
||||
}
|
||||
/* close socket after sending release/reject message */
|
||||
if (msg->type == OSMO_CC_MSG_REL_REQ || msg->type == OSMO_CC_MSG_REJ_REQ) {
|
||||
LOGP(DLCC, LOGL_DEBUG, "closing socket because we sent a release or reject message.\n");
|
||||
close_conn(conn, 0);
|
||||
return work; /* conn removed */
|
||||
}
|
||||
/* free message after sending */
|
||||
ml = conn->write_list;
|
||||
conn->write_list = ml->next;
|
||||
osmo_cc_free_msg(msg);
|
||||
free(ml);
|
||||
}
|
||||
|
||||
/* start TX keepalive timer, if not already
|
||||
* because we stop at every message above, we actually restart the timer here.
|
||||
* only if there is no message for the amount of time, the timer fires.
|
||||
*/
|
||||
if (!osmo_timer_pending(&conn->tx_keepalive_timer))
|
||||
osmo_timer_schedule(&conn->tx_keepalive_timer, OSMO_CC_SOCKET_TX_KEEPALIVE);
|
||||
|
||||
return work;
|
||||
|
||||
close:
|
||||
LOGP(DLCC, LOGL_NOTICE, "OsmoCC-Socket failed.\n");
|
||||
close_conn(conn, socket_cause);
|
||||
return work; /* conn removed */
|
||||
}
|
||||
|
||||
/* handle all sockets of a socket interface
|
||||
* return 1 if work was done.
|
||||
*/
|
||||
int osmo_cc_handle_socket(osmo_cc_socket_t *os)
|
||||
{
|
||||
int sock;
|
||||
osmo_cc_conn_t *conn;
|
||||
osmo_cc_msg_list_t *ml, **mlp;
|
||||
int flags;
|
||||
struct addrinfo *result, *rp;
|
||||
int rc;
|
||||
int work = 0;
|
||||
|
||||
/* handle messages in send queue */
|
||||
while ((ml = os->write_list)) {
|
||||
work = 1;
|
||||
/* detach list entry */
|
||||
os->write_list = ml->next;
|
||||
ml->next = NULL;
|
||||
/* search for socket connection */
|
||||
for (conn = os->conn_list; conn; conn=conn->next) {
|
||||
if (conn->callref == ml->callref)
|
||||
break;
|
||||
}
|
||||
if (conn) {
|
||||
/* attach to list */
|
||||
mlp = &conn->write_list;
|
||||
while (*mlp)
|
||||
mlp = &((*mlp)->next);
|
||||
*mlp = ml;
|
||||
conn->ofd.when |= OSMO_FD_WRITE;
|
||||
/* done with message */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* reject and release are ignored */
|
||||
if (ml->msg->type == OSMO_CC_MSG_REJ_REQ
|
||||
|| ml->msg->type == OSMO_CC_MSG_REL_REQ) {
|
||||
/* drop message */
|
||||
osmo_cc_free_msg(ml->msg);
|
||||
free(ml);
|
||||
/* done with message */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* reject, if this is not a setup message */
|
||||
if (ml->msg->type != OSMO_CC_MSG_SETUP_REQ
|
||||
&& ml->msg->type != OSMO_CC_MSG_ATTACH_REQ) {
|
||||
LOGP(DLCC, LOGL_ERROR, "Message with unknown callref.\n");
|
||||
rej_msg(os, ml->callref, 0, OSMO_CC_ISDN_CAUSE_INVAL_CALLREF, 0);
|
||||
/* drop message */
|
||||
osmo_cc_free_msg(ml->msg);
|
||||
free(ml);
|
||||
/* done with message */
|
||||
continue;
|
||||
}
|
||||
/* connect to remote */
|
||||
rc = _getaddrinfo(ml->host, ml->port, &result);
|
||||
if (rc < 0) {
|
||||
rej_msg(os, ml->callref, OSMO_CC_SOCKET_CAUSE_FAILED, 0, 0);
|
||||
/* drop message */
|
||||
osmo_cc_free_msg(ml->msg);
|
||||
free(ml);
|
||||
/* done with message */
|
||||
continue;
|
||||
}
|
||||
for (rp = result; rp; rp = rp->ai_next) {
|
||||
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (sock < 0)
|
||||
continue;
|
||||
/* set nonblocking io, to prevent connect() and subsequent reads from blocking */
|
||||
flags = fcntl(sock, F_GETFL);
|
||||
flags |= O_NONBLOCK;
|
||||
fcntl(sock, F_SETFL, flags);
|
||||
/* connect */
|
||||
rc = connect(sock, rp->ai_addr, rp->ai_addrlen);
|
||||
if (rc == 0 || errno == EINPROGRESS)
|
||||
break;
|
||||
close(sock);
|
||||
}
|
||||
freeaddrinfo(result);
|
||||
if (rp == NULL) {
|
||||
LOGP(DLCC, LOGL_ERROR, "Failed to connect to given host %s port %d.\n", ml->host, ml->port);
|
||||
rej_msg(os, ml->callref, OSMO_CC_SOCKET_CAUSE_FAILED, 0, 0);
|
||||
/* drop message */
|
||||
osmo_cc_free_msg(ml->msg);
|
||||
free(ml);
|
||||
/* done with message */
|
||||
continue;
|
||||
}
|
||||
/* create connection */
|
||||
conn = open_conn(os, sock, ml->callref, 0);
|
||||
/* attach to list */
|
||||
conn->write_list = ml;
|
||||
conn->ofd.when |= OSMO_FD_WRITE;
|
||||
/* done with (setup) message */
|
||||
}
|
||||
|
||||
return work;
|
||||
}
|
||||
|
||||
static int socket_listen_cb(struct osmo_fd *ofd, unsigned int when)
|
||||
{
|
||||
osmo_cc_socket_t *os = ofd->data;
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t slen = sizeof(sa);
|
||||
int sock;
|
||||
int flags;
|
||||
|
||||
if (when & OSMO_FD_READ) {
|
||||
/* handle new socket connection */
|
||||
if ((sock = accept(os->ofd.fd, (struct sockaddr *)&sa, &slen)) > 0) {
|
||||
/* set nonblocking io, to prevent subsequent reads from blocking */
|
||||
flags = fcntl(sock, F_GETFL);
|
||||
flags |= O_NONBLOCK;
|
||||
fcntl(sock, F_SETFL, flags);
|
||||
/* create connection */
|
||||
open_conn(os, sock, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socket_conn_cb(struct osmo_fd *ofd, unsigned int when)
|
||||
{
|
||||
osmo_cc_conn_t *conn = ofd->data;
|
||||
int work;
|
||||
|
||||
if (when & OSMO_FD_READ) {
|
||||
/* check for rx */
|
||||
work = receive_conn(conn);
|
||||
/* if "change" is set, connection list might have changed, so we restart processing the list */
|
||||
if (work)
|
||||
return 0;
|
||||
}
|
||||
if (when & OSMO_FD_WRITE) {
|
||||
/* check for tx */
|
||||
work = transmit_conn(conn);
|
||||
/* if "change" is set, connection list might have changed, so we restart processing the list */
|
||||
if (work)
|
||||
return 0;
|
||||
else
|
||||
conn->ofd.when &= ~OSMO_FD_WRITE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue