Add libraries from Osmocom-Analog
This commit is contained in:
parent
22dab51ab1
commit
e2476976a7
|
@ -33,4 +33,11 @@ Doxyfile
|
||||||
|
|
||||||
.*.sw?
|
.*.sw?
|
||||||
|
|
||||||
|
src/libdebug/libdebug.a
|
||||||
|
src/libg711/libg711.a
|
||||||
|
src/libjitter/libjitter.a
|
||||||
|
src/liboptions/liboptions.a
|
||||||
|
src/libosmocc/libosmocc.a
|
||||||
|
src/libsample/libsample.a
|
||||||
|
src/libtimer/libtimer.a
|
||||||
src/isdn/osmo-cc-misdn-endpoint
|
src/isdn/osmo-cc-misdn-endpoint
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||||
|
|
||||||
|
noinst_LIBRARIES = libdebug.a
|
||||||
|
|
||||||
|
libdebug_a_SOURCES = \
|
||||||
|
debug.c
|
||||||
|
|
|
@ -0,0 +1,192 @@
|
||||||
|
/* Simple debug functions for level and category filtering
|
||||||
|
*
|
||||||
|
* (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 <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
const char *debug_level[] = {
|
||||||
|
"debug ",
|
||||||
|
"info ",
|
||||||
|
"notice ",
|
||||||
|
"error ",
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct debug_cat {
|
||||||
|
const char *name;
|
||||||
|
const char *color;
|
||||||
|
} debug_cat[] = {
|
||||||
|
{ "options", "\033[0;33m" },
|
||||||
|
{ "dsp", "\033[0;31m" },
|
||||||
|
{ "isdn", "\033[1;35m" },
|
||||||
|
{ "misdn", "\033[0;34m" },
|
||||||
|
{ "dss1", "\033[1;34m" },
|
||||||
|
{ "cc", "\033[1;32m" },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
int debuglevel = DEBUG_INFO;
|
||||||
|
uint64_t debug_mask = ~0;
|
||||||
|
extern int num_kanal;
|
||||||
|
|
||||||
|
void _printdebug(const char *file, const char __attribute__((unused)) *function, int line, int cat, int level, const char *kanal, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
char buffer[4096], *b = buffer;
|
||||||
|
int s = sizeof(buffer) - 1;
|
||||||
|
const char *p;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
if (debuglevel > level)
|
||||||
|
return;
|
||||||
|
|
||||||
|
buffer[sizeof(buffer) - 1] = '\0';
|
||||||
|
|
||||||
|
/* if kanal is used, prefix the channel number */
|
||||||
|
if (num_kanal > 1 && kanal) {
|
||||||
|
sprintf(buffer, "(chan %s) ", kanal);
|
||||||
|
b = strchr(buffer, '\0');
|
||||||
|
s -= strlen(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(debug_mask & ((uint64_t)1 << cat)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
vsnprintf(b, s, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
while ((p = strchr(file, '/')))
|
||||||
|
file = p + 1;
|
||||||
|
printf("%s%s:%4d %s: %s\033[0;39m", debug_cat[cat].color, file, line, debug_level[level], buffer);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *debug_amplitude(double level)
|
||||||
|
{
|
||||||
|
static char text[42];
|
||||||
|
|
||||||
|
strcpy(text, " : ");
|
||||||
|
if (level > 1.0)
|
||||||
|
level = 1.0;
|
||||||
|
if (level < -1.0)
|
||||||
|
level = -1.0;
|
||||||
|
text[20 + (int)(level * 20)] = '*';
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define level2db(level) (20 * log10(level))
|
||||||
|
|
||||||
|
const char *debug_db(double level_db)
|
||||||
|
{
|
||||||
|
static char text[128];
|
||||||
|
int l;
|
||||||
|
|
||||||
|
strcpy(text, ": . : . : . : . : . : . : . : . | . : . : . : . : . : . : . : . :");
|
||||||
|
if (level_db <= 0.0)
|
||||||
|
return text;
|
||||||
|
l = (int)round(level2db(level_db));
|
||||||
|
if (l > 48)
|
||||||
|
return text;
|
||||||
|
if (l < -48)
|
||||||
|
return text;
|
||||||
|
text[l + 48] = '*';
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
void debug_list_cat(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
printf("Give number of debug level:\n");
|
||||||
|
for (i = 0; debug_level[i]; i++)
|
||||||
|
printf(" %d = %s\n", i, debug_level[i]);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("Give name(s) of debug category:\n");
|
||||||
|
for (i = 0; debug_cat[i].name; i++)
|
||||||
|
printf(" %s%s\033[0;39m\n", debug_cat[i].color, debug_cat[i].name);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_debug_opt(const char *optarg)
|
||||||
|
{
|
||||||
|
int i, max_level = 0;
|
||||||
|
char *dstring, *p;
|
||||||
|
|
||||||
|
for (i = 0; debug_level[i]; i++)
|
||||||
|
max_level = i;
|
||||||
|
|
||||||
|
dstring = strdup(optarg);
|
||||||
|
p = strsep(&dstring, ",");
|
||||||
|
for (i = 0; i < p[i]; i++) {
|
||||||
|
if (p[i] < '0' || p[i] > '9') {
|
||||||
|
fprintf(stderr, "Only digits are allowed for debug level!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debuglevel = atoi(p);
|
||||||
|
if (debuglevel > max_level) {
|
||||||
|
fprintf(stderr, "Debug level too high, use 'list' to show available levels!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (dstring)
|
||||||
|
debug_mask = 0;
|
||||||
|
while((p = strsep(&dstring, ","))) {
|
||||||
|
for (i = 0; debug_cat[i].name; i++) {
|
||||||
|
if (!strcasecmp(p, debug_cat[i].name))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!debug_cat[i].name) {
|
||||||
|
fprintf(stderr, "Given debug category '%s' unknown, use 'list' to show available categories!\n", p);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
debug_mask |= ((uint64_t)1 << i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *debug_hex(const uint8_t *data, int len)
|
||||||
|
{
|
||||||
|
static char *text = NULL;
|
||||||
|
char *p;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (text)
|
||||||
|
free(text);
|
||||||
|
p = text = calloc(1, len * 3 + 1);
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
sprintf(p, "%02x ", *data++);
|
||||||
|
p += 3;
|
||||||
|
}
|
||||||
|
if (text[0])
|
||||||
|
p[-1] = '\0';
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
#define DEBUG_DEBUG 0 /* debug info, not for normal use */
|
||||||
|
#define DEBUG_INFO 1 /* all info about process */
|
||||||
|
#define DEBUG_NOTICE 2 /* something unexpected happens */
|
||||||
|
#define DEBUG_ERROR 3 /* there is an error with this software */
|
||||||
|
|
||||||
|
#define DOPTIONS 0
|
||||||
|
#define DDSP 1
|
||||||
|
#define DISDN 2
|
||||||
|
#define DMISDN 3
|
||||||
|
#define DDSS1 4
|
||||||
|
#define DCC 5
|
||||||
|
|
||||||
|
void get_win_size(int *w, int *h);
|
||||||
|
|
||||||
|
#define PDEBUG(cat, level, fmt, arg...) _printdebug(__FILE__, __FUNCTION__, __LINE__, cat, level, NULL, fmt, ## arg)
|
||||||
|
#define PDEBUG_CHAN(cat, level, fmt, arg...) _printdebug(__FILE__, __FUNCTION__, __LINE__, cat, level, CHAN, fmt, ## arg)
|
||||||
|
void _printdebug(const char *file, const char *function, int line, int cat, int level, const char *chan_str, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 7, 8)));
|
||||||
|
|
||||||
|
const char *debug_amplitude(double level);
|
||||||
|
const char *debug_db(double level_db);
|
||||||
|
|
||||||
|
void debug_list_cat(void);
|
||||||
|
int parse_debug_opt(const char *opt);
|
||||||
|
|
||||||
|
extern int debuglevel;
|
||||||
|
|
||||||
|
extern void (*clear_console_text)(void);
|
||||||
|
extern void (*print_console_text)(void);
|
||||||
|
|
||||||
|
extern int debug_limit_scroll;
|
||||||
|
|
||||||
|
const char *debug_hex(const uint8_t *data, int len);
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||||
|
|
||||||
|
noinst_LIBRARIES = libg711.a
|
||||||
|
|
||||||
|
libg711_a_SOURCES = \
|
||||||
|
g711.c
|
||||||
|
|
|
@ -0,0 +1,537 @@
|
||||||
|
/*****************************************************************************\
|
||||||
|
** **
|
||||||
|
** PBX4Linux **
|
||||||
|
** **
|
||||||
|
**---------------------------------------------------------------------------**
|
||||||
|
** Copyright: Andreas Eversberg (GPL) **
|
||||||
|
** **
|
||||||
|
** audio conversions for alaw and ulaw **
|
||||||
|
** **
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* ulaw -> signed 16-bit */
|
||||||
|
static int16_t g711_ulaw_flipped_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_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_to_linear[i] = g711_ulaw_flipped_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_flipped_to_linear[j])
|
||||||
|
j++;
|
||||||
|
g711_linear_to_ulaw_flipped[(i - 32768) & 0xffff] = j;
|
||||||
|
g711_linear_to_ulaw[(i - 32768) & 0xffff] = g711_flip[j];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
j = 255;
|
||||||
|
while(i < 65536) {
|
||||||
|
if (i - 32768 > g711_alaw_flipped_to_linear[j])
|
||||||
|
j--;
|
||||||
|
g711_linear_to_ulaw_flipped[(i - 32768) & 0xffff] = j;
|
||||||
|
g711_linear_to_ulaw[(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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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,17 @@
|
||||||
|
void g711_init(void);
|
||||||
|
void g711_encode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
void g711_encode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
void g711_decode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
void g711_decode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
void g711_encode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
void g711_encode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
void g711_decode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
void g711_decode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
void g711_transcode_alaw_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
void g711_transcode_alaw_flipped_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
void g711_transcode_alaw_to_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
void g711_transcode_ulaw_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
void g711_transcode_ulaw_flipped_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
void g711_transcode_ulaw_to_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
void g711_transcode_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||||
|
|
||||||
|
noinst_LIBRARIES = libjitter.a
|
||||||
|
|
||||||
|
libjitter_a_SOURCES = \
|
||||||
|
jitter.c
|
|
@ -0,0 +1,125 @@
|
||||||
|
/* Jitter buffering functions
|
||||||
|
*
|
||||||
|
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "../libsample/sample.h"
|
||||||
|
#include "../libdebug/debug.h"
|
||||||
|
#include "jitter.h"
|
||||||
|
|
||||||
|
/* create jitter buffer */
|
||||||
|
int jitter_create(jitter_t *jitter, int length)
|
||||||
|
{
|
||||||
|
memset(jitter, 0, sizeof(*jitter));
|
||||||
|
jitter->spl = malloc(length * sizeof(sample_t));
|
||||||
|
if (!jitter->spl) {
|
||||||
|
PDEBUG(DDSP, DEBUG_ERROR, "No memory for jitter buffer.\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
jitter->len = length;
|
||||||
|
|
||||||
|
jitter_reset(jitter);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jitter_reset(jitter_t *jitter)
|
||||||
|
{
|
||||||
|
memset(jitter->spl, 0, jitter->len * sizeof(sample_t));
|
||||||
|
|
||||||
|
/* put write pointer ahead by half of the buffer length */
|
||||||
|
jitter->inptr = jitter->len / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jitter_destroy(jitter_t *jitter)
|
||||||
|
{
|
||||||
|
if (jitter->spl) {
|
||||||
|
free(jitter->spl);
|
||||||
|
jitter->spl = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store audio in jitterbuffer
|
||||||
|
*
|
||||||
|
* stop if buffer is completely filled
|
||||||
|
*/
|
||||||
|
void jitter_save(jitter_t *jb, sample_t *samples, int length)
|
||||||
|
{
|
||||||
|
sample_t *spl;
|
||||||
|
int inptr, outptr, len, space;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
spl = jb->spl;
|
||||||
|
inptr = jb->inptr;
|
||||||
|
outptr = jb->outptr;
|
||||||
|
len = jb->len;
|
||||||
|
space = (outptr - inptr + len - 1) % len;
|
||||||
|
|
||||||
|
if (space < length)
|
||||||
|
length = space;
|
||||||
|
for (i = 0; i < length; i++) {
|
||||||
|
spl[inptr++] = *samples++;
|
||||||
|
if (inptr == len)
|
||||||
|
inptr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
jb->inptr = inptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get audio from jitterbuffer
|
||||||
|
*/
|
||||||
|
void jitter_load(jitter_t *jb, sample_t *samples, int length)
|
||||||
|
{
|
||||||
|
sample_t *spl;
|
||||||
|
int inptr, outptr, len, fill;
|
||||||
|
int i, ii;
|
||||||
|
|
||||||
|
spl = jb->spl;
|
||||||
|
inptr = jb->inptr;
|
||||||
|
outptr = jb->outptr;
|
||||||
|
len = jb->len;
|
||||||
|
fill = (inptr - outptr + len) % len;
|
||||||
|
|
||||||
|
if (fill < length)
|
||||||
|
ii = fill;
|
||||||
|
else
|
||||||
|
ii = length;
|
||||||
|
|
||||||
|
/* fill what we got */
|
||||||
|
for (i = 0; i < ii; i++) {
|
||||||
|
*samples++ = spl[outptr++];
|
||||||
|
if (outptr == len)
|
||||||
|
outptr = 0;
|
||||||
|
}
|
||||||
|
/* on underrun, fill with silence */
|
||||||
|
for (; i < length; i++) {
|
||||||
|
*samples++ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
jb->outptr = outptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jitter_clear(jitter_t *jb)
|
||||||
|
{
|
||||||
|
jb->inptr = jb->outptr = 0;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
typedef struct jitter {
|
||||||
|
sample_t *spl; /* pointer to sample buffer */
|
||||||
|
int len; /* buffer size: number of samples */
|
||||||
|
int inptr, outptr; /* write pointer and read pointer */
|
||||||
|
} jitter_t;
|
||||||
|
|
||||||
|
int jitter_create(jitter_t *jitter, int length);
|
||||||
|
void jitter_reset(jitter_t *jitter);
|
||||||
|
void jitter_destroy(jitter_t *jitter);
|
||||||
|
void jitter_save(jitter_t *jb, sample_t *samples, int length);
|
||||||
|
void jitter_load(jitter_t *jb, sample_t *samples, int length);
|
||||||
|
void jitter_clear(jitter_t *jb);
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||||
|
|
||||||
|
noinst_LIBRARIES = liboptions.a
|
||||||
|
|
||||||
|
liboptions_a_SOURCES = \
|
||||||
|
options.c
|
||||||
|
|
|
@ -0,0 +1,275 @@
|
||||||
|
/* command line options and config file parsing
|
||||||
|
*
|
||||||
|
* (C) 2018 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 <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "options.h"
|
||||||
|
#include "../libdebug/debug.h"
|
||||||
|
|
||||||
|
typedef struct option {
|
||||||
|
struct option *next;
|
||||||
|
int short_option;
|
||||||
|
const char *long_option;
|
||||||
|
int parameter_count;
|
||||||
|
} option_t;
|
||||||
|
|
||||||
|
static option_t *option_head = NULL;
|
||||||
|
static option_t **option_tailp = &option_head;
|
||||||
|
static int first_option = 1;
|
||||||
|
|
||||||
|
void option_add(int short_option, const char *long_option, int parameter_count)
|
||||||
|
{
|
||||||
|
option_t *option;
|
||||||
|
|
||||||
|
/* check if option already exists */
|
||||||
|
for (option = option_head; option; option = option->next) {
|
||||||
|
if (option->short_option == short_option
|
||||||
|
|| !strcmp(option->long_option, long_option)) {
|
||||||
|
PDEBUG(DOPTIONS, DEBUG_ERROR, "Option '%s' added twice, please fix!\n", option->long_option);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
option = calloc(1, sizeof(*option));
|
||||||
|
if (!option) {
|
||||||
|
PDEBUG(DOPTIONS, DEBUG_ERROR, "No mem!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
option->short_option = short_option;
|
||||||
|
option->long_option = long_option;
|
||||||
|
option->parameter_count = parameter_count;
|
||||||
|
*option_tailp = option;
|
||||||
|
option_tailp = &(option->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
int options_config_file(const char *config_file, int (*handle_options)(int short_option, int argi, char *argv[]))
|
||||||
|
{
|
||||||
|
static const char *home;
|
||||||
|
char config[256];
|
||||||
|
FILE *fp;
|
||||||
|
char buffer[256], opt[256], param[256], *p, *argv[16];
|
||||||
|
char params[1024];
|
||||||
|
int line;
|
||||||
|
int rc = 1;
|
||||||
|
int i, j, quote;
|
||||||
|
option_t *option;
|
||||||
|
|
||||||
|
/* open config file */
|
||||||
|
home = getenv("HOME");
|
||||||
|
if (home == NULL)
|
||||||
|
return 1;
|
||||||
|
sprintf(config, "%s/%s", home, config_file + 2);
|
||||||
|
|
||||||
|
fp = fopen(config, "r");
|
||||||
|
if (!fp) {
|
||||||
|
PDEBUG(DOPTIONS, DEBUG_INFO, "Config file '%s' seems not to exist, using command line options only.\n", config);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse config file */
|
||||||
|
line = 0;
|
||||||
|
while((fgets(buffer, sizeof(buffer), fp))) {
|
||||||
|
line++;
|
||||||
|
/* prevent buffer overflow */
|
||||||
|
buffer[sizeof(buffer) - 1] = '\0';
|
||||||
|
/* cut away new-line and white spaces */
|
||||||
|
while (buffer[0] && buffer[strlen(buffer) - 1] <= ' ')
|
||||||
|
buffer[strlen(buffer) - 1] = '\0';
|
||||||
|
p = buffer;
|
||||||
|
/* remove white spaces in front of first keyword */
|
||||||
|
while (*p > '\0' && *p <= ' ')
|
||||||
|
p++;
|
||||||
|
/* ignore '#' lines */
|
||||||
|
if (*p == '#')
|
||||||
|
continue;
|
||||||
|
/* get option form line */
|
||||||
|
i = 0;
|
||||||
|
while (*p > ' ')
|
||||||
|
opt[i++] = *p++;
|
||||||
|
opt[i] = '\0';
|
||||||
|
if (opt[0] == '\0')
|
||||||
|
continue;
|
||||||
|
/* skip white spaces behind option */
|
||||||
|
while (*p > '\0' && *p <= ' ')
|
||||||
|
p++;
|
||||||
|
/* get param from line */
|
||||||
|
params[0] = '\0';
|
||||||
|
i = 0;
|
||||||
|
while (*p) {
|
||||||
|
/* copy parameter */
|
||||||
|
j = 0;
|
||||||
|
quote = 0;
|
||||||
|
while (*p) {
|
||||||
|
/* escape allows all following characters */
|
||||||
|
if (*p == '\\') {
|
||||||
|
p++;
|
||||||
|
if (*p)
|
||||||
|
param[j++] = *p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* no quote, check for them or break on white space */
|
||||||
|
if (quote == 0) {
|
||||||
|
if (*p == '\'') {
|
||||||
|
quote = 1;
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*p == '\"') {
|
||||||
|
quote = 2;
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*p <= ' ')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* single quote, check for unquote */
|
||||||
|
if (quote == 1 && *p == '\'') {
|
||||||
|
quote = 0;
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* double quote, check for unquote */
|
||||||
|
if (quote == 2 && *p == '\"') {
|
||||||
|
quote = 0;
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* copy character */
|
||||||
|
param[j++] = *p++;
|
||||||
|
}
|
||||||
|
param[j] = '\0';
|
||||||
|
argv[i] = strdup(param);
|
||||||
|
sprintf(strchr(params, '\0'), " '%s'", param);
|
||||||
|
/* skip white spaces behind option */
|
||||||
|
while (*p > '\0' && *p <= ' ')
|
||||||
|
p++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
/* search option */
|
||||||
|
for (option = option_head; option; option = option->next) {
|
||||||
|
if (opt[0] == option->short_option && opt[1] == '\0') {
|
||||||
|
PDEBUG(DOPTIONS, DEBUG_INFO, "Config file option '%s' ('%s'), parameter%s\n", opt, option->long_option, params);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!strcmp(opt, option->long_option)) {
|
||||||
|
PDEBUG(DOPTIONS, DEBUG_INFO, "Config file option '%s', parameter%s\n", opt, params);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!option) {
|
||||||
|
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given option '%s' in config file '%s' at line %d is not a valid option, use '-h' for help!\n", opt, config_file, line);
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (option->parameter_count != i) {
|
||||||
|
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given option '%s' in config file '%s' at line %d requires %d parameter(s), use '-h' for help!\n", opt, config_file, line, option->parameter_count);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
rc = handle_options(option->short_option, 0, argv);
|
||||||
|
if (rc <= 0)
|
||||||
|
goto done;
|
||||||
|
first_option = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
/* close config file */
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int options_command_line(int argc, char *argv[], int (*handle_options)(int short_option, int argi, char *argv[]))
|
||||||
|
{
|
||||||
|
option_t *option;
|
||||||
|
char params[1024];
|
||||||
|
int argi, i;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
for (argi = 1; argi < argc; argi++) {
|
||||||
|
if (argv[argi][0] == '-') {
|
||||||
|
if (argv[argi][1] != '-') {
|
||||||
|
if (strlen(argv[argi]) != 2) {
|
||||||
|
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' exceeds one character, use '-h' for help!\n", argv[argi]);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
/* -x */
|
||||||
|
for (option = option_head; option; option = option->next) {
|
||||||
|
if (argv[argi][1] == option->short_option) {
|
||||||
|
if (option->parameter_count && argi + option->parameter_count < argc) {
|
||||||
|
params[0] = '\0';
|
||||||
|
for (i = 0; i < option->parameter_count; i++)
|
||||||
|
sprintf(strchr(params, '\0'), " '%s'", argv[argi + 1 + i]);
|
||||||
|
PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s' ('--%s'), parameter%s\n", argv[argi], option->long_option, params);
|
||||||
|
} else
|
||||||
|
PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s' ('--%s')\n", argv[argi], option->long_option);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* --xxxxxx */
|
||||||
|
for (option = option_head; option; option = option->next) {
|
||||||
|
if (!strcmp(argv[argi] + 2, option->long_option)) {
|
||||||
|
if (option->parameter_count && argi + option->parameter_count < argc) {
|
||||||
|
params[0] = '\0';
|
||||||
|
for (i = 0; i < option->parameter_count; i++)
|
||||||
|
sprintf(strchr(params, '\0'), " '%s'", argv[argi + 1 + i]);
|
||||||
|
PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s', parameter%s\n", argv[argi], params);
|
||||||
|
} else
|
||||||
|
PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s'\n", argv[argi]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!option) {
|
||||||
|
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' is not a valid option, use '-h' for help!\n", argv[argi]);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (argi + option->parameter_count >= argc) {
|
||||||
|
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' requires %d parameter(s), use '-h' for help!\n", argv[argi], option->parameter_count);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
rc = handle_options(option->short_option, argi + 1, argv);
|
||||||
|
if (rc <= 0)
|
||||||
|
return rc;
|
||||||
|
first_option = 0;
|
||||||
|
argi += option->parameter_count;
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no more options, so we check if there is an option after a non-option parameter */
|
||||||
|
for (i = argi; i < argc; i++) {
|
||||||
|
if (argv[i][0] == '-') {
|
||||||
|
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' behind command line parameter '%s' not allowed! Please put all command line options before command line parameter(s).\n", argv[i], argv[argi]);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return argi;
|
||||||
|
}
|
||||||
|
|
||||||
|
int option_is_first(void)
|
||||||
|
{
|
||||||
|
return first_option;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
void option_add(int short_option, const char *long_option, int parameter_count);
|
||||||
|
int options_config_file(const char *config_file, int (*handle_options)(int short_option, int argi, char *argv[]));
|
||||||
|
int options_command_line(int argc, char *argv[], int (*handle_options)(int short_option, int argi, char *argv[]));
|
||||||
|
int option_is_first(void);
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||||
|
|
||||||
|
noinst_LIBRARIES = libosmocc.a
|
||||||
|
|
||||||
|
libosmocc_a_SOURCES = \
|
||||||
|
message.c \
|
||||||
|
socket.c \
|
||||||
|
cause.c \
|
||||||
|
screen.c \
|
||||||
|
endpoint.c \
|
||||||
|
session.c \
|
||||||
|
sdp.c \
|
||||||
|
rtp.c \
|
||||||
|
helper.c
|
||||||
|
|
|
@ -0,0 +1,252 @@
|
||||||
|
/* OSMO-CC Processing: convert causes
|
||||||
|
*
|
||||||
|
* (C) 2019 by Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include "message.h"
|
||||||
|
#include "cause.h"
|
||||||
|
|
||||||
|
/* stolen from freeswitch */
|
||||||
|
/* map sip responses to QSIG cause codes ala RFC4497 section 8.4.4 */
|
||||||
|
static uint8_t status2isdn_cause(uint16_t status)
|
||||||
|
{
|
||||||
|
switch (status) {
|
||||||
|
case 200:
|
||||||
|
return 16; //SWITCH_CAUSE_NORMAL_CLEARING;
|
||||||
|
case 401:
|
||||||
|
case 402:
|
||||||
|
case 403:
|
||||||
|
case 407:
|
||||||
|
case 603:
|
||||||
|
return 21; //SWITCH_CAUSE_CALL_REJECTED;
|
||||||
|
case 404:
|
||||||
|
return 1; //SWITCH_CAUSE_UNALLOCATED_NUMBER;
|
||||||
|
case 485:
|
||||||
|
case 604:
|
||||||
|
return 3; //SWITCH_CAUSE_NO_ROUTE_DESTINATION;
|
||||||
|
case 408:
|
||||||
|
case 504:
|
||||||
|
return 102; //SWITCH_CAUSE_RECOVERY_ON_TIMER_EXPIRE;
|
||||||
|
case 410:
|
||||||
|
return 22; //SWITCH_CAUSE_NUMBER_CHANGED;
|
||||||
|
case 413:
|
||||||
|
case 414:
|
||||||
|
case 416:
|
||||||
|
case 420:
|
||||||
|
case 421:
|
||||||
|
case 423:
|
||||||
|
case 505:
|
||||||
|
case 513:
|
||||||
|
return 127; //SWITCH_CAUSE_INTERWORKING;
|
||||||
|
case 480:
|
||||||
|
return 180; //SWITCH_CAUSE_NO_USER_RESPONSE;
|
||||||
|
case 400:
|
||||||
|
case 481:
|
||||||
|
case 500:
|
||||||
|
case 503:
|
||||||
|
return 41; //SWITCH_CAUSE_NORMAL_TEMPORARY_FAILURE;
|
||||||
|
case 486:
|
||||||
|
case 600:
|
||||||
|
return 17; //SWITCH_CAUSE_USER_BUSY;
|
||||||
|
case 484:
|
||||||
|
return 28; //SWITCH_CAUSE_INVALID_NUMBER_FORMAT;
|
||||||
|
case 488:
|
||||||
|
case 606:
|
||||||
|
return 88; //SWITCH_CAUSE_INCOMPATIBLE_DESTINATION;
|
||||||
|
case 502:
|
||||||
|
return 38; //SWITCH_CAUSE_NETWORK_OUT_OF_ORDER;
|
||||||
|
case 405:
|
||||||
|
return 63; //SWITCH_CAUSE_SERVICE_UNAVAILABLE;
|
||||||
|
case 406:
|
||||||
|
case 415:
|
||||||
|
case 501:
|
||||||
|
return 79; //SWITCH_CAUSE_SERVICE_NOT_IMPLEMENTED;
|
||||||
|
case 482:
|
||||||
|
case 483:
|
||||||
|
return 25; //SWITCH_CAUSE_EXCHANGE_ROUTING_ERROR;
|
||||||
|
case 487:
|
||||||
|
return 31; //??? SWITCH_CAUSE_ORIGINATOR_CANCEL;
|
||||||
|
default:
|
||||||
|
return 31; //SWITCH_CAUSE_NORMAL_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t isdn2status_cause(uint8_t cause, uint8_t location)
|
||||||
|
{
|
||||||
|
switch (cause) {
|
||||||
|
case 1:
|
||||||
|
return 404;
|
||||||
|
case 2:
|
||||||
|
return 404;
|
||||||
|
case 3:
|
||||||
|
return 404;
|
||||||
|
case 17:
|
||||||
|
return 486;
|
||||||
|
case 18:
|
||||||
|
return 408;
|
||||||
|
case 19:
|
||||||
|
return 480;
|
||||||
|
case 20:
|
||||||
|
return 480;
|
||||||
|
case 21:
|
||||||
|
if (location == OSMO_CC_LOCATION_USER)
|
||||||
|
return 603;
|
||||||
|
return 403;
|
||||||
|
case 22:
|
||||||
|
//return 301;
|
||||||
|
return 410;
|
||||||
|
case 23:
|
||||||
|
return 410;
|
||||||
|
case 26:
|
||||||
|
return 404;
|
||||||
|
case 27:
|
||||||
|
return 502;
|
||||||
|
case 28:
|
||||||
|
return 484;
|
||||||
|
case 29:
|
||||||
|
return 501;
|
||||||
|
case 31:
|
||||||
|
return 480;
|
||||||
|
case 34:
|
||||||
|
return 503;
|
||||||
|
case 38:
|
||||||
|
return 503;
|
||||||
|
case 41:
|
||||||
|
return 503;
|
||||||
|
case 42:
|
||||||
|
return 503;
|
||||||
|
case 47:
|
||||||
|
return 503;
|
||||||
|
case 55:
|
||||||
|
return 403;
|
||||||
|
case 57:
|
||||||
|
return 403;
|
||||||
|
case 58:
|
||||||
|
return 503;
|
||||||
|
case 65:
|
||||||
|
return 488;
|
||||||
|
case 69:
|
||||||
|
return 501;
|
||||||
|
case 70:
|
||||||
|
return 488;
|
||||||
|
case 79:
|
||||||
|
return 501;
|
||||||
|
case 87:
|
||||||
|
return 403;
|
||||||
|
case 88:
|
||||||
|
return 503;
|
||||||
|
case 102:
|
||||||
|
return 504;
|
||||||
|
case 111:
|
||||||
|
return 500;
|
||||||
|
case 127:
|
||||||
|
return 500;
|
||||||
|
default:
|
||||||
|
return 468;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t socket2isdn_cause(uint8_t sock)
|
||||||
|
{
|
||||||
|
switch (sock) {
|
||||||
|
case OSMO_CC_SOCKET_CAUSE_FAILED:
|
||||||
|
return 47;
|
||||||
|
case OSMO_CC_SOCKET_CAUSE_BROKEN_PIPE:
|
||||||
|
return 41;
|
||||||
|
case OSMO_CC_SOCKET_CAUSE_VERSION_MISMATCH:
|
||||||
|
return 38;
|
||||||
|
case OSMO_CC_SOCKET_CAUSE_TIMEOUT:
|
||||||
|
return 41;
|
||||||
|
default:
|
||||||
|
return 31;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void osmo_cc_convert_cause(struct osmo_cc_ie_cause *cause)
|
||||||
|
{
|
||||||
|
/* complete cause, from socket cause */
|
||||||
|
if (cause->socket_cause && cause->isdn_cause == 0 && ntohs(cause->sip_cause_networkorder) == 0)
|
||||||
|
cause->isdn_cause = socket2isdn_cause(cause->socket_cause);
|
||||||
|
|
||||||
|
/* convert ISDN cause to SIP cause */
|
||||||
|
if (cause->isdn_cause && ntohs(cause->sip_cause_networkorder) == 0) {
|
||||||
|
cause->sip_cause_networkorder = htons(isdn2status_cause(cause->isdn_cause, cause->location));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* convert SIP cause to ISDN cause */
|
||||||
|
if (ntohs(cause->sip_cause_networkorder) && cause->isdn_cause == 0) {
|
||||||
|
cause->isdn_cause = status2isdn_cause(ntohs(cause->sip_cause_networkorder));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no cause at all: use Normal Call Clearing */
|
||||||
|
if (cause->isdn_cause == 0 && ntohs(cause->sip_cause_networkorder) == 0) {
|
||||||
|
cause->isdn_cause = OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR;
|
||||||
|
cause->sip_cause_networkorder = htons(486);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void osmo_cc_convert_cause_msg(osmo_cc_msg_t *msg)
|
||||||
|
{
|
||||||
|
void *ie;
|
||||||
|
uint8_t type;
|
||||||
|
uint16_t length;
|
||||||
|
void *value;
|
||||||
|
|
||||||
|
/* search for (all) cause IE and convert the values, if needed */
|
||||||
|
ie = msg->data;
|
||||||
|
while ((value = osmo_cc_msg_sep_ie(msg, &ie, &type, &length))) {
|
||||||
|
if (type == OSMO_CC_IE_CAUSE && length >= sizeof(struct osmo_cc_ie_cause)) {
|
||||||
|
osmo_cc_convert_cause(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t osmo_cc_collect_cause(uint8_t old_cause, uint8_t new_cause)
|
||||||
|
{
|
||||||
|
/* first cause */
|
||||||
|
if (old_cause == 0)
|
||||||
|
return new_cause;
|
||||||
|
|
||||||
|
/* first prio: return 17 */
|
||||||
|
if (old_cause == OSMO_CC_ISDN_CAUSE_USER_BUSY
|
||||||
|
|| new_cause == OSMO_CC_ISDN_CAUSE_USER_BUSY)
|
||||||
|
return OSMO_CC_ISDN_CAUSE_USER_BUSY;
|
||||||
|
|
||||||
|
/* second prio: return 21 */
|
||||||
|
if (old_cause == OSMO_CC_ISDN_CAUSE_CALL_REJECTED
|
||||||
|
|| new_cause == OSMO_CC_ISDN_CAUSE_CALL_REJECTED)
|
||||||
|
return OSMO_CC_ISDN_CAUSE_CALL_REJECTED;
|
||||||
|
|
||||||
|
/* third prio: return other than 88 and 18 (what ever was first) */
|
||||||
|
if (old_cause != OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST
|
||||||
|
&& old_cause != OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND)
|
||||||
|
return old_cause;
|
||||||
|
if (new_cause != OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST
|
||||||
|
&& new_cause != OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND)
|
||||||
|
return new_cause;
|
||||||
|
|
||||||
|
/* fourth prio: return 88 */
|
||||||
|
if (old_cause == OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST
|
||||||
|
|| new_cause == OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST)
|
||||||
|
return OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST;
|
||||||
|
|
||||||
|
/* fith prio: return 18 */
|
||||||
|
return OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
void osmo_cc_convert_cause(struct osmo_cc_ie_cause *cause);
|
||||||
|
void osmo_cc_convert_cause_msg(osmo_cc_msg_t *msg);
|
||||||
|
uint8_t osmo_cc_collect_cause(uint8_t old_cause, uint8_t new_cause);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,128 @@
|
||||||
|
#ifndef OSMO_CC_ENDPOINT_H
|
||||||
|
#define OSMO_CC_ENDPOINT_H
|
||||||
|
|
||||||
|
#include "message.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include "cause.h"
|
||||||
|
|
||||||
|
/* special osmo-cc error codes */
|
||||||
|
#define OSMO_CC_RC_SEE_ERRNO -1
|
||||||
|
#define OSMO_CC_RC_VERSION_MISMATCH 1
|
||||||
|
|
||||||
|
#define OSMO_CC_ATTACH_TIMER 2
|
||||||
|
|
||||||
|
/* call control state */
|
||||||
|
enum osmo_cc_state {
|
||||||
|
OSMO_CC_STATE_IDLE = 0,
|
||||||
|
/* call states */
|
||||||
|
OSMO_CC_STATE_INIT_OUT, /* outgoing CC-SETUP-REQ sent */
|
||||||
|
OSMO_CC_STATE_INIT_IN, /* incoming CC-SETUP-IND received */
|
||||||
|
OSMO_CC_STATE_OVERLAP_OUT, /* received CC-SETUP-ACK-IND on outgoing call */
|
||||||
|
OSMO_CC_STATE_OVERLAP_IN, /* sent CC-SETUP-ACK-REQ on incoming call */
|
||||||
|
OSMO_CC_STATE_PROCEEDING_OUT, /* received CC-PROC-IND on outgoing call */
|
||||||
|
OSMO_CC_STATE_PROCEEDING_IN, /* sent CC-PROC-REQ on incoming call */
|
||||||
|
OSMO_CC_STATE_ALERTING_OUT, /* received CC-ALERT-IND on outgoing call */
|
||||||
|
OSMO_CC_STATE_ALERTING_IN, /* sent CC-ALERT-REQ on incoming call */
|
||||||
|
OSMO_CC_STATE_CONNECTING_OUT, /* received CC-SETUP-CNF on outgoing call */
|
||||||
|
OSMO_CC_STATE_CONNECTING_IN, /* sent CC-SETUP-RSP on incoming call */
|
||||||
|
OSMO_CC_STATE_ACTIVE, /* received or sent CC-SETUP-COMPL-* */
|
||||||
|
OSMO_CC_STATE_DISCONNECTING_OUT, /* sent CC-DISC-REQ */
|
||||||
|
OSMO_CC_STATE_DISCONNECTING_IN, /* received CC-DISC-IND */
|
||||||
|
OSMO_CC_STATE_DISC_COLLISION, /* received CC-DISC-IND after sending CC-DISC_REQ */
|
||||||
|
OSMO_CC_STATE_RELEASING_OUT, /* sent CC-REL-REQ */
|
||||||
|
/* attachment states */
|
||||||
|
OSMO_CC_STATE_ATTACH_SENT, /* outgoing CC-ATT-REQ sent to socket */
|
||||||
|
OSMO_CC_STATE_ATTACH_OUT, /* received CC-ATT-RSP on outgoing socket */
|
||||||
|
OSMO_CC_STATE_ATTACH_WAIT, /* wait for outgoing attachment after failure */
|
||||||
|
OSMO_CC_STATE_ATTACH_IN, /* incoming CC-ATT-REQ received from socket*/
|
||||||
|
};
|
||||||
|
|
||||||
|
/* sample type */
|
||||||
|
typedef int16_t osmo_cc_sample_t;
|
||||||
|
|
||||||
|
#define OSMO_CC_SAMPLE_MILLIWATT 23170 /* peak sine at -3 dB of full sample range */
|
||||||
|
#define OSMO_CC_SAMPLE_SPEECH 3672 /* peak speech at -16 dB of milliwatt */
|
||||||
|
#define OSMO_CC_SAMPLE_MIN -32768 /* lowest level */
|
||||||
|
#define OSMO_CC_SAMPLE_MAX 32767 /* highest level */
|
||||||
|
|
||||||
|
struct osmo_cc_call;
|
||||||
|
|
||||||
|
typedef struct osmo_cc_screen_list {
|
||||||
|
struct osmo_cc_screen_list *next;
|
||||||
|
int has_from_type;
|
||||||
|
uint8_t from_type;
|
||||||
|
int has_from_present;
|
||||||
|
uint8_t from_present;
|
||||||
|
char from[128];
|
||||||
|
int has_to_type;
|
||||||
|
uint8_t to_type;
|
||||||
|
int has_to_present;
|
||||||
|
uint8_t to_present;
|
||||||
|
char to[128];
|
||||||
|
} osmo_cc_screen_list_t;
|
||||||
|
|
||||||
|
/* endpoint instance */
|
||||||
|
typedef struct osmo_cc_endpoint {
|
||||||
|
struct osmo_cc_endpoint *next;
|
||||||
|
void *priv;
|
||||||
|
void (*ll_msg_cb)(struct osmo_cc_endpoint *ep, uint32_t callref, osmo_cc_msg_t *msg);
|
||||||
|
void (*ul_msg_cb)(struct osmo_cc_call *call, osmo_cc_msg_t *msg);
|
||||||
|
osmo_cc_msg_list_t *ll_queue; /* messages towards lower layer */
|
||||||
|
struct osmo_cc_call *call_list;
|
||||||
|
const char *local_name; /* name of interface */
|
||||||
|
const char *local_address; /* host+port */
|
||||||
|
const char *local_host;
|
||||||
|
uint16_t local_port;
|
||||||
|
const char *remote_address; /* host+port */
|
||||||
|
const char *remote_host;
|
||||||
|
uint16_t remote_port;
|
||||||
|
uint8_t serving_location;
|
||||||
|
osmo_cc_socket_t os;
|
||||||
|
osmo_cc_screen_list_t *screen_calling_in;
|
||||||
|
osmo_cc_screen_list_t *screen_called_in;
|
||||||
|
osmo_cc_screen_list_t *screen_calling_out;
|
||||||
|
osmo_cc_screen_list_t *screen_called_out;
|
||||||
|
int remote_auto; /* automatic remote address */
|
||||||
|
struct timer attach_timer; /* timer to retry attachment */
|
||||||
|
} osmo_cc_endpoint_t;
|
||||||
|
|
||||||
|
extern osmo_cc_endpoint_t *osmo_cc_endpoint_list;
|
||||||
|
|
||||||
|
/* call process */
|
||||||
|
typedef struct osmo_cc_call {
|
||||||
|
struct osmo_cc_call *next;
|
||||||
|
osmo_cc_endpoint_t *ep;
|
||||||
|
enum osmo_cc_state state;
|
||||||
|
int lower_layer_released; /* when lower layer sent release, while upper layer gets a disconnect */
|
||||||
|
int upper_layer_released; /* when upper layer sent release, while lower layer gets a disconnect */
|
||||||
|
uint32_t callref;
|
||||||
|
osmo_cc_msg_list_t *sock_queue; /* messages from socket */
|
||||||
|
const char *attached_host; /* host and port from remote peer that attached to us */
|
||||||
|
uint16_t attached_port;
|
||||||
|
const char *attached_name; /* interface name from remote peer that attached to us */
|
||||||
|
} osmo_cc_call_t;
|
||||||
|
|
||||||
|
/* returns 0 if ok
|
||||||
|
* returns <0 for error as indicated
|
||||||
|
* returns >=1 to indicate osmo-cc error code
|
||||||
|
*/
|
||||||
|
|
||||||
|
void osmo_cc_help(void);
|
||||||
|
int osmo_cc_new(osmo_cc_endpoint_t *ep, const char *version, const char *name, uint8_t serving_location, void (*ll_msg_cb)(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg), void (*ul_msg_cb)(osmo_cc_call_t *call, osmo_cc_msg_t *msg), void *priv, int argc, const char *argv[]);
|
||||||
|
void osmo_cc_delete(struct osmo_cc_endpoint *ep);
|
||||||
|
int osmo_cc_handle(void);
|
||||||
|
osmo_cc_call_t *osmo_cc_call_by_callref(osmo_cc_endpoint_t *ep, uint32_t callref);
|
||||||
|
void osmo_cc_ll_msg(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg);
|
||||||
|
void osmo_cc_ul_msg(void *priv, uint32_t callref, osmo_cc_msg_t *msg);
|
||||||
|
osmo_cc_call_t *osmo_cc_call_new(osmo_cc_endpoint_t *ep);
|
||||||
|
void osmo_cc_call_delete(struct osmo_cc_call *call);
|
||||||
|
enum osmo_cc_session_addrtype osmo_cc_address_type(const char *address);
|
||||||
|
const char *osmo_cc_host_of_address(const char *address);
|
||||||
|
const char *osmo_cc_port_of_address(const char *address);
|
||||||
|
|
||||||
|
#include "session.h"
|
||||||
|
#include "rtp.h"
|
||||||
|
#include "sdp.h"
|
||||||
|
#include "screen.h"
|
||||||
|
|
||||||
|
#endif /* OSMO_CC_ENDPOINT_H */
|
|
@ -0,0 +1,171 @@
|
||||||
|
/* Osmo-CC: helpers to simplify Osmo-CC usage
|
||||||
|
*
|
||||||
|
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include "../libtimer/timer.h"
|
||||||
|
#include "../libdebug/debug.h"
|
||||||
|
#include "endpoint.h"
|
||||||
|
#include "helper.h"
|
||||||
|
|
||||||
|
osmo_cc_session_t *osmo_cc_helper_audio_offer(void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug)
|
||||||
|
{
|
||||||
|
osmo_cc_session_t *session;
|
||||||
|
osmo_cc_session_media_t *media;
|
||||||
|
const char *sdp;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
session = osmo_cc_new_session(priv, NULL, NULL, NULL, 0, 0, NULL, NULL, debug);
|
||||||
|
if (!session)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
media = osmo_cc_add_media(session, 0, 0, NULL, osmo_cc_session_media_type_audio, 0, osmo_cc_session_media_proto_rtp, 1, 1, receiver, debug);
|
||||||
|
osmo_cc_rtp_open(media);
|
||||||
|
|
||||||
|
for (i = 0; codecs[i].payload_name; i++)
|
||||||
|
osmo_cc_add_codec(media, codecs[i].payload_name, codecs[i].payload_rate, codecs[i].payload_channels, codecs[i].encoder, codecs[i].decoder, debug);
|
||||||
|
|
||||||
|
sdp = osmo_cc_session_send_offer(session);
|
||||||
|
osmo_cc_add_ie_sdp(msg, sdp);
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *osmo_cc_helper_audio_accept(void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec)
|
||||||
|
{
|
||||||
|
char offer_sdp[65536];
|
||||||
|
const char *accept_sdp;
|
||||||
|
osmo_cc_session_media_t *media, *selected_media = NULL;
|
||||||
|
osmo_cc_session_codec_t *codec, *selected_codec = NULL;
|
||||||
|
int rc;
|
||||||
|
int i, selected_i;
|
||||||
|
|
||||||
|
if (*session_p) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Session already set, please fix!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if (*codec_p) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Codec already set, please fix!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SDP IE */
|
||||||
|
rc = osmo_cc_get_ie_sdp(msg, 0, offer_sdp, sizeof(offer_sdp));
|
||||||
|
if (rc < 0) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "There is no SDP included in setup request.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*session_p = osmo_cc_session_receive_offer(priv, offer_sdp);
|
||||||
|
if (!*session_p) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Failed to parse SDP.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
selected_i = -1;
|
||||||
|
osmo_cc_session_for_each_media((*session_p)->media_list, media) {
|
||||||
|
/* only audio */
|
||||||
|
if (media->description.type != osmo_cc_session_media_type_audio)
|
||||||
|
continue;
|
||||||
|
osmo_cc_session_for_each_codec(media->codec_list, codec) {
|
||||||
|
for (i = 0; codecs[i].payload_name; i++) {
|
||||||
|
if (osmo_cc_session_if_codec(codec, codecs[i].payload_name, codecs[i].payload_rate, codecs[i].payload_channels)) {
|
||||||
|
/* select the first matchting codec or the one we prefer */
|
||||||
|
if (selected_i < 0 || i < selected_i) {
|
||||||
|
selected_codec = codec;
|
||||||
|
selected_media = media;
|
||||||
|
selected_i = i;
|
||||||
|
}
|
||||||
|
/* if we don't force our preferred codec, use the preferred one from the remote */
|
||||||
|
if (!force_our_codec)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!selected_codec) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "No codec found in setup message that we support.\n");
|
||||||
|
osmo_cc_free_session(*session_p);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
osmo_cc_session_accept_codec(selected_codec, codecs[selected_i].encoder, codecs[selected_i].decoder);
|
||||||
|
osmo_cc_session_accept_media(selected_media, 0, 0, NULL, 1, 1, receiver);
|
||||||
|
osmo_cc_rtp_open(selected_media);
|
||||||
|
osmo_cc_rtp_connect(selected_media);
|
||||||
|
*codec_p = selected_codec;
|
||||||
|
|
||||||
|
accept_sdp = osmo_cc_session_send_answer(*session_p);
|
||||||
|
if (!accept_sdp) {
|
||||||
|
osmo_cc_free_session(*session_p);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return accept_sdp;
|
||||||
|
}
|
||||||
|
|
||||||
|
int osmo_cc_helper_audio_negotiate(osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p)
|
||||||
|
{
|
||||||
|
char sdp[65536];
|
||||||
|
osmo_cc_session_media_t *media;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (!(*session_p)) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Session not set, please fix!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* once done, just ignore further messages that reply to setup */
|
||||||
|
if (*codec_p)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* SDP IE */
|
||||||
|
rc = osmo_cc_get_ie_sdp(msg, 0, sdp, sizeof(sdp));
|
||||||
|
if (rc < 0)
|
||||||
|
return 0; // no reply in this message
|
||||||
|
|
||||||
|
rc = osmo_cc_session_receive_answer(*session_p, sdp);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
osmo_cc_session_for_each_media((*session_p)->media_list, media) {
|
||||||
|
/* only audio */
|
||||||
|
if (media->description.type != osmo_cc_session_media_type_audio)
|
||||||
|
continue;
|
||||||
|
/* select first codec, if one was accpeted */
|
||||||
|
if (media->codec_list)
|
||||||
|
*codec_p = media->codec_list;
|
||||||
|
if (*codec_p) {
|
||||||
|
osmo_cc_rtp_connect(media);
|
||||||
|
/* no more media streams */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!(*codec_p)) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "No codec found in setup reply message that we support.\n");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
struct osmo_cc_helper_audio_codecs {
|
||||||
|
const char *payload_name;
|
||||||
|
uint32_t payload_rate;
|
||||||
|
int payload_channels;
|
||||||
|
void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
};
|
||||||
|
|
||||||
|
osmo_cc_session_t *osmo_cc_helper_audio_offer(void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug);
|
||||||
|
const char *osmo_cc_helper_audio_accept(void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec);
|
||||||
|
int osmo_cc_helper_audio_negotiate(osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p);
|
||||||
|
|
|
@ -0,0 +1,879 @@
|
||||||
|
/* Osmo-CC: Message handling
|
||||||
|
*
|
||||||
|
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include "../libdebug/debug.h"
|
||||||
|
#include "message.h"
|
||||||
|
|
||||||
|
static uint32_t new_callref = 0;
|
||||||
|
|
||||||
|
uint32_t osmo_cc_new_callref(void)
|
||||||
|
{
|
||||||
|
return (++new_callref);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *osmo_cc_msg_name(uint8_t msg_type)
|
||||||
|
{
|
||||||
|
switch (msg_type) {
|
||||||
|
case OSMO_CC_MSG_SETUP_REQ:
|
||||||
|
return "CC-SETUP-REQ";
|
||||||
|
case OSMO_CC_MSG_SETUP_IND:
|
||||||
|
return "CC-SETUP-IND";
|
||||||
|
case OSMO_CC_MSG_REJ_REQ:
|
||||||
|
return "CC-REJ-REQ";
|
||||||
|
case OSMO_CC_MSG_REJ_IND:
|
||||||
|
return "CC-REJ-IND";
|
||||||
|
case OSMO_CC_MSG_SETUP_ACK_REQ:
|
||||||
|
return "CC-SETUP-ACK-REQ";
|
||||||
|
case OSMO_CC_MSG_SETUP_ACK_IND:
|
||||||
|
return "CC-SETUP-ACK-IND";
|
||||||
|
case OSMO_CC_MSG_PROC_REQ:
|
||||||
|
return "CC-PROC-REQ";
|
||||||
|
case OSMO_CC_MSG_PROC_IND:
|
||||||
|
return "CC-PROC-IND";
|
||||||
|
case OSMO_CC_MSG_ALERT_REQ:
|
||||||
|
return "CC-ALERT-REQ";
|
||||||
|
case OSMO_CC_MSG_ALERT_IND:
|
||||||
|
return "CC-ALERT-IND";
|
||||||
|
case OSMO_CC_MSG_SETUP_RSP:
|
||||||
|
return "CC-SETUP-RSP";
|
||||||
|
case OSMO_CC_MSG_SETUP_CNF:
|
||||||
|
return "CC-SETUP-CNF";
|
||||||
|
case OSMO_CC_MSG_SETUP_COMP_REQ:
|
||||||
|
return "CC-SETUP-COMP-REQ";
|
||||||
|
case OSMO_CC_MSG_SETUP_COMP_IND:
|
||||||
|
return "CC-SETUP-COMP-IND";
|
||||||
|
case OSMO_CC_MSG_DISC_REQ:
|
||||||
|
return "CC-DISC-REQ";
|
||||||
|
case OSMO_CC_MSG_DISC_IND:
|
||||||
|
return "CC-DISC-IND";
|
||||||
|
case OSMO_CC_MSG_REL_REQ:
|
||||||
|
return "CC-REL-REQ";
|
||||||
|
case OSMO_CC_MSG_REL_CNF:
|
||||||
|
return "CC-REL-CNF";
|
||||||
|
case OSMO_CC_MSG_REL_IND:
|
||||||
|
return "CC-REL-IND";
|
||||||
|
case OSMO_CC_MSG_PROGRESS_REQ:
|
||||||
|
return "CC-PROGRESS-REQ";
|
||||||
|
case OSMO_CC_MSG_PROGRESS_IND:
|
||||||
|
return "CC-PROGRESS-IND";
|
||||||
|
case OSMO_CC_MSG_NOTIFY_REQ:
|
||||||
|
return "CC-NOTIFY-REQ";
|
||||||
|
case OSMO_CC_MSG_NOTIFY_IND:
|
||||||
|
return "CC-NOTIFY-IND";
|
||||||
|
case OSMO_CC_MSG_INFO_REQ:
|
||||||
|
return "CC-INFO-REQ";
|
||||||
|
case OSMO_CC_MSG_INFO_IND:
|
||||||
|
return "CC-INFO-IND";
|
||||||
|
case OSMO_CC_MSG_ATTACH_REQ:
|
||||||
|
return "CC-ATTACH-REQ";
|
||||||
|
case OSMO_CC_MSG_ATTACH_IND:
|
||||||
|
return "CC-ATTACH-IND";
|
||||||
|
case OSMO_CC_MSG_ATTACH_RSP:
|
||||||
|
return "CC-ATTACH-RSP";
|
||||||
|
case OSMO_CC_MSG_ATTACH_CNF:
|
||||||
|
return "CC-ATTACH-CNF";
|
||||||
|
default:
|
||||||
|
return "<unknown>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create message with maximum size */
|
||||||
|
osmo_cc_msg_t *osmo_cc_new_msg(uint8_t msg_type)
|
||||||
|
{
|
||||||
|
osmo_cc_msg_t *msg;
|
||||||
|
|
||||||
|
/* allocate message */
|
||||||
|
msg = calloc(1, sizeof(*msg) + 65535);
|
||||||
|
if (!msg) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "No memory\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
/* set message type and zero lentgh */
|
||||||
|
msg->type = msg_type;
|
||||||
|
msg->length_networkorder = htons(0);
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clone message */
|
||||||
|
osmo_cc_msg_t *osmo_cc_clone_msg(osmo_cc_msg_t *msg)
|
||||||
|
{
|
||||||
|
osmo_cc_msg_t *new_msg;
|
||||||
|
|
||||||
|
new_msg = osmo_cc_new_msg(msg->type);
|
||||||
|
new_msg->length_networkorder = msg->length_networkorder;
|
||||||
|
memcpy(new_msg->data, msg->data, ntohs(msg->length_networkorder));
|
||||||
|
|
||||||
|
return new_msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
osmo_cc_msg_t *osmo_cc_msg_list_dequeue(osmo_cc_msg_list_t **mlp, uint32_t *callref_p)
|
||||||
|
{
|
||||||
|
osmo_cc_msg_list_t *ml;
|
||||||
|
osmo_cc_msg_t *msg;
|
||||||
|
|
||||||
|
ml = *mlp;
|
||||||
|
msg = ml->msg;
|
||||||
|
if (callref_p)
|
||||||
|
*callref_p = ml->callref;
|
||||||
|
*mlp = ml->next;
|
||||||
|
free(ml);
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
osmo_cc_msg_list_t *osmo_cc_msg_list_enqueue(osmo_cc_msg_list_t **mlp, osmo_cc_msg_t *msg, uint32_t callref)
|
||||||
|
{
|
||||||
|
osmo_cc_msg_list_t *ml;
|
||||||
|
|
||||||
|
ml = calloc(1, sizeof(*ml));
|
||||||
|
ml->msg = msg;
|
||||||
|
ml->callref = callref;
|
||||||
|
while (*mlp)
|
||||||
|
mlp = &((*mlp)->next);
|
||||||
|
*mlp = ml;
|
||||||
|
|
||||||
|
return ml;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* destroy message */
|
||||||
|
void osmo_cc_free_msg(osmo_cc_msg_t *msg)
|
||||||
|
{
|
||||||
|
free(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void osmo_cc_debug_ie(osmo_cc_msg_t *msg, int level)
|
||||||
|
{
|
||||||
|
uint16_t msg_len, len;
|
||||||
|
uint8_t *p;
|
||||||
|
osmo_cc_ie_t *ie;
|
||||||
|
|
||||||
|
msg_len = ntohs(msg->length_networkorder);
|
||||||
|
p = msg->data;
|
||||||
|
|
||||||
|
PDEBUG(DCC, level, "Debugging Message: type=0x%02x length=%d value=%s\n", msg->type, msg_len, debug_hex(p, msg_len));
|
||||||
|
while (msg_len) {
|
||||||
|
ie = (osmo_cc_ie_t *)p;
|
||||||
|
/* check for minimum IE length */
|
||||||
|
if (msg_len < sizeof(*ie)) {
|
||||||
|
PDEBUG(DCC, level, "****** Rest of message is too short for an IE: value=%s\n", debug_hex(p, msg_len));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* get actual IE length */
|
||||||
|
len = ntohs(ie->length_networkorder);
|
||||||
|
/* check if IE length does not exceed message */
|
||||||
|
if (msg_len < sizeof(*ie) + len) {
|
||||||
|
PDEBUG(DCC, level, "****** IE: type=0x%02x length=%d would exceed the rest length of message (%d bytes left)\n", ie->type, len, msg_len - (int)sizeof(*ie));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PDEBUG(DCC, level, "IE: type=0x%02x length=%d value=%s\n", ie->type, len, debug_hex(ie->data, len));
|
||||||
|
p += sizeof(*ie) + len;
|
||||||
|
msg_len -= sizeof(*ie) + len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* search and return information element
|
||||||
|
* we give the IE type we are searching for
|
||||||
|
* we also give the repetition, to find IE that is repeated
|
||||||
|
* the result is stored in *ie_data
|
||||||
|
* the return length is the length that exceeds the given ie_len
|
||||||
|
* if there is an error, a value < 0 is returned
|
||||||
|
*/
|
||||||
|
int osmo_cc_get_ie_struct(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat, int ie_len, const osmo_cc_ie_t **ie_struct)
|
||||||
|
{
|
||||||
|
uint16_t msg_len, len;
|
||||||
|
uint8_t *p;
|
||||||
|
osmo_cc_ie_t *ie;
|
||||||
|
|
||||||
|
msg_len = ntohs(msg->length_networkorder);
|
||||||
|
p = msg->data;
|
||||||
|
|
||||||
|
while (msg_len) {
|
||||||
|
ie = (osmo_cc_ie_t *)p;
|
||||||
|
/* check for minimum IE length */
|
||||||
|
if (msg_len < sizeof(*ie)) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "MSG short read\n");
|
||||||
|
osmo_cc_debug_ie(msg, DEBUG_ERROR);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
/* get actual IE length */
|
||||||
|
len = ntohs(ie->length_networkorder);
|
||||||
|
/* check if IE length does not exceed message */
|
||||||
|
if (msg_len < sizeof(*ie) + len) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "MSG short read\n");
|
||||||
|
osmo_cc_debug_ie(msg, DEBUG_ERROR);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
/* check if IE matches the one that is searched for */
|
||||||
|
if (ie->type != ie_type) {
|
||||||
|
p += sizeof(*ie) + len;
|
||||||
|
msg_len -= sizeof(*ie) + len;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* check if IE repetition exists */
|
||||||
|
if (ie_repeat) {
|
||||||
|
--ie_repeat;
|
||||||
|
p += sizeof(*ie) + len;
|
||||||
|
msg_len -= sizeof(*ie) + len;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* return IE and indicate how many bytes we have more than the given length*/
|
||||||
|
if (ntohs(ie->length_networkorder) < ie_len) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "IE 0x%02d has length of %d, but we expect it to have at least %d!\n", ie_type, ntohs(ie->length_networkorder), ie_len);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
*ie_struct = ie;
|
||||||
|
return ntohs(ie->length_networkorder) - ie_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IE not found */
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* as above, but return data of IE only */
|
||||||
|
int osmo_cc_get_ie_data(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat, int ie_len, const void **ie_data)
|
||||||
|
{
|
||||||
|
const osmo_cc_ie_t *ie;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_struct(msg, ie_type, ie_repeat, ie_len, &ie);
|
||||||
|
if (rc >= 0)
|
||||||
|
*ie_data = ie->data;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* as above, but return 1 if IE exists */
|
||||||
|
int osmo_cc_has_ie(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat)
|
||||||
|
{
|
||||||
|
const osmo_cc_ie_t *ie;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_struct(msg, ie_type, ie_repeat, 0, &ie);
|
||||||
|
if (rc >= 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove IE from message */
|
||||||
|
int osmo_cc_remove_ie(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat)
|
||||||
|
{
|
||||||
|
const osmo_cc_ie_t *ie;
|
||||||
|
int rc;
|
||||||
|
int msg_len, before_ie, ie_size, after_ie;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_struct(msg, ie_type, ie_repeat, 0, &ie);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
msg_len = ntohs(msg->length_networkorder);
|
||||||
|
before_ie = (void *)ie - (void *)msg->data;
|
||||||
|
ie_size = sizeof(*ie) + ntohs(ie->length_networkorder);
|
||||||
|
after_ie = msg_len - ie_size - before_ie;
|
||||||
|
if (after_ie)
|
||||||
|
memcpy(msg->data + before_ie, msg->data + before_ie + ie_size, after_ie);
|
||||||
|
msg->length_networkorder = htons(msg_len - ie_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add information element
|
||||||
|
* the type is given by ie_type and length is given by ie_len
|
||||||
|
* the return value is a pointer to the data of the IE
|
||||||
|
*/
|
||||||
|
void *osmo_cc_add_ie(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_len)
|
||||||
|
{
|
||||||
|
uint16_t msg_len;
|
||||||
|
int new_msg_len;
|
||||||
|
uint8_t *p;
|
||||||
|
osmo_cc_ie_t *ie;
|
||||||
|
|
||||||
|
/* get pointer to first IE, if any */
|
||||||
|
p = msg->data;
|
||||||
|
/* expand messasge */
|
||||||
|
msg_len = ntohs(msg->length_networkorder);
|
||||||
|
new_msg_len = msg_len + sizeof(*ie) + ie_len;
|
||||||
|
if (new_msg_len > 65535) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "MSG overflow\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
msg->length_networkorder = htons(new_msg_len);
|
||||||
|
/* go to end of (unexpanded) message */
|
||||||
|
ie = (osmo_cc_ie_t *)(p + msg_len);
|
||||||
|
/* add ie */
|
||||||
|
ie->type = ie_type;
|
||||||
|
ie->length_networkorder = htons(ie_len);
|
||||||
|
memset(ie->data, 0, ie_len); /* just in case there is something, but it shouldn't */
|
||||||
|
|
||||||
|
return ie->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* gets the information element's data that *iep points to and returns that ie.
|
||||||
|
* if *iep points to msg->data, the first IE's data is returned. (must be set before first call.)
|
||||||
|
* if *iep points to the end of the message, NULL is returned.
|
||||||
|
* if there is no next IE, *iep is set to point to the end of message.
|
||||||
|
*/
|
||||||
|
void *osmo_cc_msg_sep_ie(osmo_cc_msg_t *msg, void **iep, uint8_t *ie_type, uint16_t *ie_length)
|
||||||
|
{
|
||||||
|
uint16_t msg_len;
|
||||||
|
osmo_cc_ie_t *ie;
|
||||||
|
|
||||||
|
/* in case that *iep points to start of message, make it point to first IE */
|
||||||
|
if (*iep == msg)
|
||||||
|
*iep = msg->data;
|
||||||
|
/* case IE */
|
||||||
|
ie = *iep;
|
||||||
|
/* check if it is NULL */
|
||||||
|
if (ie == NULL)
|
||||||
|
return NULL;
|
||||||
|
/* check if it points to the end of message or there is not at least an IE header */
|
||||||
|
msg_len = ntohs(msg->length_networkorder);
|
||||||
|
if ((int)((uint8_t *)ie - msg->data) > (int)(msg_len - sizeof(*ie)))
|
||||||
|
return NULL;
|
||||||
|
/* increment iep and return IE */
|
||||||
|
*ie_type = ie->type;
|
||||||
|
*ie_length = ntohs(ie->length_networkorder);
|
||||||
|
*iep = (uint8_t *)ie + sizeof(*ie) + *ie_length;
|
||||||
|
return ie->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy given block to given string with given size */
|
||||||
|
static void _ie2string(char *string, size_t string_size, const char *ie_string, int ie_size)
|
||||||
|
{
|
||||||
|
int copy_size;
|
||||||
|
|
||||||
|
copy_size = string_size - 1;
|
||||||
|
if (ie_size < copy_size)
|
||||||
|
copy_size = ie_size;
|
||||||
|
memcpy(string, ie_string, copy_size);
|
||||||
|
string[copy_size] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode called party number (dialing) */
|
||||||
|
void osmo_cc_add_ie_called(osmo_cc_msg_t *msg, uint8_t type, uint8_t plan, const char *dialing)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_called *ie_called;
|
||||||
|
|
||||||
|
ie_called = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLED, sizeof(*ie_called) + strlen(dialing));
|
||||||
|
ie_called->type = type;
|
||||||
|
ie_called->plan = plan;
|
||||||
|
memcpy(ie_called->digits, dialing, strlen(dialing));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode called party number (dialing) */
|
||||||
|
int osmo_cc_get_ie_called(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, uint8_t *plan, char *dialing, size_t dialing_size)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_called *ie_called;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLED, ie_repeat, sizeof(*ie_called), (const void **)&ie_called);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
*type = ie_called->type;
|
||||||
|
*plan = ie_called->plan;
|
||||||
|
_ie2string(dialing, dialing_size, ie_called->digits, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode called party sub address (dialing) */
|
||||||
|
void osmo_cc_add_ie_called_sub(osmo_cc_msg_t *msg, uint8_t type, const char *dialing)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_called_sub *ie_called_sub;
|
||||||
|
|
||||||
|
ie_called_sub = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLED_SUB, sizeof(*ie_called_sub) + strlen(dialing));
|
||||||
|
ie_called_sub->type = type;
|
||||||
|
memcpy(ie_called_sub->digits, dialing, strlen(dialing));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode called party sub address (dialing) */
|
||||||
|
int osmo_cc_get_ie_called_sub(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, char *dialing, size_t dialing_size)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_called_sub *ie_called_sub;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLED_SUB, ie_repeat, sizeof(*ie_called_sub), (const void **)&ie_called_sub);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
*type = ie_called_sub->type;
|
||||||
|
_ie2string(dialing, dialing_size, ie_called_sub->digits, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode called party name (dialing) */
|
||||||
|
void osmo_cc_add_ie_called_name(osmo_cc_msg_t *msg, const char *name)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_called_name *ie_called_name;
|
||||||
|
|
||||||
|
ie_called_name = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLED_NAME, sizeof(*ie_called_name) + strlen(name));
|
||||||
|
memcpy(ie_called_name->name, name, strlen(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode called party name (dialing) */
|
||||||
|
int osmo_cc_get_ie_called_name(osmo_cc_msg_t *msg, int ie_repeat, char *name, size_t name_size)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_called_name *ie_called_name;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLED_NAME, ie_repeat, sizeof(*ie_called_name), (const void **)&ie_called_name);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
_ie2string(name, name_size, ie_called_name->name, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode called interface name */
|
||||||
|
void osmo_cc_add_ie_called_interface(osmo_cc_msg_t *msg, const char *interface)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_called_interface *ie_interface;
|
||||||
|
|
||||||
|
ie_interface = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLED_INTERFACE, sizeof(*ie_interface) + strlen(interface));
|
||||||
|
memcpy(ie_interface->name, interface, strlen(interface));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode called interface name */
|
||||||
|
int osmo_cc_get_ie_called_interface(osmo_cc_msg_t *msg, int ie_repeat, char *interface, size_t interface_size)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_called_interface *ie_interface;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLED_INTERFACE, ie_repeat, sizeof(*ie_interface), (const void **)&ie_interface);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
_ie2string(interface, interface_size, ie_interface->name, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode complete IE */
|
||||||
|
void osmo_cc_add_ie_complete(osmo_cc_msg_t *msg)
|
||||||
|
{
|
||||||
|
osmo_cc_add_ie(msg, OSMO_CC_IE_COMPLETE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode complete IE */
|
||||||
|
int osmo_cc_get_ie_complete(osmo_cc_msg_t *msg, int ie_repeat)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
void *ie_complete;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_COMPLETE, ie_repeat, 0, (const void **)&ie_complete);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode calling/connected party number (caller ID or connected ID) */
|
||||||
|
void osmo_cc_add_ie_calling(osmo_cc_msg_t *msg, uint8_t type, uint8_t plan, uint8_t present, uint8_t screen, const char *callerid)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_calling *ie_calling;
|
||||||
|
|
||||||
|
ie_calling = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLING, sizeof(*ie_calling) + strlen(callerid));
|
||||||
|
ie_calling->type = type;
|
||||||
|
ie_calling->plan = plan;
|
||||||
|
ie_calling->present = present;
|
||||||
|
ie_calling->screen = screen;
|
||||||
|
memcpy(ie_calling->digits, callerid, strlen(callerid));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode calling/connected party number (caller ID or connected ID) */
|
||||||
|
int osmo_cc_get_ie_calling(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, uint8_t *plan, uint8_t *present, uint8_t *screen, char *callerid, size_t callerid_size)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_calling *ie_calling;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLING, ie_repeat, sizeof(*ie_calling), (const void **)&ie_calling);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
*type = ie_calling->type;
|
||||||
|
*plan = ie_calling->plan;
|
||||||
|
*present = ie_calling->present;
|
||||||
|
*screen = ie_calling->screen;
|
||||||
|
_ie2string(callerid, callerid_size, ie_calling->digits, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode calling/connected sub address (caller ID or connected ID) */
|
||||||
|
void osmo_cc_add_ie_calling_sub(osmo_cc_msg_t *msg, uint8_t type, const char *callerid)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_calling_sub *ie_calling_sub;
|
||||||
|
|
||||||
|
ie_calling_sub = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLING_SUB, sizeof(*ie_calling_sub) + strlen(callerid));
|
||||||
|
ie_calling_sub->type = type;
|
||||||
|
memcpy(ie_calling_sub->digits, callerid, strlen(callerid));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode calling/connected sub address (caller ID or connected ID) */
|
||||||
|
int osmo_cc_get_ie_calling_sub(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, char *callerid, size_t callerid_size)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_calling_sub *ie_calling_sub;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLING_SUB, ie_repeat, sizeof(*ie_calling_sub), (const void **)&ie_calling_sub);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
*type = ie_calling_sub->type;
|
||||||
|
_ie2string(callerid, callerid_size, ie_calling_sub->digits, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode calling/connected name (caller ID or connected ID) */
|
||||||
|
void osmo_cc_add_ie_calling_name(osmo_cc_msg_t *msg, const char *name)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_calling_name *ie_calling_name;
|
||||||
|
|
||||||
|
ie_calling_name = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLING_NAME, sizeof(*ie_calling_name) + strlen(name));
|
||||||
|
memcpy(ie_calling_name->name, name, strlen(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode calling/connected name address (caller ID or connected ID) */
|
||||||
|
int osmo_cc_get_ie_calling_name(osmo_cc_msg_t *msg, int ie_repeat, char *name, size_t name_size)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_calling_name *ie_calling_name;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLING_NAME, ie_repeat, sizeof(*ie_calling_name), (const void **)&ie_calling_name);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
_ie2string(name, name_size, ie_calling_name->name, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode calling interface name */
|
||||||
|
void osmo_cc_add_ie_calling_interface(osmo_cc_msg_t *msg, const char *interface)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_calling_interface *ie_interface;
|
||||||
|
|
||||||
|
ie_interface = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLING_INTERFACE, sizeof(*ie_interface) + strlen(interface));
|
||||||
|
memcpy(ie_interface->name, interface, strlen(interface));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode calling interface name */
|
||||||
|
int osmo_cc_get_ie_calling_interface(osmo_cc_msg_t *msg, int ie_repeat, char *interface, size_t interface_size)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_calling_interface *ie_interface;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLING_INTERFACE, ie_repeat, sizeof(*ie_interface), (const void **)&ie_interface);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
_ie2string(interface, interface_size, ie_interface->name, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode network specific caller/connected ID */
|
||||||
|
void osmo_cc_add_ie_calling_network(osmo_cc_msg_t *msg, uint8_t type, const char *networkid)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_network *ie_network;
|
||||||
|
|
||||||
|
ie_network = osmo_cc_add_ie(msg, OSMO_CC_IE_CALLING_NETWORK, sizeof(*ie_network) + strlen(networkid));
|
||||||
|
ie_network->type = type;
|
||||||
|
memcpy(ie_network->id, networkid, strlen(networkid));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode network specific caller/connected ID */
|
||||||
|
int osmo_cc_get_ie_calling_network(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, char *networkid, size_t networkid_size)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_network *ie_network;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CALLING_NETWORK, ie_repeat, sizeof(*ie_network), (const void **)&ie_network);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
*type = ie_network->type;
|
||||||
|
_ie2string(networkid, networkid_size, ie_network->id, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode bearer capability */
|
||||||
|
void osmo_cc_add_ie_bearer(osmo_cc_msg_t *msg, uint8_t coding, uint8_t capability, uint8_t mode)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_bearer *ie_bearer;
|
||||||
|
|
||||||
|
ie_bearer = osmo_cc_add_ie(msg, OSMO_CC_IE_BEARER, sizeof(*ie_bearer));
|
||||||
|
ie_bearer->coding = coding;
|
||||||
|
ie_bearer->capability = capability;
|
||||||
|
ie_bearer->mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode bearer capability */
|
||||||
|
int osmo_cc_get_ie_bearer(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *coding, uint8_t *capability, uint8_t *mode)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_bearer *ie_bearer;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_BEARER, ie_repeat, sizeof(*ie_bearer), (const void **)&ie_bearer);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
*coding = ie_bearer->coding;
|
||||||
|
*capability = ie_bearer->capability;
|
||||||
|
*mode = ie_bearer->mode;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode redirection and redirecting number */
|
||||||
|
void osmo_cc_add_ie_redir(osmo_cc_msg_t *msg, uint8_t type, uint8_t plan, uint8_t present, uint8_t screen, uint8_t redir_reason, const char *callerid)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_redir *ie_redir;
|
||||||
|
|
||||||
|
ie_redir = osmo_cc_add_ie(msg, OSMO_CC_IE_REDIR, sizeof(*ie_redir) + strlen(callerid));
|
||||||
|
ie_redir->type = type;
|
||||||
|
ie_redir->plan = plan;
|
||||||
|
ie_redir->present = present;
|
||||||
|
ie_redir->screen = screen;
|
||||||
|
ie_redir->redir_reason = redir_reason;
|
||||||
|
memcpy(ie_redir->digits, callerid, strlen(callerid));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode redirection and redirecting number */
|
||||||
|
int osmo_cc_get_ie_redir(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, uint8_t *plan, uint8_t *present, uint8_t *screen, uint8_t *reason, char *callerid, size_t callerid_size)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_redir *ie_redir;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_REDIR, ie_repeat, sizeof(*ie_redir), (const void **)&ie_redir);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
*type = ie_redir->type;
|
||||||
|
*plan = ie_redir->plan;
|
||||||
|
*present = ie_redir->present;
|
||||||
|
*screen = ie_redir->screen;
|
||||||
|
*reason = ie_redir->redir_reason;
|
||||||
|
_ie2string(callerid, callerid_size, ie_redir->digits, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode DTMF tones */
|
||||||
|
void osmo_cc_add_ie_dtmf(osmo_cc_msg_t *msg, uint8_t duration_ms, uint8_t pause_ms, uint8_t dtmf_mode, const char *digits)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_dtmf *ie_dtmf;
|
||||||
|
|
||||||
|
ie_dtmf = osmo_cc_add_ie(msg, OSMO_CC_IE_DTMF, sizeof(*ie_dtmf) + strlen(digits));
|
||||||
|
ie_dtmf->duration_ms = duration_ms;
|
||||||
|
ie_dtmf->pause_ms = pause_ms;
|
||||||
|
ie_dtmf->dtmf_mode = dtmf_mode;
|
||||||
|
memcpy(ie_dtmf->digits, digits, strlen(digits));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode DTMF tones */
|
||||||
|
int osmo_cc_get_ie_dtmf(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *duration_ms, uint8_t *pause_ms, uint8_t *dtmf_mode, char *digits, size_t digits_size)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_dtmf *ie_dtmf;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_DTMF, ie_repeat, sizeof(*ie_dtmf), (const void **)&ie_dtmf);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
*duration_ms = ie_dtmf->duration_ms;
|
||||||
|
*pause_ms = ie_dtmf->pause_ms;
|
||||||
|
*dtmf_mode = ie_dtmf->dtmf_mode;
|
||||||
|
_ie2string(digits, digits_size, ie_dtmf->digits, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode keypad press */
|
||||||
|
void osmo_cc_add_ie_keypad(osmo_cc_msg_t *msg, const char *digits)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_keypad *ie_keypad;
|
||||||
|
|
||||||
|
ie_keypad = osmo_cc_add_ie(msg, OSMO_CC_IE_KEYPAD, sizeof(*ie_keypad) + strlen(digits));
|
||||||
|
memcpy(ie_keypad->digits, digits, strlen(digits));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode keypad press */
|
||||||
|
int osmo_cc_get_ie_keypad(osmo_cc_msg_t *msg, int ie_repeat, char *digits, size_t digits_size)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_keypad *ie_keypad;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_KEYPAD, ie_repeat, sizeof(*ie_keypad), (const void **)&ie_keypad);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
_ie2string(digits, digits_size, ie_keypad->digits, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode call progress information */
|
||||||
|
void osmo_cc_add_ie_progress(osmo_cc_msg_t *msg, uint8_t coding, uint8_t location, uint8_t progress)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_progress *ie_progress;
|
||||||
|
|
||||||
|
ie_progress = osmo_cc_add_ie(msg, OSMO_CC_IE_PROGRESS, sizeof(*ie_progress));
|
||||||
|
ie_progress->coding = coding;
|
||||||
|
ie_progress->location = location;
|
||||||
|
ie_progress->progress = progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode call progress information */
|
||||||
|
int osmo_cc_get_ie_progress(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *coding, uint8_t *location, uint8_t *progress)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_progress *ie_progress;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_PROGRESS, ie_repeat, sizeof(*ie_progress), (const void **)&ie_progress);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
*coding = ie_progress->coding;
|
||||||
|
*location = ie_progress->location;
|
||||||
|
*progress = ie_progress->progress;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode notification */
|
||||||
|
void osmo_cc_add_ie_notify(osmo_cc_msg_t *msg, uint8_t notify)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_notify *ie_notify;
|
||||||
|
|
||||||
|
ie_notify = osmo_cc_add_ie(msg, OSMO_CC_IE_NOTIFY, sizeof(*ie_notify));
|
||||||
|
ie_notify->notify = notify;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode notification */
|
||||||
|
int osmo_cc_get_ie_notify(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *notify)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_notify *ie_notify;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_NOTIFY, ie_repeat, sizeof(*ie_notify), (const void **)&ie_notify);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
*notify = ie_notify->notify;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode cause */
|
||||||
|
void osmo_cc_add_ie_cause(osmo_cc_msg_t *msg, uint8_t location, uint8_t isdn_cause, uint16_t sip_cause, uint8_t socket_cause)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_cause *ie_cause;
|
||||||
|
|
||||||
|
ie_cause = osmo_cc_add_ie(msg, OSMO_CC_IE_CAUSE, sizeof(*ie_cause));
|
||||||
|
ie_cause->location = location;
|
||||||
|
ie_cause->isdn_cause = isdn_cause;
|
||||||
|
ie_cause->sip_cause_networkorder = htons(sip_cause);
|
||||||
|
ie_cause->socket_cause = socket_cause;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to deccode cause */
|
||||||
|
int osmo_cc_get_ie_cause(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *location, uint8_t *isdn_cause, uint16_t *sip_cause, uint8_t *socket_cause)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_cause *ie_cause;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_CAUSE, ie_repeat, sizeof(*ie_cause), (const void **)&ie_cause);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
*location = ie_cause->location;
|
||||||
|
*isdn_cause = ie_cause->isdn_cause;
|
||||||
|
*sip_cause = ntohs(ie_cause->sip_cause_networkorder);
|
||||||
|
*socket_cause = ie_cause->socket_cause;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode DISPLAY information */
|
||||||
|
void osmo_cc_add_ie_display(osmo_cc_msg_t *msg, const char *text)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_display *ie_display;
|
||||||
|
|
||||||
|
ie_display = osmo_cc_add_ie(msg, OSMO_CC_IE_DISPLAY, sizeof(*ie_display) + strlen(text));
|
||||||
|
memcpy(ie_display->text, text, strlen(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode DISPLAY information */
|
||||||
|
int osmo_cc_get_ie_display(osmo_cc_msg_t *msg, int ie_repeat, char *text, size_t text_size)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_display *ie_display;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_DISPLAY, ie_repeat, sizeof(*ie_display), (const void **)&ie_display);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
_ie2string(text, text_size, ie_display->text, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode SDP */
|
||||||
|
void osmo_cc_add_ie_sdp(osmo_cc_msg_t *msg, const char *sdp)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_sdp *ie_sdp;
|
||||||
|
|
||||||
|
ie_sdp = osmo_cc_add_ie(msg, OSMO_CC_IE_SDP, sizeof(*ie_sdp) + strlen(sdp));
|
||||||
|
memcpy(ie_sdp->sdp, sdp, strlen(sdp));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode SDP */
|
||||||
|
int osmo_cc_get_ie_sdp(osmo_cc_msg_t *msg, int ie_repeat, char *sdp, size_t sdp_size)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_sdp *ie_sdp;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_SDP, ie_repeat, sizeof(*ie_sdp), (const void **)&ie_sdp);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
_ie2string(sdp, sdp_size, ie_sdp->sdp, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode socket addresss */
|
||||||
|
void osmo_cc_add_ie_socket_address(osmo_cc_msg_t *msg, const char *address)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_socket_address *ie_socket_address;
|
||||||
|
|
||||||
|
ie_socket_address = osmo_cc_add_ie(msg, OSMO_CC_IE_SOCKET_ADDRESS, sizeof(*ie_socket_address) + strlen(address));
|
||||||
|
memcpy(ie_socket_address->address, address, strlen(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode socket addresss */
|
||||||
|
int osmo_cc_get_ie_socket_address(osmo_cc_msg_t *msg, int ie_repeat, char *address, size_t address_size)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_socket_address *ie_socket_address;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_SOCKET_ADDRESS, ie_repeat, sizeof(*ie_socket_address), (const void **)&ie_socket_address);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
_ie2string(address, address_size, ie_socket_address->address, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to encode private information element */
|
||||||
|
void osmo_cc_add_ie_private(osmo_cc_msg_t *msg, uint32_t unique, const uint8_t *data, size_t data_size)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_private *ie_private;
|
||||||
|
|
||||||
|
ie_private = osmo_cc_add_ie(msg, OSMO_CC_IE_PRIVATE, sizeof(*ie_private) + data_size);
|
||||||
|
ie_private->unique_networkorder = htonl(unique);
|
||||||
|
memcpy(ie_private->data, data, data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper to decode private information element */
|
||||||
|
int osmo_cc_get_ie_private(osmo_cc_msg_t *msg, int ie_repeat, uint32_t *unique, uint8_t *data, size_t data_size)
|
||||||
|
{
|
||||||
|
struct osmo_cc_ie_private *ie_private;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_PRIVATE, ie_repeat, sizeof(*ie_private), (const void **)&ie_private);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
*unique = ntohl(ie_private->unique_networkorder);
|
||||||
|
memcpy(data, ie_private->data, (rc < (int)data_size) ? rc : (int)data_size);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,437 @@
|
||||||
|
#ifndef OSMO_CC_MSG_H
|
||||||
|
#define OSMO_CC_MSG_H
|
||||||
|
|
||||||
|
#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_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_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,
|
||||||
|
|
||||||
|
/* 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
/* cause, see ITU-T Rec. Q.850 */
|
||||||
|
#define OSMO_CC_ISDN_CAUSE_UNASSIGNED_NR 1
|
||||||
|
#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
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
/* transfer mode, see ITU-T Rec. Q.931 */
|
||||||
|
#define OSMO_CC_MODE_CIRCUIT 0
|
||||||
|
#define OSMO_CC_MODE_PACKET 2
|
||||||
|
|
||||||
|
#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_SOCKET_CAUSE_VERSION_MISMATCH 1 /* version missmatch */
|
||||||
|
#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!
|
||||||
|
|
||||||
|
/* 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_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... */
|
||||||
|
|
||||||
|
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);
|
||||||
|
const char *osmo_cc_msg_name(uint8_t msg_type);
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
|
||||||
|
#endif /* OSMO_CC_MSG_H */
|
|
@ -0,0 +1,399 @@
|
||||||
|
/* 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 <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include "../libdebug/debug.h"
|
||||||
|
#include "../libtimer/timer.h"
|
||||||
|
#include "endpoint.h"
|
||||||
|
|
||||||
|
#define RTP_VERSION 2
|
||||||
|
|
||||||
|
static uint16_t rtp_port_next = 16384;
|
||||||
|
static uint16_t rtp_port_from = 16384;
|
||||||
|
static uint16_t rtp_port_to = 32767;
|
||||||
|
|
||||||
|
void osmo_cc_set_rtp_ports(uint16_t from, uint16_t to)
|
||||||
|
{
|
||||||
|
rtp_port_next = from;
|
||||||
|
rtp_port_from = from;
|
||||||
|
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(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)
|
||||||
|
{
|
||||||
|
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 = read(sock, data, sizeof(data));
|
||||||
|
if (len < 0) {
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
return -EAGAIN;
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, "Read errno = %d (%s)\n", errno, strerror(errno));
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
if (len < 12) {
|
||||||
|
PDEBUG(DCC, DEBUG_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);
|
||||||
|
|
||||||
|
if (version != RTP_VERSION) {
|
||||||
|
PDEBUG(DCC, DEBUG_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) {
|
||||||
|
PDEBUG(DCC, DEBUG_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)) {
|
||||||
|
PDEBUG(DCC, DEBUG_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)) {
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "Received RTP frame too short, extension header exceeds frame length.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (padding) {
|
||||||
|
if (payload_len < 1) {
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "Received RTP frame too short for padding length.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
payload_len -= payload[payload_len - 1];
|
||||||
|
if (payload_len < 0) {
|
||||||
|
PDEBUG(DCC, DEBUG_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 void rtp_send(int sock, uint8_t *payload, int payload_len, 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;
|
||||||
|
rtph->sequence = htons(sequence);
|
||||||
|
rtph->timestamp = htonl(timestamp);
|
||||||
|
rtph->ssrc = htonl(ssrc);
|
||||||
|
len += payload_len;
|
||||||
|
if (len > (int)sizeof(data)) {
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "Buffer overflow, please fix!.\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
memcpy(data + sizeof(*rtph), payload, payload_len);
|
||||||
|
|
||||||
|
rc = write(sock, data, len);
|
||||||
|
if (rc < 0)
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, "Write errno = %d (%s)\n", errno, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* open and bind RTP
|
||||||
|
* set local port to what we bound
|
||||||
|
*/
|
||||||
|
int osmo_cc_rtp_open(osmo_cc_session_media_t *media)
|
||||||
|
{
|
||||||
|
int domain = 0; // make GCC happy
|
||||||
|
uint16_t start_port;
|
||||||
|
struct sockaddr_storage sa;
|
||||||
|
int slen = 0; // make GCC happy
|
||||||
|
struct sockaddr_in6 *sa6;
|
||||||
|
struct sockaddr_in *sa4;
|
||||||
|
uint16_t *sport;
|
||||||
|
int flags;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
media->rtp_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;
|
||||||
|
sa4->sin_addr.s_addr = INADDR_ANY;
|
||||||
|
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;
|
||||||
|
sa6->sin6_addr = in6addr_any;
|
||||||
|
sport = &sa6->sin6_port;
|
||||||
|
slen = sizeof(*sa6);
|
||||||
|
break;
|
||||||
|
case osmo_cc_session_addrtype_unknown:
|
||||||
|
PDEBUG(DCC, DEBUG_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 (rtp_port_next < rtp_port_from || rtp_port_next > rtp_port_to)
|
||||||
|
rtp_port_next = rtp_port_from;
|
||||||
|
start_port = rtp_port_next;
|
||||||
|
while (1) {
|
||||||
|
/* open sockets */
|
||||||
|
rc = socket(domain, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
if (rc < 0) {
|
||||||
|
socket_error:
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Cannot create socket (domain=%d, errno=%d(%s))\n", domain, errno, strerror(errno));
|
||||||
|
osmo_cc_rtp_close(media);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
media->rtp_socket = rc;
|
||||||
|
rc = socket(domain, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
if (rc < 0)
|
||||||
|
goto socket_error;
|
||||||
|
media->rtcp_socket = rc;
|
||||||
|
|
||||||
|
/* bind sockets */
|
||||||
|
*sport = htons(rtp_port_next);
|
||||||
|
rc = bind(media->rtp_socket, (struct sockaddr *)&sa, slen);
|
||||||
|
if (rc < 0) {
|
||||||
|
bind_error:
|
||||||
|
osmo_cc_rtp_close(media);
|
||||||
|
rtp_port_next = (rtp_port_next + 2 > rtp_port_to) ? rtp_port_from : rtp_port_next + 2;
|
||||||
|
if (rtp_port_next == start_port) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Cannot bind socket (errno=%d(%s))\n", errno, strerror(errno));
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*sport = htons(rtp_port_next + 1);
|
||||||
|
rc = bind(media->rtcp_socket, (struct sockaddr *)&sa, slen);
|
||||||
|
if (rc < 0)
|
||||||
|
goto bind_error;
|
||||||
|
media->description.port_local = rtp_port_next;
|
||||||
|
rtp_port_next = (rtp_port_next + 2 > rtp_port_to) ? rtp_port_from : rtp_port_next + 2;
|
||||||
|
/* set nonblocking io */
|
||||||
|
flags = fcntl(media->rtp_socket, F_GETFL);
|
||||||
|
flags |= O_NONBLOCK;
|
||||||
|
fcntl(media->rtp_socket, F_SETFL, flags);
|
||||||
|
flags = fcntl(media->rtcp_socket, F_GETFL);
|
||||||
|
flags |= O_NONBLOCK;
|
||||||
|
fcntl(media->rtcp_socket, F_SETFL, flags);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_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_storage sa;
|
||||||
|
int slen = 0; // make GCC happy
|
||||||
|
struct sockaddr_in6 *sa6;
|
||||||
|
struct sockaddr_in *sa4;
|
||||||
|
uint16_t *sport;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, "Connecting media port %d->%d\n", media->description.port_local, media->description.port_remote);
|
||||||
|
|
||||||
|
switch (media->connection_data_remote.addrtype) {
|
||||||
|
case osmo_cc_session_addrtype_ipv4:
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa4 = (struct sockaddr_in *)&sa;
|
||||||
|
sa4->sin_family = AF_INET;
|
||||||
|
rc = inet_pton(AF_INET, media->connection_data_remote.address, &sa4->sin_addr);
|
||||||
|
if (rc < 1) {
|
||||||
|
pton_error:
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "Cannot connect to address '%s'.\n", media->connection_data_remote.address);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
sport = &sa4->sin_port;
|
||||||
|
slen = sizeof(*sa4);
|
||||||
|
break;
|
||||||
|
case osmo_cc_session_addrtype_ipv6:
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa6 = (struct sockaddr_in6 *)&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;
|
||||||
|
sport = &sa6->sin6_port;
|
||||||
|
slen = sizeof(*sa6);
|
||||||
|
break;
|
||||||
|
case osmo_cc_session_addrtype_unknown:
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "Unsupported address type '%s'.\n", media->connection_data_local.addrtype_name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*sport = htons(media->description.port_remote);
|
||||||
|
rc = connect(media->rtp_socket, (struct sockaddr *)&sa, slen);
|
||||||
|
if (rc < 0) {
|
||||||
|
connect_error:
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "Cannot connect to address '%s'.\n", media->connection_data_remote.address);
|
||||||
|
osmo_cc_rtp_close(media);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
*sport = htons(media->description.port_remote + 1);
|
||||||
|
rc = connect(media->rtcp_socket, (struct sockaddr *)&sa, slen);
|
||||||
|
if (rc < 0)
|
||||||
|
goto connect_error;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* send rtp data with given codec */
|
||||||
|
void osmo_cc_rtp_send(osmo_cc_session_codec_t *codec, uint8_t *data, int len, int inc_sequence, int inc_timestamp)
|
||||||
|
{
|
||||||
|
uint8_t *payload = NULL;
|
||||||
|
int payload_len = 0;
|
||||||
|
|
||||||
|
if (!codec || !codec->media->rtp_socket)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (codec->encoder)
|
||||||
|
codec->encoder(data, len, &payload, &payload_len);
|
||||||
|
else {
|
||||||
|
payload = data;
|
||||||
|
payload_len = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtp_send(codec->media->rtp_socket, payload, payload_len, codec->payload_type_remote, codec->media->tx_sequence, codec->media->tx_timestamp, codec->media->rtp_ssrc);
|
||||||
|
codec->media->tx_sequence += inc_sequence;
|
||||||
|
codec->media->tx_timestamp += inc_timestamp;
|
||||||
|
|
||||||
|
if (codec->encoder)
|
||||||
|
free(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* receive rtp data for given media, return < 0, if there is nothing this time */
|
||||||
|
int osmo_cc_rtp_receive(osmo_cc_session_media_t *media)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (!media || media->rtp_socket <= 0)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
rc = rtp_receive(media->rtp_socket, &payload, &payload_len, &marker, &payload_type, &media->rx_sequence, &media->rx_timestamp);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* search for codec */
|
||||||
|
for (codec = media->codec_list; codec; codec = codec->next) {
|
||||||
|
|
||||||
|
if (codec->payload_type_local == payload_type)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!codec) {
|
||||||
|
PDEBUG(DCC, DEBUG_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);
|
||||||
|
else {
|
||||||
|
data = payload;
|
||||||
|
len = payload_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codec->media->receive)
|
||||||
|
codec->media->receiver(codec, media->rx_sequence, media->rx_timestamp, data, len);
|
||||||
|
|
||||||
|
if (codec->decoder)
|
||||||
|
free(data);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void osmo_cc_rtp_close(osmo_cc_session_media_t *media)
|
||||||
|
{
|
||||||
|
if (media->rtp_socket) {
|
||||||
|
close(media->rtp_socket);
|
||||||
|
media->rtp_socket = 0;
|
||||||
|
}
|
||||||
|
if (media->rtcp_socket) {
|
||||||
|
close(media->rtcp_socket);
|
||||||
|
media->rtcp_socket = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
void osmo_cc_set_rtp_ports(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, int inc_sequence, int inc_timestamp);
|
||||||
|
int osmo_cc_rtp_receive(osmo_cc_session_media_t *media);
|
||||||
|
void osmo_cc_rtp_close(osmo_cc_session_media_t *media);
|
||||||
|
|
|
@ -0,0 +1,684 @@
|
||||||
|
/* 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 "../libtimer/timer.h"
|
||||||
|
#include "../libdebug/debug.h"
|
||||||
|
#include "endpoint.h"
|
||||||
|
#include "message.h"
|
||||||
|
|
||||||
|
#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");
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
} 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 {
|
||||||
|
PDEBUG(DCC, DEBUG_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);
|
||||||
|
PDEBUG(DCC, DEBUG_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);
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
|
||||||
|
PDEBUG(DCC, DEBUG_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 {
|
||||||
|
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);
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
|
||||||
|
PDEBUG(DCC, DEBUG_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';
|
||||||
|
}
|
||||||
|
|
||||||
|
star_used = 0;
|
||||||
|
next_to:
|
||||||
|
token = osmo_cc_strtok_quotes(&text);
|
||||||
|
if (!token) {
|
||||||
|
free(list);
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
|
||||||
|
PDEBUG(DCC, DEBUG_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 {
|
||||||
|
for (i = j = 0; token[i] && j < (int)sizeof(list->to) - 1; i++, j++) {
|
||||||
|
if (token[i] == '*') {
|
||||||
|
if (star_used) {
|
||||||
|
free(list);
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
|
||||||
|
PDEBUG(DCC, DEBUG_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);
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "The '@' may be used only for incoming calls from interface.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (at_used) {
|
||||||
|
free(list);
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
|
||||||
|
PDEBUG(DCC, DEBUG_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);
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
|
||||||
|
PDEBUG(DCC, DEBUG_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;
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, "Screening %s '%s':\n", what, id_from);
|
||||||
|
switch (*type) {
|
||||||
|
case OSMO_CC_TYPE_UNKNOWN:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = unknown\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_TYPE_INTERNATIONAL:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = international\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_TYPE_NATIONAL:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = national\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_TYPE_NETWORK:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = network\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_TYPE_SUBSCRIBER:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = subscriber\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_TYPE_ABBREVIATED:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = abbreviated\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (present) switch (*present) {
|
||||||
|
case OSMO_CC_PRESENT_ALLOWED:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> present = allowed\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_PRESENT_RESTRICTED:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> present = restricted\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule = 0;
|
||||||
|
while (list) {
|
||||||
|
rule++;
|
||||||
|
PDEBUG(DCC, DEBUG_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:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = unknown\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_TYPE_INTERNATIONAL:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = international\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_TYPE_NATIONAL:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = national\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_TYPE_NETWORK:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = network\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_TYPE_SUBSCRIBER:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = subscriber\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_TYPE_ABBREVIATED:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = abbreviated\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (list->has_from_present) switch (list->from_present) {
|
||||||
|
case OSMO_CC_PRESENT_ALLOWED:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> present = allowed\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_PRESENT_RESTRICTED:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> present = restricted\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
suffix = NULL;
|
||||||
|
/* attributes do not match */
|
||||||
|
if (list->has_from_type && list->from_type != *type) {
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, "Rule does not match, because 'type' is different.\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (present && list->has_from_present && list->from_present != *present) {
|
||||||
|
PDEBUG(DCC, DEBUG_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;
|
||||||
|
PDEBUG(DCC, DEBUG_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) {
|
||||||
|
*routing_p = &list->to[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* copy output digit */
|
||||||
|
id_to[j++] = list->to[i];
|
||||||
|
}
|
||||||
|
id_to[j] = '\0';
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_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:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = unknown\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_TYPE_INTERNATIONAL:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = international\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_TYPE_NATIONAL:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = national\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_TYPE_NETWORK:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = network\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_TYPE_SUBSCRIBER:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = subscriber\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_TYPE_ABBREVIATED:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> type = abbreviated\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (list->has_to_present) switch (list->to_present) {
|
||||||
|
case OSMO_CC_PRESENT_ALLOWED:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> present = allowed\n");
|
||||||
|
break;
|
||||||
|
case OSMO_CC_PRESENT_RESTRICTED:
|
||||||
|
PDEBUG(DCC, DEBUG_INFO, " -> present = restricted\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, routing_p);
|
||||||
|
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, NULL);
|
||||||
|
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), "", NULL);
|
||||||
|
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,7 @@
|
||||||
|
|
||||||
|
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,539 @@
|
||||||
|
/* 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 "../libdebug/debug.h"
|
||||||
|
#include "../libtimer/timer.h"
|
||||||
|
#include "endpoint.h"
|
||||||
|
#include "sdp.h"
|
||||||
|
|
||||||
|
#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)
|
||||||
|
{
|
||||||
|
/* 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 (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 (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) {
|
||||||
|
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)
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Fatal error: Allocated SDP buffer with %d bytes is too small, please fix!\n", (int)sizeof(sdp));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return sdp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* seperate 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(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(priv, NULL, NULL, NULL, osmo_cc_session_nettype_inet, osmo_cc_session_addrtype_ipv4, "127.0.0.1", NULL, 0); // values will be replaced by local definitions during negotiation
|
||||||
|
|
||||||
|
/* 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] != '=') {
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "SDP line %d = '%s' is garbage, expecting '=' as second character.\n", line_no, line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(line[0]) {
|
||||||
|
case 'v':
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> Version: %s\n", next_word);
|
||||||
|
if (atoi(next_word) != 0) {
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "SDP line %d = '%s' describes unsupported version.\n", line_no, line);
|
||||||
|
osmo_cc_free_session(session);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
PDEBUG(DCC, DEBUG_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 */
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> Session Name: %s\n", next_word);
|
||||||
|
free((char *)session->name); // if already set
|
||||||
|
session->name = strdup(next_word);
|
||||||
|
break;
|
||||||
|
case 'c': /* Connection Data */
|
||||||
|
PDEBUG(DCC, DEBUG_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 {
|
||||||
|
PDEBUG(DCC, DEBUG_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;
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> Address Type = IPv4\n");
|
||||||
|
} else
|
||||||
|
if (!strcmp(word, "IP6")) {
|
||||||
|
cd->addrtype = osmo_cc_session_addrtype_ipv6;
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> Address Type = IPv6\n");
|
||||||
|
} else {
|
||||||
|
PDEBUG(DCC, DEBUG_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);
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> Address = %s\n", word);
|
||||||
|
break;
|
||||||
|
case 'm': /* Media Description */
|
||||||
|
PDEBUG(DCC, DEBUG_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);
|
||||||
|
PDEBUG(DCC, DEBUG_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);
|
||||||
|
PDEBUG(DCC, DEBUG_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);
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> payload type = %d\n", codec->payload_type_remote);
|
||||||
|
if (codec->payload_name)
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> payload name = %s\n", codec->payload_name);
|
||||||
|
if (codec->payload_rate)
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> payload rate = %d\n", codec->payload_rate);
|
||||||
|
if (codec->payload_channels)
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> payload channels = %d\n", codec->payload_channels);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
PDEBUG(DCC, DEBUG_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) {
|
||||||
|
PDEBUG(DCC, DEBUG_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) {
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "Attribute without previously defined codec in SDP line %d = '%s'\n", line_no, line);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> (rtpmap) payload type = %d\n", codec->payload_type_remote);
|
||||||
|
if (!(word = wordsep(&next_word)))
|
||||||
|
break;
|
||||||
|
if ((p = strchr(word, '/')))
|
||||||
|
*p++ = '\0';
|
||||||
|
free((char *)codec->payload_name); // in case it is already set above
|
||||||
|
codec->payload_name = strdup(word);
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> (rtpmap) payload name = %s\n", codec->payload_name);
|
||||||
|
if (!(word = p))
|
||||||
|
break;
|
||||||
|
if ((p = strchr(word, '/')))
|
||||||
|
*p++ = '\0';
|
||||||
|
codec->payload_rate = atoi(word);
|
||||||
|
PDEBUG(DCC, DEBUG_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;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
codec->payload_channels = atoi(word);
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> (rtpmap) payload channels = %d\n", codec->payload_channels);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if something is incomplete, abort here */
|
||||||
|
if (osmo_cc_session_check(session, 1)) {
|
||||||
|
PDEBUG(DCC, DEBUG_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';
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " | %s\n", text);
|
||||||
|
while (*sdp > 0 && *sdp < 32)
|
||||||
|
sdp++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
char *osmo_cc_session_gensdp(struct osmo_cc_session *session);
|
||||||
|
struct osmo_cc_session *osmo_cc_session_parsesdp(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,639 @@
|
||||||
|
/* 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 "../libtimer/timer.h"
|
||||||
|
#include "../libdebug/debug.h"
|
||||||
|
#include "endpoint.h"
|
||||||
|
|
||||||
|
#define NTP_OFFSET 2208988800
|
||||||
|
|
||||||
|
enum osmo_cc_session_nettype default_nettype = osmo_cc_session_nettype_inet;
|
||||||
|
enum osmo_cc_session_addrtype default_addrtype = osmo_cc_session_addrtype_ipv4;
|
||||||
|
const char *default_unicast_address = "127.0.0.1";
|
||||||
|
|
||||||
|
void osmo_cc_set_local_peer(enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address)
|
||||||
|
{
|
||||||
|
default_nettype = nettype;
|
||||||
|
default_addrtype = addrtype;
|
||||||
|
default_unicast_address = strdup(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
osmo_cc_session_t *osmo_cc_new_session(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) PDEBUG(DCC, DEBUG_DEBUG, "Creating session structure.\n");
|
||||||
|
|
||||||
|
session = calloc(1, sizeof(*session));
|
||||||
|
if (!session) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "No mem!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
session->priv = priv;
|
||||||
|
if (username) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; username[i]; i++) {
|
||||||
|
if ((uint8_t)username[i] < 33) {
|
||||||
|
PDEBUG(DCC, DEBUG_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) PDEBUG(DCC, DEBUG_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) PDEBUG(DCC, DEBUG_DEBUG, " -> session ID = %s\n", session->origin_local.sess_id);
|
||||||
|
if (debug) PDEBUG(DCC, DEBUG_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(default_nettype));
|
||||||
|
if (debug) PDEBUG(DCC, DEBUG_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(default_addrtype));
|
||||||
|
if (debug) PDEBUG(DCC, DEBUG_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(default_unicast_address);
|
||||||
|
if (debug) PDEBUG(DCC, DEBUG_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) PDEBUG(DCC, DEBUG_DEBUG, " -> session name = %s\n", session->name);
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
void osmo_cc_free_session(osmo_cc_session_t *session)
|
||||||
|
{
|
||||||
|
PDEBUG(DCC, DEBUG_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, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), int debug)
|
||||||
|
{
|
||||||
|
osmo_cc_session_media_t *media, **mediap;
|
||||||
|
|
||||||
|
media = calloc(1, sizeof(*media));
|
||||||
|
if (!media) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "No mem!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
media->session = session;
|
||||||
|
if (nettype)
|
||||||
|
media->connection_data_local.nettype = nettype;
|
||||||
|
else
|
||||||
|
media->connection_data_local.nettype = default_nettype;
|
||||||
|
if (addrtype)
|
||||||
|
media->connection_data_local.addrtype = addrtype;
|
||||||
|
else
|
||||||
|
media->connection_data_local.addrtype = default_addrtype;
|
||||||
|
if (address)
|
||||||
|
media->connection_data_local.address = strdup(address);
|
||||||
|
else
|
||||||
|
media->connection_data_local.address = strdup(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) PDEBUG(DCC, DEBUG_DEBUG, "Adding session media.\n");
|
||||||
|
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> network type = %s\n", osmo_cc_session_nettype2string(media->connection_data_local.nettype));
|
||||||
|
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> address type = %s\n", osmo_cc_session_addrtype2string(media->connection_data_local.addrtype));
|
||||||
|
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> address = %s\n", media->connection_data_local.address);
|
||||||
|
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> media type = %s\n", osmo_cc_session_media_type2string(media->description.type));
|
||||||
|
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> media port = %d\n", media->description.port_local);
|
||||||
|
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> media proto = %s\n", osmo_cc_session_media_proto2string(media->description.proto));
|
||||||
|
if (debug) PDEBUG(DCC, DEBUG_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;
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_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 (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), int debug)
|
||||||
|
{
|
||||||
|
osmo_cc_session_codec_t *codec, **codecp;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
codec = calloc(1, sizeof(*codec));
|
||||||
|
if (!codec) {
|
||||||
|
PDEBUG(DCC, DEBUG_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) PDEBUG(DCC, DEBUG_DEBUG, "Adding session codec.\n");
|
||||||
|
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> payload type = %d\n", codec->payload_type_local);
|
||||||
|
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> payload name = %s\n", codec->payload_name);
|
||||||
|
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> payload rate = %d\n", codec->payload_rate);
|
||||||
|
if (debug) PDEBUG(DCC, DEBUG_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;
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_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) {
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "Missing data in session origin\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (!session->name) {
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "Missing data in session origin\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (!session->media_list) {
|
||||||
|
PDEBUG(DCC, DEBUG_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) {
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "Session with media #%d is missing connection network type\n", i);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (!cd->addrtype && !cd->addrtype_name) {
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "Session with media #%d is missing connection address type\n", i);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (!cd->address) {
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "Session with media #%d is missing connection address\n", i);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
md = &media->description;
|
||||||
|
if (!md->type && !md->type_name) {
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "Session with media #%d is missing media type\n", i);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (!md->proto && !md->proto_name) {
|
||||||
|
PDEBUG(DCC, DEBUG_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) {
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "Session with media #%d, codec #%d is missing name\n", i, j);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (!codec->payload_rate) {
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "Session with media #%d, codec #%d is missing rate\n", i, j);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (!codec->payload_channels) {
|
||||||
|
PDEBUG(DCC, DEBUG_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;
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, "Generating session offer and opening RTP stream.\n");
|
||||||
|
|
||||||
|
rc = osmo_cc_session_check(session, 0);
|
||||||
|
if (rc < 0) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Please fix!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
sdp = osmo_cc_session_gensdp(session);
|
||||||
|
osmo_cc_debug_sdp(sdp);
|
||||||
|
|
||||||
|
return sdp;
|
||||||
|
}
|
||||||
|
|
||||||
|
osmo_cc_session_t *osmo_cc_session_receive_offer(void *priv, const char *sdp)
|
||||||
|
{
|
||||||
|
osmo_cc_session_t *session;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, "Parsing session offer.\n");
|
||||||
|
|
||||||
|
osmo_cc_debug_sdp(sdp);
|
||||||
|
session = osmo_cc_session_parsesdp(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, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len))
|
||||||
|
{
|
||||||
|
media->accepted = 1;
|
||||||
|
if (nettype)
|
||||||
|
media->connection_data_local.nettype = nettype;
|
||||||
|
else
|
||||||
|
media->connection_data_local.nettype = default_nettype;
|
||||||
|
if (addrtype)
|
||||||
|
media->connection_data_local.addrtype = addrtype;
|
||||||
|
else
|
||||||
|
media->connection_data_local.addrtype = 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(default_unicast_address);
|
||||||
|
media->send = send;
|
||||||
|
media->receive = receive;
|
||||||
|
media->receiver = receiver;
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, "Accepting session media.\n");
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> network type = %s\n", osmo_cc_session_nettype2string(media->connection_data_local.nettype));
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> address type = %s\n", osmo_cc_session_addrtype2string(media->connection_data_local.addrtype));
|
||||||
|
PDEBUG(DCC, DEBUG_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 (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, "Accepting session codec.\n");
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> payload type = %d\n", codec->payload_type_local);
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> payload name = %s\n", codec->payload_name);
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> payload rate = %d\n", codec->payload_rate);
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, " -> payload channels = %d\n", codec->payload_channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove codecs/media that have not been accepted and generate SDP */
|
||||||
|
const char *osmo_cc_session_send_answer(osmo_cc_session_t *session)
|
||||||
|
{
|
||||||
|
osmo_cc_session_media_t *media;
|
||||||
|
osmo_cc_session_codec_t *codec, **codec_p;
|
||||||
|
const char *sdp;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, "Generating session answer.\n");
|
||||||
|
|
||||||
|
/* loop all media */
|
||||||
|
osmo_cc_session_for_each_media(session->media_list, media) {
|
||||||
|
/* remove unaccepted codecs */
|
||||||
|
codec_p = &media->codec_list;
|
||||||
|
codec = *codec_p;
|
||||||
|
while (codec) {
|
||||||
|
if (!codec->accepted) {
|
||||||
|
osmo_cc_free_codec(codec);
|
||||||
|
codec = *codec_p;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
codec_p = &codec->next;
|
||||||
|
codec = *codec_p;
|
||||||
|
}
|
||||||
|
/* mark media as unused, if no codec or not accepted */
|
||||||
|
if (!media->accepted || !media->codec_list)
|
||||||
|
media->description.port_local = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = osmo_cc_session_check(session, 0);
|
||||||
|
if (rc < 0) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Please fix!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
sdp = osmo_cc_session_gensdp(session);
|
||||||
|
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;
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, "Negotiating session.\n");
|
||||||
|
|
||||||
|
/* copy remote session infromation */
|
||||||
|
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) {
|
||||||
|
osmo_cc_free_codec(codec_local);
|
||||||
|
codec_local = *codec_local_p;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* copy remote codec information */
|
||||||
|
codec_local->payload_type_remote = codec_remote->payload_type_remote;
|
||||||
|
codec_local_p = &codec_local->next;
|
||||||
|
codec_local = *codec_local_p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (media_local) {
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "Negotiation failed, because remote endpoint returns less media streams than we offered.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (media_remote) {
|
||||||
|
PDEBUG(DCC, DEBUG_NOTICE, "Negotiation failed, because remote endpoint returns more media streams than we offered.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove media with port == 0 or no codec at all */
|
||||||
|
media_local_p = &session_local->media_list;
|
||||||
|
media_local = *media_local_p;
|
||||||
|
while (media_local) {
|
||||||
|
if (media_local->description.port_remote == 0 || !media_local->codec_list) {
|
||||||
|
osmo_cc_free_media(media_local);
|
||||||
|
media_local = *media_local_p;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, "Parsing session answer.\n");
|
||||||
|
|
||||||
|
osmo_cc_debug_sdp(sdp);
|
||||||
|
session_remote = osmo_cc_session_parsesdp(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
int osmo_cc_session_handle(osmo_cc_session_t *session)
|
||||||
|
{
|
||||||
|
osmo_cc_session_media_t *media;
|
||||||
|
int w = 0, rc;
|
||||||
|
|
||||||
|
osmo_cc_session_for_each_media(session->media_list, media) {
|
||||||
|
do {
|
||||||
|
rc = osmo_cc_rtp_receive(media);
|
||||||
|
if (rc >= 0)
|
||||||
|
w = 1;
|
||||||
|
} while (rc >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
|
||||||
|
/* 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 {
|
||||||
|
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: */
|
||||||
|
|
||||||
|
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_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, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len);
|
||||||
|
int rtp_socket;
|
||||||
|
int rtcp_socket;
|
||||||
|
uint32_t rtp_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 (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||||
|
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(enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address);
|
||||||
|
osmo_cc_session_t *osmo_cc_new_session(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, uint16_t sequence_number, uint32_t timestamp, 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 (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), 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(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, uint16_t sequence_number, uint32_t timestamp, 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 (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len));
|
||||||
|
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);
|
||||||
|
int osmo_cc_session_handle(osmo_cc_session_t *session);
|
||||||
|
|
|
@ -0,0 +1,583 @@
|
||||||
|
/* 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 "../libdebug/debug.h"
|
||||||
|
#include "../libtimer/timer.h"
|
||||||
|
#include "message.h"
|
||||||
|
#include "cause.h"
|
||||||
|
#include "socket.h"
|
||||||
|
|
||||||
|
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) {
|
||||||
|
PDEBUG(DCC, DEBUG_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tx_keepalive_timeout(struct timer *timer)
|
||||||
|
{
|
||||||
|
osmo_cc_conn_t *conn = (osmo_cc_conn_t *)timer->priv;
|
||||||
|
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);
|
||||||
|
timer_start(&conn->tx_keepalive_timer, OSMO_CC_SOCKET_TX_KEEPALIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void close_conn(osmo_cc_conn_t *conn, uint8_t socket_cause);
|
||||||
|
|
||||||
|
void rx_keepalive_timeout(struct timer *timer)
|
||||||
|
{
|
||||||
|
osmo_cc_conn_t *conn = (osmo_cc_conn_t *)timer->priv;
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "OsmoCC-Socket failed due to timeout.\n");
|
||||||
|
close_conn(conn, OSMO_CC_SOCKET_CAUSE_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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, flags;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_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) {
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, "Failed to bind host %s port %d, trying again.\n", host, port);
|
||||||
|
goto try_again;
|
||||||
|
}
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Failed to bind given host %s port %d.\n", host, port);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* listen to socket */
|
||||||
|
rc = listen(sock, 10);
|
||||||
|
if (rc < 0) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "Failed to listen on socket.\n");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set nonblocking io */
|
||||||
|
flags = fcntl(sock, F_GETFL);
|
||||||
|
flags |= O_NONBLOCK;
|
||||||
|
fcntl(sock, F_SETFL, flags);
|
||||||
|
|
||||||
|
os->socket = sock;
|
||||||
|
os->recv_msg_cb = recv_msg_cb;
|
||||||
|
os->priv = priv;
|
||||||
|
os->location = location;
|
||||||
|
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "No mem!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
conn->os = os;
|
||||||
|
conn->socket = sock;
|
||||||
|
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();
|
||||||
|
|
||||||
|
timer_init(&conn->tx_keepalive_timer, tx_keepalive_timeout, conn);
|
||||||
|
timer_init(&conn->rx_keepalive_timer, rx_keepalive_timeout, conn);
|
||||||
|
timer_start(&conn->tx_keepalive_timer, OSMO_CC_SOCKET_TX_KEEPALIVE);
|
||||||
|
timer_start(&conn->rx_keepalive_timer, OSMO_CC_SOCKET_RX_KEEPALIVE);
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, "Destroy socket connection (callref %d).\n", conn->callref);
|
||||||
|
|
||||||
|
/* close socket */
|
||||||
|
if (conn->socket)
|
||||||
|
close(conn->socket);
|
||||||
|
/* 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 */
|
||||||
|
timer_exit(&conn->tx_keepalive_timer);
|
||||||
|
timer_exit(&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;
|
||||||
|
|
||||||
|
PDEBUG(DCC, DEBUG_DEBUG, "Destroy socket.\n");
|
||||||
|
|
||||||
|
/* free all connections */
|
||||||
|
while (os->conn_list)
|
||||||
|
close_conn(os->conn_list, 0);
|
||||||
|
/* close socket */
|
||||||
|
if (os->socket > 0) {
|
||||||
|
close(os->socket);
|
||||||
|
os->socket = 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->socket, 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)) {
|
||||||
|
PDEBUG(DCC, DEBUG_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]) {
|
||||||
|
PDEBUG(DCC, DEBUG_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->socket, ((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->socket, 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 */
|
||||||
|
timer_start(&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) {
|
||||||
|
PDEBUG(DCC, DEBUG_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:
|
||||||
|
PDEBUG(DCC, DEBUG_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->socket, version_string, strlen(version_string));
|
||||||
|
if (rc < 0 && errno == EAGAIN)
|
||||||
|
return work;
|
||||||
|
work = 1;
|
||||||
|
if (rc <= 0) {
|
||||||
|
goto close;
|
||||||
|
}
|
||||||
|
if (rc != strlen(version_string)) {
|
||||||
|
PDEBUG(DCC, DEBUG_ERROR, "short write, please fix handling!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
conn->write_version = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* send message to remote */
|
||||||
|
while (conn->write_list) {
|
||||||
|
timer_stop(&conn->tx_keepalive_timer);
|
||||||
|
msg = conn->write_list->msg;
|
||||||
|
len = sizeof(*msg) + ntohs(msg->length_networkorder);
|
||||||
|
rc = write(conn->socket, msg, len);
|
||||||
|
if (rc < 0 && errno == EAGAIN)
|
||||||
|
return work;
|
||||||
|
work = 1;
|
||||||
|
if (rc <= 0) {
|
||||||
|
goto close;
|
||||||
|
}
|
||||||
|
if (rc != len) {
|
||||||
|
PDEBUG(DCC, DEBUG_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) {
|
||||||
|
PDEBUG(DCC, DEBUG_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 timeer, if not already
|
||||||
|
* because we stop at every message above, we actually restart the timer here.
|
||||||
|
* only if there is no message for the amout of time, the timer fires.
|
||||||
|
*/
|
||||||
|
if (!timer_running(&conn->tx_keepalive_timer))
|
||||||
|
timer_start(&conn->tx_keepalive_timer, OSMO_CC_SOCKET_TX_KEEPALIVE);
|
||||||
|
|
||||||
|
return work;
|
||||||
|
|
||||||
|
close:
|
||||||
|
PDEBUG(DCC, DEBUG_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)
|
||||||
|
{
|
||||||
|
struct sockaddr_storage sa;
|
||||||
|
socklen_t slen = sizeof(sa);
|
||||||
|
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;
|
||||||
|
/* 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) {
|
||||||
|
PDEBUG(DCC, DEBUG_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 */
|
||||||
|
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) {
|
||||||
|
PDEBUG(DCC, DEBUG_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;
|
||||||
|
/* done with (setup) message */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* handle new socket connection */
|
||||||
|
while ((sock = accept(os->socket, (struct sockaddr *)&sa, &slen)) > 0) {
|
||||||
|
work = 1;
|
||||||
|
/* set nonblocking io */
|
||||||
|
flags = fcntl(sock, F_GETFL);
|
||||||
|
flags |= O_NONBLOCK;
|
||||||
|
fcntl(sock, F_SETFL, flags);
|
||||||
|
/* create connection */
|
||||||
|
open_conn(os, sock, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* start with list after each read/write, because while handling (the message), one or more connections may be destroyed */
|
||||||
|
for (conn = os->conn_list; conn; conn=conn->next) {
|
||||||
|
/* check for rx */
|
||||||
|
work = receive_conn(conn);
|
||||||
|
/* if "change" is set, connection list might have changed, so we restart processing the list */
|
||||||
|
if (work)
|
||||||
|
break;
|
||||||
|
/* check for tx */
|
||||||
|
work = transmit_conn(conn);
|
||||||
|
/* if "change" is set, connection list might have changed, so we restart processing the list */
|
||||||
|
if (work)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return work;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
#ifndef OSMO_CC_SOCKET_H
|
||||||
|
#define OSMO_CC_SOCKET_H
|
||||||
|
|
||||||
|
#define OSMO_CC_DEFAULT_PORT 4200
|
||||||
|
#define OSMO_CC_DEFAULT_PORT_MAX 4299
|
||||||
|
|
||||||
|
#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;
|
||||||
|
int socket;
|
||||||
|
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 timer tx_keepalive_timer;
|
||||||
|
struct timer rx_keepalive_timer;
|
||||||
|
} osmo_cc_conn_t;
|
||||||
|
|
||||||
|
typedef struct osmo_cc_socket {
|
||||||
|
int socket;
|
||||||
|
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);
|
||||||
|
|
||||||
|
#endif /* OSMO_CC_SOCKET_H */
|
|
@ -0,0 +1,6 @@
|
||||||
|
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||||
|
|
||||||
|
noinst_LIBRARIES = libsample.a
|
||||||
|
|
||||||
|
libsample_a_SOURCES = \
|
||||||
|
sample.c
|
|
@ -0,0 +1,64 @@
|
||||||
|
/* Sample definition
|
||||||
|
*
|
||||||
|
* (C) 2017 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 "sample.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A regular voice conversation takes place at this factor below the full range
|
||||||
|
* of 16 bits signed value:
|
||||||
|
*/
|
||||||
|
static double int_16_speech_level = SPEECH_LEVEL * 0.7079; /* 16 dBm below dBm0, which is about 3dBm below full 16 bit range */
|
||||||
|
|
||||||
|
/* A sample_t is a value that has virtually infinite precision but will also
|
||||||
|
* support high numbers. 'double' or 'float' types are sufficient.
|
||||||
|
*
|
||||||
|
* When using sample_t inside signal processing of each base station, the
|
||||||
|
* level of +- 1 is relative to the normal speech evenlope.
|
||||||
|
*
|
||||||
|
* When converting sample_t to int16_t, the level of +- 1 is reduced by factor.
|
||||||
|
* This way the speech may be louder before clipping happens.
|
||||||
|
*
|
||||||
|
* When using sample_t to modulate (SDR or sound card), the level is changed,
|
||||||
|
* so it represents the frequency deviation in Hz. The deviation of speech
|
||||||
|
* envelope is network dependent.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void samples_to_int16(int16_t *spl, sample_t *samples, int length)
|
||||||
|
{
|
||||||
|
int32_t value;
|
||||||
|
|
||||||
|
while (length--) {
|
||||||
|
value = *samples++ * int_16_speech_level * 32768.0;
|
||||||
|
if (value > 32767.0)
|
||||||
|
*spl++ = 32767;
|
||||||
|
else if (value < -32767.0)
|
||||||
|
*spl++ = -32767;
|
||||||
|
else
|
||||||
|
*spl++ = (uint16_t)value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void int16_to_samples(sample_t *samples, int16_t *spl, int length)
|
||||||
|
{
|
||||||
|
while (length--) {
|
||||||
|
*samples++ = (double)(*spl++) / 32767.0 / int_16_speech_level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
typedef double sample_t;
|
||||||
|
|
||||||
|
#define SPEECH_LEVEL 0.1585
|
||||||
|
|
||||||
|
void samples_to_int16(int16_t *spl, sample_t *samples, int length);
|
||||||
|
void int16_to_samples(sample_t *samples, int16_t *spl, int length);
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||||
|
|
||||||
|
noinst_LIBRARIES = libtimer.a
|
||||||
|
|
||||||
|
libtimer_a_SOURCES = \
|
||||||
|
timer.c
|
|
@ -0,0 +1,121 @@
|
||||||
|
/* Timer 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 <string.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
|
static struct timer *timer_head = NULL;
|
||||||
|
static struct timer **timer_tail_p = &timer_head;
|
||||||
|
|
||||||
|
double get_time(void)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
|
||||||
|
return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void timer_init(struct timer *timer, void (*fn)(struct timer *timer), void *priv)
|
||||||
|
{
|
||||||
|
if (timer->linked) {
|
||||||
|
fprintf(stderr, "Timer is already initialized, aborting!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
timer->timeout = 0;
|
||||||
|
timer->fn = fn;
|
||||||
|
timer->priv = priv;
|
||||||
|
timer->next = NULL;
|
||||||
|
*timer_tail_p = timer;
|
||||||
|
timer_tail_p = &timer->next;
|
||||||
|
timer->linked = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void timer_exit(struct timer *timer)
|
||||||
|
{
|
||||||
|
timer_tail_p = &timer_head;
|
||||||
|
while (*timer_tail_p) {
|
||||||
|
if (timer == *timer_tail_p)
|
||||||
|
*timer_tail_p = (*timer_tail_p)->next;
|
||||||
|
else
|
||||||
|
timer_tail_p = &((*timer_tail_p)->next);
|
||||||
|
}
|
||||||
|
timer->linked = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void timer_start(struct timer *timer, double duration)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
if (!timer->linked) {
|
||||||
|
fprintf(stderr, "Timer is not initialized, aborting!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
|
||||||
|
timer->duration = duration;
|
||||||
|
timer->timeout = get_time() + duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void timer_stop(struct timer *timer)
|
||||||
|
{
|
||||||
|
if (!timer->linked) {
|
||||||
|
fprintf(stderr, "Timer is not initialized, aborting!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
timer->timeout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int timer_running(struct timer *timer)
|
||||||
|
{
|
||||||
|
if (!timer->linked) {
|
||||||
|
fprintf(stderr, "Timer is not initialized, aborting!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (timer->timeout != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_timer(void)
|
||||||
|
{
|
||||||
|
struct timer *timer;
|
||||||
|
double now;
|
||||||
|
|
||||||
|
now = get_time();
|
||||||
|
|
||||||
|
again:
|
||||||
|
timer = timer_head;
|
||||||
|
|
||||||
|
while (timer) {
|
||||||
|
if (timer->linked && timer->timeout > 0 && now >= timer->timeout) {
|
||||||
|
timer->timeout = 0;
|
||||||
|
timer->fn(timer);
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
timer = timer->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
|
||||||
|
struct timer {
|
||||||
|
struct timer *next;
|
||||||
|
int linked; /* set is timer is initialized and linked */
|
||||||
|
double duration;
|
||||||
|
double timeout;
|
||||||
|
void (*fn)(struct timer *timer);
|
||||||
|
void *priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
double get_time(void);
|
||||||
|
void timer_init(struct timer *timer, void (*fn)(struct timer *timer), void *priv);
|
||||||
|
void timer_exit(struct timer *timer);
|
||||||
|
void timer_start(struct timer *timer, double duration);
|
||||||
|
void timer_stop(struct timer *timer);
|
||||||
|
int timer_running(struct timer *timer);
|
||||||
|
void process_timer(void);
|
||||||
|
|
Loading…
Reference in New Issue