commit
593121d551
@ -0,0 +1,40 @@
|
||||
CC= gcc
|
||||
CFLAGS= -g -Wall `pkg-config --cflags glib-2.0` `pcre-config --cflags` -I/lib/modules/`uname -r`/build/include/ -I../kernel-module/
|
||||
#CFLAGS+= -O2
|
||||
LDFLAGS= `pkg-config --libs glib-2.0` `pcre-config --libs`
|
||||
|
||||
SRCS= main.c kernel.c poller.c aux.c control.c streambuf.c call.c
|
||||
|
||||
OBJS= $(SRCS:.c=.o)
|
||||
|
||||
|
||||
.PHONY: all dep clean tests
|
||||
|
||||
all:
|
||||
make mediaproxy-ng
|
||||
|
||||
tests:
|
||||
make aux-test poller-test
|
||||
|
||||
dep: .depend
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) mediaproxy-ng aux-test poller-test aux-test.o poller-test.o .depend build_time.h core
|
||||
|
||||
.depend: $(SRCS) Makefile build_time.h
|
||||
$(CC) $(CFLAGS) -M $(SRCS) | sed -e 's/:/ .depend:/' > .depend
|
||||
|
||||
build_time.h: $(SRCS) Makefile
|
||||
date +"#define BUILD_TIME \"%Y-%m-%d %H:%M:%S\"" > build_time.h
|
||||
|
||||
mediaproxy-ng: $(OBJS) .depend build_time.h
|
||||
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS)
|
||||
|
||||
aux-test: aux.o aux-test.o .depend build_time.h
|
||||
$(CC) $(CFLAGS) -o $@ aux-test.o aux.o $(LDFLAGS)
|
||||
|
||||
poller-test: poller.o poller-test.o aux.o .depend build_time.h
|
||||
$(CC) $(CFLAGS) -o $@ poller-test.o poller.o aux.o $(LDFLAGS)
|
||||
|
||||
|
||||
include .depend
|
@ -0,0 +1,305 @@
|
||||
#include <stdio.h>
|
||||
#include "aux.h"
|
||||
|
||||
|
||||
int test[32] = {
|
||||
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
|
||||
0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xff,
|
||||
|
||||
0x10ff, 0x20ff, 0x30ff, 0x40ff, 0x50ff, 0x60ff, 0x70ff, 0x80ff,
|
||||
0x90ff, 0xa0ff, 0xb0ff, 0xc0ff, 0xd0ff, 0xe0ff, 0xf0ff, 0xffff,
|
||||
};
|
||||
|
||||
void exsrx(int x, int exp, unsigned int s, int ex) {
|
||||
int ret = mybsearch(test, s, sizeof(int), &x, 0, sizeof(x), ex);
|
||||
if (ret != exp)
|
||||
fprintf(stderr, "TEST FAILED! params=%u %i; result=%i, expected=%i\n", s, ex, ret, exp);
|
||||
}
|
||||
|
||||
void exsr1(int x, int exp) {
|
||||
exsrx(x, exp, 16, 1);
|
||||
}
|
||||
|
||||
void exsr2(int x, int exp) {
|
||||
exsrx(x, exp, 15, 1);
|
||||
}
|
||||
|
||||
void exsr3(int x, int exp) {
|
||||
exsrx(x, exp, 2, 1);
|
||||
}
|
||||
|
||||
void exsr4(int x, int exp) {
|
||||
exsrx(x, exp, 1, 1);
|
||||
}
|
||||
|
||||
void exsr5(int x, int exp) {
|
||||
exsrx(x, exp, 32, 1);
|
||||
}
|
||||
|
||||
void exsr6(int x, int exp) {
|
||||
exsrx(x, exp, 31, 1);
|
||||
}
|
||||
|
||||
void exsr7(int x, int exp) {
|
||||
exsrx(x, exp, 16, 0);
|
||||
}
|
||||
|
||||
void exsr8(int x, int exp) {
|
||||
exsrx(x, exp, 15, 0);
|
||||
}
|
||||
|
||||
void exsr9(int x, int exp) {
|
||||
exsrx(x, exp, 2, 0);
|
||||
}
|
||||
|
||||
void exsr10(int x, int exp) {
|
||||
exsrx(x, exp, 1, 0);
|
||||
}
|
||||
|
||||
void exsr11(int x, int exp) {
|
||||
exsrx(x, exp, 32, 0);
|
||||
}
|
||||
|
||||
void exsr12(int x, int exp) {
|
||||
exsrx(x, exp, 31, 0);
|
||||
}
|
||||
|
||||
int main() {
|
||||
exsr1(0x10, 0);
|
||||
exsr1(0x20, 1);
|
||||
exsr1(0x30, 2);
|
||||
exsr1(0x40, 3);
|
||||
exsr1(0x50, 4);
|
||||
exsr1(0x60, 5);
|
||||
exsr1(0x70, 6);
|
||||
exsr1(0x80, 7);
|
||||
exsr1(0x90, 8);
|
||||
exsr1(0xa0, 9);
|
||||
exsr1(0xb0, 10);
|
||||
exsr1(0xc0, 11);
|
||||
exsr1(0xd0, 12);
|
||||
exsr1(0xe0, 13);
|
||||
exsr1(0xf0, 14);
|
||||
exsr1(0xff, 15);
|
||||
exsr1(0xffff, -1);
|
||||
exsr1(0xfe, -1);
|
||||
|
||||
exsr2(0x10, 0);
|
||||
exsr2(0x20, 1);
|
||||
exsr2(0x30, 2);
|
||||
exsr2(0x40, 3);
|
||||
exsr2(0x50, 4);
|
||||
exsr2(0x60, 5);
|
||||
exsr2(0x70, 6);
|
||||
exsr2(0x80, 7);
|
||||
exsr2(0x90, 8);
|
||||
exsr2(0xa0, 9);
|
||||
exsr2(0xb0, 10);
|
||||
exsr2(0xc0, 11);
|
||||
exsr2(0xd0, 12);
|
||||
exsr2(0xe0, 13);
|
||||
exsr2(0xf0, 14);
|
||||
exsr2(0xff, -1);
|
||||
|
||||
exsr3(0x10, 0);
|
||||
exsr3(0x20, 1);
|
||||
exsr3(0x30, -1);
|
||||
|
||||
exsr4(0x10, 0);
|
||||
exsr4(0x20, -1);
|
||||
|
||||
exsr5(0x10, 0);
|
||||
exsr5(0x20, 1);
|
||||
exsr5(0x30, 2);
|
||||
exsr5(0x40, 3);
|
||||
exsr5(0x50, 4);
|
||||
exsr5(0x60, 5);
|
||||
exsr5(0x70, 6);
|
||||
exsr5(0x80, 7);
|
||||
exsr5(0x90, 8);
|
||||
exsr5(0xa0, 9);
|
||||
exsr5(0xb0, 10);
|
||||
exsr5(0xc0, 11);
|
||||
exsr5(0xd0, 12);
|
||||
exsr5(0xe0, 13);
|
||||
exsr5(0xf0, 14);
|
||||
exsr5(0xff, 15);
|
||||
exsr5(0x10ff, 16);
|
||||
exsr5(0x20ff, 17);
|
||||
exsr5(0x30ff, 18);
|
||||
exsr5(0x40ff, 19);
|
||||
exsr5(0x50ff, 20);
|
||||
exsr5(0x60ff, 21);
|
||||
exsr5(0x70ff, 22);
|
||||
exsr5(0x80ff, 23);
|
||||
exsr5(0x90ff, 24);
|
||||
exsr5(0xa0ff, 25);
|
||||
exsr5(0xb0ff, 26);
|
||||
exsr5(0xc0ff, 27);
|
||||
exsr5(0xd0ff, 28);
|
||||
exsr5(0xe0ff, 29);
|
||||
exsr5(0xf0ff, 30);
|
||||
exsr5(0xffff, 31);
|
||||
exsr5(0xfff3, -1);
|
||||
exsr5(0xffffff, -1);
|
||||
|
||||
exsr6(0x10, 0);
|
||||
exsr6(0x20, 1);
|
||||
exsr6(0x30, 2);
|
||||
exsr6(0x40, 3);
|
||||
exsr6(0x50, 4);
|
||||
exsr6(0x60, 5);
|
||||
exsr6(0x70, 6);
|
||||
exsr6(0x80, 7);
|
||||
exsr6(0x90, 8);
|
||||
exsr6(0xa0, 9);
|
||||
exsr6(0xb0, 10);
|
||||
exsr6(0xc0, 11);
|
||||
exsr6(0xd0, 12);
|
||||
exsr6(0xe0, 13);
|
||||
exsr6(0xf0, 14);
|
||||
exsr6(0xff, 15);
|
||||
exsr6(0x10ff, 16);
|
||||
exsr6(0x20ff, 17);
|
||||
exsr6(0x30ff, 18);
|
||||
exsr6(0x40ff, 19);
|
||||
exsr6(0x50ff, 20);
|
||||
exsr6(0x60ff, 21);
|
||||
exsr6(0x70ff, 22);
|
||||
exsr6(0x80ff, 23);
|
||||
exsr6(0x90ff, 24);
|
||||
exsr6(0xa0ff, 25);
|
||||
exsr6(0xb0ff, 26);
|
||||
exsr6(0xc0ff, 27);
|
||||
exsr6(0xd0ff, 28);
|
||||
exsr6(0xe0ff, 29);
|
||||
exsr6(0xf0ff, 30);
|
||||
exsr6(0xffff, -1);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
exsr7(0x10, 0);
|
||||
exsr7(0x20, 1);
|
||||
exsr7(0x30, 2);
|
||||
exsr7(0x40, 3);
|
||||
exsr7(0x50, 4);
|
||||
exsr7(0x60, 5);
|
||||
exsr7(0x70, 6);
|
||||
exsr7(0x80, 7);
|
||||
exsr7(0x90, 8);
|
||||
exsr7(0xa0, 9);
|
||||
exsr7(0xb0, 10);
|
||||
exsr7(0xc0, 11);
|
||||
exsr7(0xd0, 12);
|
||||
exsr7(0xe0, 13);
|
||||
exsr7(0xf0, 14);
|
||||
exsr7(0xff, 15);
|
||||
exsr7(0xffff, -17);
|
||||
exsr7(0xfe, -16);
|
||||
exsr7(0x00, -1);
|
||||
exsr8(0x05, -1);
|
||||
exsr8(0x15, -2);
|
||||
|
||||
exsr8(0x10, 0);
|
||||
exsr8(0x20, 1);
|
||||
exsr8(0x30, 2);
|
||||
exsr8(0x40, 3);
|
||||
exsr8(0x50, 4);
|
||||
exsr8(0x60, 5);
|
||||
exsr8(0x70, 6);
|
||||
exsr8(0x80, 7);
|
||||
exsr8(0x90, 8);
|
||||
exsr8(0xa0, 9);
|
||||
exsr8(0xb0, 10);
|
||||
exsr8(0xc0, 11);
|
||||
exsr8(0xd0, 12);
|
||||
exsr8(0xe0, 13);
|
||||
exsr8(0xf0, 14);
|
||||
exsr8(0xff, -16);
|
||||
exsr8(0xffff, -16);
|
||||
exsr8(0xef, -15);
|
||||
exsr8(0x00, -1);
|
||||
exsr8(0x05, -1);
|
||||
exsr8(0x15, -2);
|
||||
|
||||
exsr9(0x10, 0);
|
||||
exsr9(0x20, 1);
|
||||
exsr9(0x30, -3);
|
||||
|
||||
exsr10(0x10, 0);
|
||||
exsr10(0x20, -2);
|
||||
|
||||
exsr11(0x10, 0);
|
||||
exsr11(0x20, 1);
|
||||
exsr11(0x30, 2);
|
||||
exsr11(0x40, 3);
|
||||
exsr11(0x50, 4);
|
||||
exsr11(0x60, 5);
|
||||
exsr11(0x70, 6);
|
||||
exsr11(0x80, 7);
|
||||
exsr11(0x90, 8);
|
||||
exsr11(0xa0, 9);
|
||||
exsr11(0xb0, 10);
|
||||
exsr11(0xc0, 11);
|
||||
exsr11(0xd0, 12);
|
||||
exsr11(0xe0, 13);
|
||||
exsr11(0xf0, 14);
|
||||
exsr11(0xff, 15);
|
||||
exsr11(0x10ff, 16);
|
||||
exsr11(0x20ff, 17);
|
||||
exsr11(0x30ff, 18);
|
||||
exsr11(0x40ff, 19);
|
||||
exsr11(0x50ff, 20);
|
||||
exsr11(0x60ff, 21);
|
||||
exsr11(0x70ff, 22);
|
||||
exsr11(0x80ff, 23);
|
||||
exsr11(0x90ff, 24);
|
||||
exsr11(0xa0ff, 25);
|
||||
exsr11(0xb0ff, 26);
|
||||
exsr11(0xc0ff, 27);
|
||||
exsr11(0xd0ff, 28);
|
||||
exsr11(0xe0ff, 29);
|
||||
exsr11(0xf0ff, 30);
|
||||
exsr11(0xffff, 31);
|
||||
exsr11(0xfff3, -16);
|
||||
exsr11(0xffffff, -33);
|
||||
|
||||
exsr12(0x10, 0);
|
||||
exsr12(0x20, 1);
|
||||
exsr12(0x30, 2);
|
||||
exsr12(0x40, 3);
|
||||
exsr12(0x50, 4);
|
||||
exsr12(0x60, 5);
|
||||
exsr12(0x70, 6);
|
||||
exsr12(0x80, 7);
|
||||
exsr12(0x90, 8);
|
||||
exsr12(0xa0, 9);
|
||||
exsr12(0xb0, 10);
|
||||
exsr12(0xc0, 11);
|
||||
exsr12(0xd0, 12);
|
||||
exsr12(0xe0, 13);
|
||||
exsr12(0xf0, 14);
|
||||
exsr12(0xff, 15);
|
||||
exsr12(0x10ff, 16);
|
||||
exsr12(0x20ff, 17);
|
||||
exsr12(0x30ff, 18);
|
||||
exsr12(0x40ff, 19);
|
||||
exsr12(0x50ff, 20);
|
||||
exsr12(0x60ff, 21);
|
||||
exsr12(0x70ff, 22);
|
||||
exsr12(0x80ff, 23);
|
||||
exsr12(0x90ff, 24);
|
||||
exsr12(0xa0ff, 25);
|
||||
exsr12(0xb0ff, 26);
|
||||
exsr12(0xc0ff, 27);
|
||||
exsr12(0xd0ff, 28);
|
||||
exsr12(0xe0ff, 29);
|
||||
exsr12(0xf0ff, 30);
|
||||
exsr12(0xffff, -32);
|
||||
|
||||
printf("all done\n");
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <glib.h>
|
||||
#include <pcre.h>
|
||||
|
||||
#include "aux.h"
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
#define BSDB(x...) fprintf(stderr, x)
|
||||
#else
|
||||
#define BSDB(x...) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
int mybsearch(void *base, unsigned int len, unsigned int size, void *key, unsigned int key_off, unsigned int key_size, int exact) {
|
||||
unsigned char *cbase = base;
|
||||
int pos;
|
||||
unsigned char *cur;
|
||||
int res;
|
||||
unsigned int num;
|
||||
|
||||
if (!len) {
|
||||
BSDB("zero length array\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pos = len / 2;
|
||||
num = pos;
|
||||
num += 3;
|
||||
num /= 2;
|
||||
pos--;
|
||||
if (pos < 0)
|
||||
pos = 0;
|
||||
|
||||
BSDB("starting pos=%u, num=%u\n", pos, num);
|
||||
|
||||
for (;;) {
|
||||
cur = cbase + (pos * size);
|
||||
res = memcmp(cur + key_off, key, key_size);
|
||||
BSDB("compare=%i\n", res);
|
||||
if (!res)
|
||||
return pos;
|
||||
if (!num) {
|
||||
BSDB("nothing found\n");
|
||||
if (exact)
|
||||
return -1;
|
||||
if (res > 0) /* cur > key */
|
||||
return -1 * pos - 1;
|
||||
return -1 * pos - 2;
|
||||
}
|
||||
|
||||
if (res < 0) { /* cur < key */
|
||||
pos += num;
|
||||
if (pos >= len)
|
||||
pos = len - 1;
|
||||
}
|
||||
else {
|
||||
pos -= num;
|
||||
if (pos < 0)
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
BSDB("new pos=%u\n", pos);
|
||||
|
||||
if (num == 1)
|
||||
num = 0;
|
||||
else {
|
||||
num++;
|
||||
num /= 2;
|
||||
}
|
||||
|
||||
BSDB("new num=%u\n", num);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GList *g_list_link(GList *list, GList *el) {
|
||||
el->prev = NULL;
|
||||
el->next = list;
|
||||
if (list)
|
||||
list->prev = el;
|
||||
return el;
|
||||
}
|
||||
|
||||
|
||||
GQueue *pcre_multi_match(pcre **re, pcre_extra **ree, const char *rex, const char *s, unsigned int num, parse_func f, void *p) {
|
||||
GQueue *q;
|
||||
const char *errptr;
|
||||
int erroff;
|
||||
unsigned int start, len;
|
||||
int ovec[60];
|
||||
int *ov;
|
||||
char **el;
|
||||
unsigned int i;
|
||||
void *ins;
|
||||
|
||||
if (!*re) {
|
||||
*re = pcre_compile(rex, PCRE_DOLLAR_ENDONLY | PCRE_DOTALL, &errptr, &erroff, NULL);
|
||||
if (!*re)
|
||||
return NULL;
|
||||
*ree = pcre_study(*re, 0, &errptr);
|
||||
}
|
||||
|
||||
q = g_queue_new();
|
||||
el = malloc(sizeof(*el) * num);
|
||||
|
||||
for (start = 0, len = strlen(s); pcre_exec(*re, *ree, s + start, len - start, 0, 0, ovec, ARRAY_SIZE(ovec)) > 0; start += ovec[1]) {
|
||||
for (i = 0; i < num; i++) {
|
||||
ov = ovec + 2 + i*2;
|
||||
el[i] = (ov[0] == -1) ? NULL : g_strndup(s + start + ov[0], ov[1] - ov[0]);
|
||||
}
|
||||
|
||||
if (!f(el, &ins, p))
|
||||
g_queue_push_tail(q, ins);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
if (el[i])
|
||||
free(el[i]);
|
||||
}
|
||||
}
|
||||
|
||||
free(el);
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
|
||||
void strmove(char **d, char **s) {
|
||||
if (*d)
|
||||
free(*d);
|
||||
*d = *s;
|
||||
*s = strdup("");
|
||||
}
|
||||
|
||||
void strdupfree(char **d, const char *s) {
|
||||
if (*d)
|
||||
free(*d);
|
||||
*d = strdup(s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if !GLIB_CHECK_VERSION(2,14,0)
|
||||
|
||||
void g_string_vprintf(GString *string, const gchar *format, va_list args) {
|
||||
char *s;
|
||||
int r;
|
||||
|
||||
r = vasprintf(&s, format, args);
|
||||
if (r < 0)
|
||||
return;
|
||||
|
||||
g_string_assign(string, s);
|
||||
free(s);
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,48 @@
|
||||
#ifndef __AUX_H__
|
||||
#define __AUX_H__
|
||||
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <fcntl.h>
|
||||
#include <glib.h>
|
||||
#include <pcre.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
|
||||
|
||||
#define OFFSET_OF(t,e) ((unsigned int) (unsigned long) &(((t *) 0)->e))
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
|
||||
#define ZERO(x) memset(&(x), 0, sizeof(x))
|
||||
|
||||
#define IPF "%u.%u.%u.%u"
|
||||
#define IPP(x) ((unsigned char *) (&(x)))[0], ((unsigned char *) (&(x)))[1], ((unsigned char *) (&(x)))[2], ((unsigned char *) (&(x)))[3]
|
||||
#define DF IPF ":%u"
|
||||
#define DP(x) IPP((x).sin_addr.s_addr), ntohs((x).sin_port)
|
||||
|
||||
#define NONBLOCK(x) fcntl(x, F_SETFL, O_NONBLOCK)
|
||||
#define REUSEADDR(x) do { int ONE = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &ONE, sizeof(ONE)); } while (0)
|
||||
|
||||
|
||||
|
||||
|
||||
typedef int (*parse_func)(char **, void **, void *);
|
||||
|
||||
int mybsearch(void *, unsigned int, unsigned int, void *, unsigned int, unsigned int, int);
|
||||
GList *g_list_link(GList *, GList *);
|
||||
GQueue *pcre_multi_match(pcre **, pcre_extra **, const char *, const char *, unsigned int, parse_func, void *);
|
||||
void strmove(char **, char **);
|
||||
void strdupfree(char **, const char *);
|
||||
|
||||
|
||||
#if !GLIB_CHECK_VERSION(2,14,0)
|
||||
void g_string_vprintf(GString *string, const gchar *format, va_list args);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,989 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <glib.h>
|
||||
#include <stdlib.h>
|
||||
#include <pcre.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "call.h"
|
||||
#include "poller.h"
|
||||
#include "aux.h"
|
||||
#include "log.h"
|
||||
#include "kernel.h"
|
||||
#include "control.h"
|
||||
#include "streambuf.h"
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
#define DBG(x...) mylog(LOG_DEBUG, x)
|
||||
#else
|
||||
#define DBG(x...) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
static pcre *info_re;
|
||||
static pcre_extra *info_ree;
|
||||
static pcre *streams_re;
|
||||
static pcre_extra *streams_ree;
|
||||
|
||||
|
||||
|
||||
|
||||
static char *rtp_codecs[] = {
|
||||
[0] = "G711u",
|
||||
[1] = "1016",
|
||||
[2] = "G721",
|
||||
[3] = "GSM",
|
||||
[4] = "G723",
|
||||
[5] = "DVI4",
|
||||
[6] = "DVI4",
|
||||
[7] = "LPC",
|
||||
[8] = "G711a",
|
||||
[9] = "G722",
|
||||
[10] = "L16",
|
||||
[11] = "L16",
|
||||
[14] = "MPA",
|
||||
[15] = "G728",
|
||||
[18] = "G729",
|
||||
[25] = "CelB",
|
||||
[26] = "JPEG",
|
||||
[28] = "nv",
|
||||
[31] = "H261",
|
||||
[32] = "MPV",
|
||||
[33] = "MP2T",
|
||||
[34] = "H263",
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void call_destroy(struct call *);
|
||||
static void unkernelize(struct peer *);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void stream_closed(int fd, void *p) {
|
||||
struct streamrelay *r = p;
|
||||
struct call *c;
|
||||
|
||||
c = r->up->up->call;
|
||||
|
||||
mylog(LOG_WARNING, "[%s] Read error on RTP socket", c->callid);
|
||||
|
||||
call_destroy(c);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void kernelize(struct callstream *c) {
|
||||
int i, j;
|
||||
struct peer *p, *pp;
|
||||
struct streamrelay *r, *rp;
|
||||
struct kernel_stream ks;
|
||||
|
||||
mylog(LOG_DEBUG, "[%s] Kernelizing RTP streams", c->call->callid);
|
||||
|
||||
ZERO(ks);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
p = &c->peers[i];
|
||||
pp = &c->peers[i ^ 1];
|
||||
|
||||
if (p->kernelized)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < 2; j++) {
|
||||
r = &p->rtps[j];
|
||||
rp = &pp->rtps[j];
|
||||
|
||||
if (!r->peer.ip || !r->peer.port)
|
||||
continue;
|
||||
|
||||
ZERO(r->kstats);
|
||||
|
||||
ks.local_port = r->localport;
|
||||
ks.src.ip = c->call->callmaster->ip;
|
||||
ks.src.port = rp->localport;
|
||||
ks.dest.ip = r->peer.ip;
|
||||
ks.dest.port = r->peer.port;
|
||||
ks.tos = c->call->callmaster->tos;
|
||||
|
||||
kernel_add_stream(c->call->callmaster->kernelfd, &ks, 0);
|
||||
}
|
||||
|
||||
p->kernelized = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static int stream_packet(struct streamrelay *r, char *b, int l, struct sockaddr_in *fsin) {
|
||||
struct streamrelay *p, *p2;
|
||||
struct peer *pe, *pe2;
|
||||
struct callstream *cs;
|
||||
int ret;
|
||||
struct sockaddr_in sin;
|
||||
struct msghdr mh;
|
||||
struct iovec iov;
|
||||
unsigned char buf[256];
|
||||
struct cmsghdr *ch;
|
||||
struct in_pktinfo *pi;
|
||||
struct call *c;
|
||||
struct callmaster *m;
|
||||
unsigned char cc;
|
||||
|
||||
pe = r->up;
|
||||
cs = pe->up;
|
||||
pe2 = &cs->peers[pe->idx ^ 1];
|
||||
p = &pe2->rtps[r->idx];
|
||||
c = cs->call;
|
||||
m = c->callmaster;
|
||||
|
||||
if (p->fd == -1) {
|
||||
mylog(LOG_WARNING, "[%s] RTP packet discarded from " DF, c->callid, DP(*fsin));
|
||||
r->stats.errors++;
|
||||
m->statsps.errors++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!pe->confirmed && pe->filled && r->idx == 0) {
|
||||
if (l < 2)
|
||||
goto skip;
|
||||
|
||||
if (!pe->codec) {
|
||||
cc = b[1];
|
||||
cc &= 0x7f;
|
||||
if (cc < ARRAY_SIZE(rtp_codecs))
|
||||
pe->codec = rtp_codecs[cc] ? : "unknown";
|
||||
else
|
||||
pe->codec = "unknown";
|
||||
}
|
||||
|
||||
mylog(LOG_DEBUG, "[%s] Confirmed peer information - " DF, c->callid, DP(*fsin));
|
||||
|
||||
p->peer.ip = fsin->sin_addr.s_addr;
|
||||
p->peer.port = ntohs(fsin->sin_port);
|
||||
|
||||
p2 = &p->up->rtps[p->idx ^ 1];
|
||||
p2->peer.ip = fsin->sin_addr.s_addr;
|
||||
p2->peer.port = p->peer.port + ((int) (p2->idx * 2) - 1);
|
||||
|
||||
pe->confirmed = 1;
|
||||
|
||||
|
||||
if (pe2->confirmed && pe2->filled)
|
||||
kernelize(cs);
|
||||
}
|
||||
|
||||
skip:
|
||||
if (!r->peer.ip || !r->peer.port)
|
||||
goto drop;
|
||||
|
||||
ZERO(sin);
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr.s_addr = r->peer.ip;
|
||||
sin.sin_port = htons(r->peer.port);
|
||||
|
||||
ZERO(iov);
|
||||
iov.iov_base = b;
|
||||
iov.iov_len = l;
|
||||
|
||||
ZERO(mh);
|
||||
mh.msg_name = &sin;
|
||||
mh.msg_namelen = sizeof(sin);
|
||||
mh.msg_iov = &iov;
|
||||
mh.msg_iovlen = 1;
|
||||
mh.msg_control = buf;
|
||||
mh.msg_controllen = sizeof(buf);
|
||||
|
||||
ch = CMSG_FIRSTHDR(&mh);
|
||||
ZERO(*ch);
|
||||
ch->cmsg_len = CMSG_LEN(sizeof(*pi));
|
||||
ch->cmsg_level = IPPROTO_IP;
|
||||
ch->cmsg_type = IP_PKTINFO;
|
||||
|
||||
pi = (void *) CMSG_DATA(ch);
|
||||
ZERO(*pi);
|
||||
pi->ipi_spec_dst.s_addr = m->ip;
|
||||
|
||||
mh.msg_controllen = CMSG_SPACE(sizeof(*pi));
|
||||
|
||||
ret = sendmsg(p->fd, &mh, 0);
|
||||
|
||||
if (ret == -1 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
r->stats.errors++;
|
||||
m->statsps.errors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
drop:
|
||||
r->stats.packets++;
|
||||
r->stats.bytes += l;
|
||||
m->statsps.packets++;
|
||||
m->statsps.bytes += l;
|
||||
r->last = m->poller->now;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void stream_readable(int fd, void *p) {
|
||||
struct streamrelay *r = p;
|
||||
char buf[1024];
|
||||
int ret;
|
||||
struct sockaddr_in sin;
|
||||
unsigned int sinlen;
|
||||
|
||||
for (;;) {
|
||||
sinlen = sizeof(sin);
|
||||
ret = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) &sin, &sinlen);
|
||||
|
||||
if (ret == 0)
|
||||
goto err;
|
||||
else if (ret < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
break;
|
||||
err:
|
||||
stream_closed(fd, r);
|
||||
break;
|
||||
}
|
||||
|
||||
if (stream_packet(r, buf, ret, &sin)) {
|
||||
mylog(LOG_WARNING, "Write error on RTP socket");
|
||||
call_destroy(r->up->up->call);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static int info_parse_func(char **a, void **ret, void *p) {
|
||||
GHashTable *h = p;
|
||||
|
||||
g_hash_table_replace(h, strdup(a[0]), a[1] ? strdup(a[1]) : NULL);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static GHashTable *info_parse(const char *s, GHashTable **h) {
|
||||
GQueue *q;
|
||||
|
||||
if (!*h)
|
||||
*h = g_hash_table_new_full(g_str_hash, g_str_equal, free, free);
|
||||
|
||||
q = pcre_multi_match(&info_re, &info_ree, "^([^:,]+)(?::(.*?))?(?:$|,)", s, 2, info_parse_func, *h);
|
||||
g_queue_free(q);
|
||||
|
||||
return *h;
|
||||
}
|
||||
|
||||
|
||||
static int streams_parse_func(char **a, void **ret, void *p) {
|
||||
struct stream *st;
|
||||
|
||||
st = malloc(sizeof(*st));
|
||||
ZERO(*st);
|
||||
|
||||
st->ip = inet_addr(a[0]);
|
||||
st->port = atoi(a[1]);
|
||||
st->mediatype = strdup(a[2] ? : "");
|
||||
|
||||
if (st->ip == -1)
|
||||
goto fail;
|
||||
if (!st->port)
|
||||
goto fail;
|
||||
|
||||
*ret = st;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
free(st);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static GQueue *streams_parse(const char *s) {
|
||||
return pcre_multi_match(&streams_re, &streams_ree, "^([\\d.]+):(\\d+)(?::(.*?))?(?:$|,)", s, 3, streams_parse_func, NULL);
|
||||
}
|
||||
|
||||
static void streams_free(GQueue *q) {
|
||||
struct stream *s;
|
||||
|
||||
while (q->head) {
|
||||
s = g_queue_pop_head(q);
|
||||
free(s->mediatype);
|
||||
free(s);
|
||||
}
|
||||
|
||||
g_queue_free(q);
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct iterator_helper {
|
||||
GList *del;
|
||||
struct streamrelay *ports[0x10000];
|
||||
};
|
||||
|
||||
|
||||
static void call_timer_iterator(void *key, void *val, void *ptr) {
|
||||
struct call *c = val;
|
||||
struct iterator_helper *hlp = ptr;
|
||||
GList *it;
|
||||
struct callstream *cs;
|
||||
int i;
|
||||
struct peer *p;
|
||||
struct poller *po;
|
||||
struct callmaster *cm;
|
||||
unsigned int check;
|
||||
|
||||
if (!c->callstreams->head)
|
||||
goto drop;
|
||||
|
||||
cm = c->callmaster;
|
||||
po = cm->poller;
|
||||
|
||||
for (it = c->callstreams->head; it; it = it->next) {
|
||||
cs = it->data;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
p = &cs->peers[i];
|
||||
|
||||
hlp->ports[p->rtps[0].localport] = &p->rtps[0];
|
||||
hlp->ports[p->rtps[1].localport] = &p->rtps[1];
|
||||
|
||||
check = cm->timeout;
|
||||
if (!p->rtps[0].peer.ip || !p->rtps[0].peer.port)
|
||||
check = cm->silent_timeout;
|
||||
|
||||
if (po->now - p->rtps[0].last < check)
|
||||
goto good;
|
||||
}
|
||||
}
|
||||
|
||||
mylog(LOG_INFO, "[%s] Closing call due to timeout", c->callid);
|
||||
|
||||
drop:
|
||||
hlp->del = g_list_prepend(hlp->del, c);
|
||||
return;
|
||||
|
||||
good:
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
#define DS(x) do { \
|
||||
if (ke->stats.x < sr->kstats.x) \
|
||||
d = 0; \
|
||||
else \
|
||||
d = ke->stats.x - sr->kstats.x; \
|
||||
sr->stats.x += d; \
|
||||
m->statsps.x += d; \
|
||||
} while (0)
|
||||
static void callmaster_timer(void *ptr) {
|
||||
struct callmaster *m = ptr;
|
||||
struct iterator_helper hlp;
|
||||
GList *i;
|
||||
struct call *c;
|
||||
struct mediaproxy_list_entry *ke;
|
||||
struct streamrelay *sr;
|
||||
struct poller *po;
|
||||
u_int64_t d;
|
||||
|
||||
ZERO(hlp);
|
||||
po = m->poller;
|
||||
|
||||
g_hash_table_foreach(m->callhash, call_timer_iterator, &hlp);
|
||||
|
||||
memcpy(&m->stats, &m->statsps, sizeof(m->stats));
|
||||
ZERO(m->statsps);
|
||||
|
||||
i = kernel_list(m->kernelid);
|
||||
while (i) {
|
||||
ke = i->data;
|
||||
|
||||
sr = hlp.ports[ke->target.target_port];
|
||||
if (!sr)
|
||||
goto next;
|
||||
|
||||
DS(packets);
|
||||
DS(bytes);
|
||||
DS(errors);
|
||||
|
||||
if (ke->stats.packets != sr->kstats.packets)
|
||||
sr->last = po->now;
|
||||
|
||||
memcpy(&sr->kstats, &ke->stats, sizeof(sr->kstats));
|
||||
|
||||
next:
|
||||
free(ke);
|
||||
i = g_list_delete_link(i, i);
|
||||
}
|
||||
|
||||
for (i = hlp.del; i; i = i->next) {
|
||||
c = i->data;
|
||||
call_destroy(c);
|
||||
}
|
||||
|
||||
g_list_free(i);
|
||||
}
|
||||
#undef DS
|
||||
|
||||
|
||||
struct callmaster *callmaster_new(struct poller *p) {
|
||||
struct callmaster *c;
|
||||
|
||||
c = malloc(sizeof(*c));
|
||||
ZERO(*c);
|
||||
|
||||
c->callhash = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
if (!c->callhash)
|
||||
goto fail;
|
||||
c->poller = p;
|
||||
|
||||
poller_timer(p, callmaster_timer, c);
|
||||
|
||||
return c;
|
||||
|
||||
fail:
|
||||
free(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int get_port(struct streamrelay *r, u_int16_t p) {
|
||||
int fd;
|
||||
struct sockaddr_in sin;
|
||||
|
||||
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
NONBLOCK(fd);
|
||||
REUSEADDR(fd);
|
||||
setsockopt(fd, SOL_IP, IP_TOS, &r->up->up->call->callmaster->tos, sizeof(r->up->up->call->callmaster->tos));
|
||||
|
||||
ZERO(sin);
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(p);
|
||||
if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)))
|
||||
goto fail;
|
||||
|
||||
r->fd = fd;
|
||||
r->localport = p;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void get_port_pair(struct peer *p) {
|
||||
struct call *c;
|
||||
struct callmaster *m;
|
||||
struct streamrelay *a, *b;
|
||||
u_int16_t port;
|
||||
|
||||
c = p->up->call;
|
||||
m = c->callmaster;
|
||||
a = &p->rtps[0];
|
||||
b = &p->rtps[1];
|
||||
|
||||
assert(a->fd == -1 && b->fd == -1);
|
||||
|
||||
port = m->lastport + 1;
|
||||
|
||||
for (;;) {
|
||||
if (port < 1024)
|
||||
port = 1024;
|
||||
|
||||
if (port == m->lastport)
|
||||
goto fail;
|
||||
|
||||
if ((port & 1))
|
||||
goto next;
|
||||
|
||||
if (get_port(a, port))
|
||||
goto next;
|
||||
|
||||
port++;
|
||||
if (get_port(b, port))
|
||||
goto tryagain;
|
||||
|
||||
break;
|
||||
|
||||
tryagain:
|
||||
close(a->fd);
|
||||
next:
|
||||
port++;
|
||||
}
|
||||
|
||||
m->lastport = port;
|
||||
mylog(LOG_DEBUG, "[%s] Opened ports %u/%u for RTP", c->callid, a->localport, b->localport);
|
||||
return;
|
||||
|
||||
fail:
|
||||
mylog(LOG_ERR, "[%s] Failed to get RTP port pair", c->callid);
|
||||
if (a->fd != -1)
|
||||
close(a->fd);
|
||||
if (b->fd != -1)
|
||||
close(b->fd);
|
||||
a->fd = b->fd = -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int setup_peer(struct peer *p, struct stream *s, char *tag) {
|
||||
struct streamrelay *a, *b;
|
||||
|
||||
a = &p->rtps[0];
|
||||
b = &p->rtps[1];
|
||||
|
||||
if (a->peer.ip != s->ip || a->peer.port != b->peer.port) {
|
||||
p->confirmed = 0;
|
||||
if (p->kernelized)
|
||||
unkernelize(p);
|
||||
}
|
||||
|
||||
a->peer.ip = b->peer.ip = s->ip;
|
||||
a->peer.port = b->peer.port = s->port;
|
||||
if (b->peer.port)
|
||||
b->peer.port++;
|
||||
|
||||
strdupfree(&p->mediatype, s->mediatype);
|
||||
strdupfree(&p->tag, tag);
|
||||
p->filled = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void steal_peer(struct peer *p, struct streamrelay *r) {
|
||||
struct peer *s = r->up;
|
||||
int i;
|
||||
struct poller_item pi;
|
||||
struct streamrelay *sr, *srs;
|
||||
struct call *c;
|
||||
struct poller *po;
|
||||
|
||||
ZERO(pi);
|
||||
c = s->up->call;
|
||||
po = c->callmaster->poller;
|
||||
|
||||
mylog(LOG_DEBUG, "[%s] Re-using existing open RTP ports", c->callid);
|
||||
|
||||
if (s->kernelized)
|
||||
unkernelize(s);
|
||||
|
||||
p->filled = 1;
|
||||
strmove(&p->mediatype, &s->mediatype);
|
||||
strmove(&p->tag, &s->tag);
|
||||
//p->kernelized = s->kernelized;
|
||||
//s->kernelized = 0;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
sr = &p->rtps[i];
|
||||
srs = &s->rtps[i];
|
||||
|
||||
if (sr->fd != -1) {
|
||||
close(sr->fd);
|
||||
poller_del_item(po, sr->fd);
|
||||
}
|
||||
|
||||
sr->fd = srs->fd;
|
||||
|
||||
sr->peer.ip = srs->peer.ip;
|
||||
sr->peer.port = srs->peer.port;
|
||||
|
||||
sr->localport = srs->localport;
|
||||
|
||||
|
||||
srs->fd = -1;
|
||||
srs->peer.ip = 0;
|
||||
srs->peer.port = 0;
|
||||
srs->localport = 0;
|
||||
|
||||
pi.fd = sr->fd;
|
||||
pi.ptr = sr;
|
||||
pi.readable = stream_readable;
|
||||
pi.closed = stream_closed;
|
||||
|
||||
poller_update_item(po, &pi);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void callstream_init(struct callstream *s, struct call *ca) {
|
||||
int i, j;
|
||||
struct peer *p;
|
||||
struct streamrelay *r;
|
||||
struct poller_item pi;
|
||||
struct poller *po;
|
||||
|
||||
po = ca->callmaster->poller;
|
||||
|
||||
ZERO(*s);
|
||||
ZERO(pi);
|
||||
|
||||
s->call = ca;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
p = &s->peers[i];
|
||||
|
||||
p->idx = i;
|
||||
p->up = s;
|
||||
p->tag = strdup("");
|
||||
p->mediatype = strdup("");
|
||||
|
||||
for (j = 0; j < 2; j++) {
|
||||
r = &p->rtps[j];
|
||||
|
||||
r->fd = -1;
|
||||
r->idx = j;
|
||||
r->up = p;
|
||||
r->last = po->now;
|
||||
}
|
||||
|
||||
get_port_pair(p);
|
||||
|
||||
for (j = 0; j < 2; j++) {
|
||||
r = &p->rtps[j];
|
||||
|
||||
pi.fd = r->fd;
|
||||
pi.ptr = r;
|
||||
pi.readable = stream_readable;
|
||||
pi.closed = stream_closed;
|
||||
|
||||
poller_add_item(po, &pi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static unsigned int call_streams(struct call *c, GQueue *s, char *tag, int opmode) {
|
||||
GQueue *q;
|
||||
GList *i, *l;
|
||||
struct stream *t;
|
||||
int x;
|
||||
struct streamrelay *r;
|
||||
struct callstream *cs;
|
||||
struct peer *p;
|
||||
unsigned int ret;
|
||||
|
||||
q = g_queue_new(); /* new callstreams list */
|
||||
|
||||
if (!tag)
|
||||
tag = "";
|
||||
|
||||
for (i = s->head; i; i = i->next) {
|
||||
t = i->data;
|
||||
|
||||
p = NULL;
|
||||
|
||||
if (!opmode) {
|
||||
DBG("creating new callstream");
|
||||
cs = malloc(sizeof(*cs));
|
||||
callstream_init(cs, c);
|
||||
p = &cs->peers[0];
|
||||
}
|
||||
else {
|
||||
l = c->callstreams->head;
|
||||
if (!l) {
|
||||
mylog(LOG_WARNING, "[%s] Got LOOKUP, but no callstreams found", c->callid);
|
||||
break;
|
||||
}
|
||||
cs = l->data;
|
||||
#if 0
|
||||
if (cs->peers[1].filled) {
|
||||
mylog(LOG_WARNING, "[%s] Got LOOKUP, but no incomplete callstreams found", c->callid);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
g_queue_delete_link(c->callstreams, l);
|
||||
p = &cs->peers[1];
|
||||
}
|
||||
|
||||
|
||||
for (l = c->callstreams->head; l; l = l->next) {
|
||||
cs = l->data;
|
||||
for (x = 0; x < 2; x++) {
|
||||
r = &cs->peers[x].rtps[0];
|
||||
if (r->peer.ip != t->ip)
|
||||
continue;
|
||||
if (r->peer.port != t->port)
|
||||
continue;
|
||||
if (strcmp(cs->peers[x].tag, tag))
|
||||
continue;
|
||||
DBG("found existing call stream to steal");
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
/* not found */
|
||||
setup_peer(p, t, tag);
|
||||
g_queue_push_tail(q, p->up);
|
||||
continue;
|
||||
|
||||
found:
|
||||
steal_peer(p, r);
|
||||
g_queue_push_tail(q, p->up);
|
||||
}
|
||||
|
||||
ret = q->length;
|
||||
|
||||
if (!q->head)
|
||||
g_queue_free(q);
|
||||
else {
|
||||
if (c->callstreams->head) {
|
||||
q->tail->next = c->callstreams->head;
|
||||
c->callstreams->head->prev = q->tail;
|
||||
q->tail = c->callstreams->tail;
|
||||
q->length += c->callstreams->length;
|
||||
c->callstreams->head = c->callstreams->tail = NULL;
|
||||
c->callstreams->length = 0;
|
||||
}
|
||||
g_queue_free(c->callstreams);
|
||||
c->callstreams = q;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void unkernelize(struct peer *p) {
|
||||
struct streamrelay *r;
|
||||
int i;
|
||||
|
||||
if (!p->kernelized)
|
||||
return;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
r = &p->rtps[i];
|
||||
|
||||
kernel_del_stream(p->up->call->callmaster->kernelfd, r->localport);
|
||||
|
||||
}
|
||||
|
||||
p->kernelized = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void kill_callstream(struct callstream *s) {
|
||||
int i, j;
|
||||
struct peer *p;
|
||||
struct streamrelay *r;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
p = &s->peers[i];
|
||||
|
||||
unkernelize(p);
|
||||
|
||||
free(p->tag);
|
||||
free(p->mediatype);
|
||||
|
||||
for (j = 0; j < 2; j++) {
|
||||
r = &p->rtps[j];
|
||||
|
||||
if (r->fd != -1)
|
||||
close(r->fd);
|
||||
poller_del_item(s->call->callmaster->poller, r->fd);
|
||||
}
|
||||
}
|
||||
|
||||
free(s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void call_destroy(struct call *c) {
|
||||
struct callmaster *m = c->callmaster;
|
||||
struct callstream *s;
|
||||
|
||||
g_hash_table_remove(m->callhash, c->callid);
|
||||
|
||||
free(c->callid);
|
||||
g_hash_table_destroy(c->infohash);
|
||||
if (c->calling_agent)
|
||||
free(c->calling_agent);
|
||||
if (c->called_agent)
|
||||
free(c->called_agent);
|
||||
|
||||
while (c->callstreams->head) {
|
||||
s = g_queue_pop_head(c->callstreams);
|
||||
kill_callstream(s);
|
||||
}
|
||||
g_queue_free(c->callstreams);
|
||||
|
||||
free(c);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static char *streams_print(GQueue *s, unsigned int num, unsigned int off) {
|
||||
GString *o;
|
||||
int i;
|
||||
GList *l;
|
||||
struct callstream *t;
|
||||
struct streamrelay *x;
|
||||
|
||||
o = g_string_new("");
|
||||
|
||||
if (!s->head)
|
||||
goto out;
|
||||
|
||||
t = s->head->data;
|
||||
g_string_append_printf(o, IPF, IPP(t->call->callmaster->ip));
|
||||
|
||||
for (i = 0, l = s->head; i < num && l; i++, l = l->next) {
|
||||
t = l->data;
|
||||
x = &t->peers[off].rtps[0];
|
||||
g_string_append_printf(o, " %u", x->localport);
|
||||
}
|
||||
|
||||
out:
|
||||
g_string_append(o, "\n");
|
||||
|
||||
return g_string_free(o, FALSE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
char *call_request(const char **o, struct callmaster *m) {
|
||||
struct call *c;
|
||||
GQueue *s;
|
||||
unsigned int num;
|
||||
|
||||
c = g_hash_table_lookup(m->callhash, o[2]);
|
||||
if (!c) {
|
||||
mylog(LOG_NOTICE, "[%s] Creating new call", o[2]);
|
||||
c = malloc(sizeof(*c));
|
||||
ZERO(*c);
|
||||
c->callmaster = m;
|
||||
c->callid = strdup(o[2]);
|
||||
c->callstreams = g_queue_new();
|
||||
c->created = m->poller->now;
|
||||
g_hash_table_insert(m->callhash, c->callid, c);
|
||||
}
|
||||
|
||||
strdupfree(&c->calling_agent, o[9] ? : "UNKNOWN");
|
||||
info_parse(o[10], &c->infohash);
|
||||
s = streams_parse(o[3]);
|
||||
num = call_streams(c, s, g_hash_table_lookup(c->infohash, "fromtag"), 0);
|
||||
streams_free(s);
|
||||
|
||||
return streams_print(c->callstreams, num, 0);
|
||||
}
|
||||
|
||||
char *call_lookup(const char **o, struct callmaster *m) {
|
||||
struct call *c;
|
||||
GQueue *s;
|
||||
unsigned int num;
|
||||
|
||||
c = g_hash_table_lookup(m->callhash, o[2]);
|
||||
if (!c) {
|
||||
mylog(LOG_WARNING, "[%s] Got LOOKUP for unknown call-id", o[2]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strdupfree(&c->called_agent, o[9] ? : "UNKNOWN");
|
||||
info_parse(o[10], &c->infohash);
|
||||
s = streams_parse(o[3]);
|
||||
num = call_streams(c, s, g_hash_table_lookup(c->infohash, "totag"), 1);
|
||||
streams_free(s);
|
||||
|
||||
return streams_print(c->callstreams, num, 1);
|
||||
}
|
||||
|
||||
void call_delete(const char **o, struct callmaster *m) {
|
||||
struct call *c;
|
||||
|
||||
c = g_hash_table_lookup(m->callhash, o[12]);
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
call_destroy(c);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void call_status_iterator(void *key, void *val, void *ptr) {
|
||||
struct call *c = val;
|
||||
struct control_stream *s = ptr;
|
||||
GList *l;
|
||||
struct callstream *cs;
|
||||
struct peer *p;
|
||||
struct streamrelay *r1, *r2;
|
||||
struct streamrelay *rx1, *rx2;
|
||||
struct callmaster *m;
|
||||
|
||||
m = c->callmaster;
|
||||
|
||||
streambuf_printf(s->outbuf, "session %s %s %s %s %s %i\n",
|
||||
c->callid,
|
||||
(char *) g_hash_table_lookup(c->infohash, "from"),
|
||||
(char *) g_hash_table_lookup(c->infohash, "to"),
|
||||
c->calling_agent, c->called_agent,
|
||||
(int) (m->poller->now - c->created));
|
||||
|
||||
for (l = c->callstreams->head; l; l = l->next) {
|
||||
cs = l->data;
|
||||
|
||||
p = &cs->peers[0];
|
||||
r1 = &p->rtps[0];
|
||||
r2 = &cs->peers[1].rtps[0];
|
||||
rx1 = &p->rtps[1];
|
||||
rx2 = &cs->peers[1].rtps[1];
|
||||
|
||||
if (r1->fd == -1 || r2->fd == -1)
|
||||
continue;
|
||||
|
||||
streambuf_printf(s->outbuf, "stream " IPF ":%u " IPF ":%u " IPF ":%u %llu/%llu/%llu %s %s %s %i\n",
|
||||
IPP(r1->peer.ip), r1->peer.port,
|
||||
IPP(r2->peer.ip), r2->peer.port,
|
||||
IPP(m->ip), r1->localport,
|
||||
(long long unsigned int) r1->stats.bytes + rx1->stats.bytes,
|
||||
(long long unsigned int) r2->stats.bytes + rx2->stats.bytes,
|
||||
(long long unsigned int) r1->stats.bytes + rx1->stats.bytes + r2->stats.bytes + rx2->stats.bytes,
|
||||
"active",
|
||||
p->codec ? : "unknown",
|
||||
p->mediatype, (int) (m->poller->now - r1->last));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void calls_status(struct callmaster *m, struct control_stream *s) {
|
||||
streambuf_printf(s->outbuf, "proxy %u %llu/%llu/%llu\n",
|
||||
g_hash_table_size(m->callhash),
|
||||
(long long unsigned int) m->stats.bytes,
|
||||
(long long unsigned int) m->stats.bytes - m->stats.errors,
|
||||
(long long unsigned int) m->stats.bytes * 2 - m->stats.errors);
|
||||
|
||||
g_hash_table_foreach(m->callhash, call_status_iterator, s);
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
#ifndef __CALL_H__
|
||||
#define __CALL_H__
|
||||
|
||||
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include "ipt_MEDIAPROXY.h"
|
||||
|
||||
|
||||
|
||||
struct poller;
|
||||
struct control_stream;
|
||||
|
||||
|
||||
|
||||
struct peer;
|
||||
struct callstream;
|
||||
struct call;
|
||||
struct callmaster;
|
||||
|
||||
|
||||
|
||||
|
||||
struct stream {
|
||||
u_int32_t ip;
|
||||
u_int16_t port;
|
||||
char *mediatype;
|
||||
};
|
||||
struct streamrelay {
|
||||
int fd;
|
||||
struct stream peer;
|
||||
u_int16_t localport;
|
||||
unsigned char idx;
|
||||
struct peer *up;
|
||||
struct mediaproxy_stats stats;
|
||||
struct mediaproxy_stats kstats;
|
||||
time_t last;
|
||||
};
|
||||
struct peer {
|
||||
struct streamrelay rtps[2];
|
||||
char *tag;
|
||||
char *mediatype;
|
||||
char *codec;
|
||||
unsigned char idx;
|
||||
struct callstream *up;
|
||||
int kernelized:1;
|
||||
int filled:1;
|
||||
int confirmed:1;
|
||||
};
|
||||
struct callstream {
|
||||
struct peer peers[2];
|
||||
struct call *call;
|
||||
};
|
||||
|
||||
struct call {
|
||||
struct callmaster *callmaster;
|
||||
|
||||
GQueue *callstreams;
|
||||
|
||||
char *callid;
|
||||
time_t created;
|
||||
char *calling_agent;
|
||||
char *called_agent;
|
||||
GHashTable *infohash;
|
||||
};
|
||||
|
||||
struct callmaster {
|
||||
GHashTable *callhash;
|
||||
u_int16_t lastport;
|
||||
struct mediaproxy_stats statsps;
|
||||
struct mediaproxy_stats stats;
|
||||
|
||||
struct poller *poller;
|
||||
int kernelfd;
|
||||
unsigned int kernelid;
|
||||
u_int32_t ip;
|
||||
unsigned int timeout;
|
||||
unsigned int silent_timeout;
|
||||
unsigned char tos;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
struct callmaster *callmaster_new(struct poller *);
|
||||
|
||||
|
||||
|
||||
char *call_request(const char **, struct callmaster *);
|
||||
char *call_lookup(const char **, struct callmaster *);
|
||||
void call_delete(const char **, struct callmaster *);
|
||||
|
||||
void calls_status(struct callmaster *, struct control_stream *);
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,250 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <pcre.h>
|
||||
|
||||
#include "control.h"
|
||||
#include "poller.h"
|
||||
#include "aux.h"
|
||||
#include "streambuf.h"
|
||||
#include "log.h"
|
||||
#include "build_time.h"
|
||||
#include "call.h"
|
||||
|
||||
|
||||
|
||||
static pcre *parse_re;
|
||||
static pcre_extra *parse_ree;
|
||||
|
||||
|
||||
|
||||
|
||||
static void control_stream_closed(int fd, void *p) {
|
||||
struct control_stream *s = p;
|
||||
struct control *c;
|
||||
|
||||
mylog(LOG_INFO, "Control connection from " DF " closed", DP(s->inaddr));
|
||||
|
||||
c = s->control;
|
||||
|
||||
c->stream_head = g_list_remove_link(c->stream_head, &s->link);
|
||||
|
||||
close(fd);
|
||||
if (poller_del_item(s->poller, fd))
|
||||
abort();
|
||||
|
||||
streambuf_destroy(s->inbuf);
|
||||
streambuf_destroy(s->outbuf);
|
||||
free(s);
|
||||
}
|
||||
|
||||
|
||||
static void control_list(struct control *c, struct control_stream *s) {
|
||||
struct control_stream *i;
|
||||
|
||||
for (i = (void *) c->stream_head; i; i = (void *) i->link.next)
|
||||
streambuf_printf(s->outbuf, DF "\n", DP(s->inaddr));
|
||||
|
||||
streambuf_printf(s->outbuf, "End.\n");
|
||||
}
|
||||
|
||||
|
||||
static int control_stream_parse(struct control_stream *s, char *line) {
|
||||
const char *errptr;
|
||||
int erroff;
|
||||
int ovec[60];
|
||||
int ret;
|
||||
const char **out;
|
||||
struct control *c = s->control;
|
||||
char *output = NULL;
|
||||
|
||||
if (!parse_re) {
|
||||
parse_re = pcre_compile(
|
||||
/* reqtype callid streams ip fromdom fromtype todom totype agent info |reqtype callid info | reqtype */
|
||||
"^(?:(request|lookup)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+info=(\\S*)|(delete)\\s+(\\S+)\\s+info=(\\S*)|(build|version|controls|quit|exit|status))$",
|
||||
PCRE_DOLLAR_ENDONLY | PCRE_DOTALL, &errptr, &erroff, NULL);
|
||||
parse_ree = pcre_study(parse_re, 0, &errptr);
|
||||
}
|
||||
|
||||
ret = pcre_exec(parse_re, parse_ree, line, strlen(line), 0, 0, ovec, ARRAY_SIZE(ovec));
|
||||
if (ret <= 0) {
|
||||
mylog(LOG_WARNING, "Unable to parse command line from " DF ": %s", DP(s->inaddr), line);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mylog(LOG_INFO, "Got valid command from " DF ": %s", DP(s->inaddr), line);
|
||||
|
||||
pcre_get_substring_list(line, ovec, ret, &out);
|
||||
|
||||
|
||||
if (!strcmp(out[1], "request"))
|
||||
output = call_request(out, c->callmaster);
|
||||
else if (!strcmp(out[1], "lookup"))
|
||||
output = call_lookup(out, c->callmaster);
|
||||
else if (!strcmp(out[11], "delete"))
|
||||
call_delete(out, c->callmaster);
|
||||
else if (!strcmp(out[14], "status"))
|
||||
calls_status(c->callmaster, s);
|
||||
else if (!strcmp(out[14], "build") | !strcmp(out[14], "version"))
|
||||
streambuf_printf(s->outbuf, "Build: %s\n", BUILD_TIME);
|
||||
else if (!strcmp(out[14], "controls"))
|
||||
control_list(c, s);
|
||||
else if (!strcmp(out[14], "quit") || !strcmp(out[14], "exit"))
|
||||
;
|
||||
|
||||
if (output) {
|
||||
streambuf_write(s->outbuf, output, strlen(output));
|
||||
free(output);
|
||||
}
|
||||
|
||||
pcre_free(out);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void control_stream_timer(int fd, void *p) {
|
||||
struct control_stream *s = p;
|
||||
struct poller *o = s->poller;
|
||||
|
||||
if ((o->now - s->inbuf->active) >= 60 || (o->now - s->outbuf->active) >= 60)
|
||||
control_stream_closed(s->fd, s);
|
||||
}
|
||||
|
||||
|
||||
static void control_stream_readable(int fd, void *p) {
|
||||
struct control_stream *s = p;
|
||||
char *line;
|
||||
int ret;
|
||||
|
||||
if (streambuf_readable(s->inbuf))
|
||||
goto close;
|
||||
|
||||
while ((line = streambuf_getline(s->inbuf))) {
|
||||
mylog(LOG_DEBUG, "Got control line from " DF ": %s", DP(s->inaddr), line);
|
||||
ret = control_stream_parse(s, line);
|
||||
free(line);
|
||||
if (ret)
|
||||
goto close;
|
||||
}
|
||||
|
||||
if (streambuf_bufsize(s->inbuf) > 1024) {
|
||||
mylog(LOG_WARNING, "Buffer length exceeded in control connection from " DF, DP(s->inaddr));
|
||||
goto close;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
close:
|
||||
control_stream_closed(fd, s);
|
||||
}
|
||||
|
||||
static void control_stream_writeable(int fd, void *p) {
|
||||
struct control_stream *s = p;
|
||||
|
||||
if (streambuf_writeable(s->outbuf))
|
||||
control_stream_closed(fd, s);
|
||||
}
|
||||
|
||||
static void control_closed(int fd, void *p) {
|
||||
abort();
|
||||
}
|
||||
|
||||
static void control_incoming(int fd, void *p) {
|
||||
int nfd;
|
||||
struct control *c = p;
|
||||
struct control_stream *s;
|
||||
struct poller_item i;
|
||||
struct sockaddr_in sin;
|
||||
socklen_t sinl;
|
||||
|
||||
sinl = sizeof(sin);
|
||||
nfd = accept(fd, (struct sockaddr *) &sin, &sinl);
|
||||
if (nfd == -1)
|
||||
return;
|
||||
NONBLOCK(nfd);
|
||||
|
||||
mylog(LOG_INFO, "New control connection from " DF, DP(sin));
|
||||
|
||||
s = malloc(sizeof(*s));
|
||||
ZERO(*s);
|
||||
|
||||
ZERO(i);
|
||||
i.fd = nfd;
|
||||
i.closed = control_stream_closed;
|
||||
i.readable = control_stream_readable;
|
||||
i.writeable = control_stream_writeable;
|
||||
i.timer = control_stream_timer;
|
||||
i.ptr = s;
|
||||
if (poller_add_item(c->poller, &i))
|
||||
goto fail;
|
||||
s->fd = nfd;
|
||||
s->control = c;
|
||||
s->poller = c->poller;
|
||||
s->inbuf = streambuf_new(c->poller, nfd);
|
||||
s->outbuf = streambuf_new(c->poller, nfd);
|
||||
memcpy(&s->inaddr, &sin, sizeof(s->inaddr));
|
||||
|
||||
c->stream_head = g_list_link(c->stream_head, &s->link);
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
free(s);
|
||||
close(nfd);
|
||||
}
|
||||
|
||||
|
||||
struct control *control_new(struct poller *p, u_int32_t ip, u_int16_t port, struct callmaster *m) {
|
||||
int fd;
|
||||
struct control *c;
|
||||
struct poller_item i;
|
||||
struct sockaddr_in sin;
|
||||
|
||||
if (!p)
|
||||
return NULL;
|
||||
if (!m)
|
||||
return NULL;
|
||||
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
|
||||
NONBLOCK(fd);
|
||||
REUSEADDR(fd);
|
||||
|
||||
ZERO(sin);
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr.s_addr = ip;
|
||||
sin.sin_port = htons(port);
|
||||
if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)))
|
||||
goto fail;
|
||||
|
||||
if (listen(fd, 5))
|
||||
goto fail;
|
||||
|
||||
|
||||
c = malloc(sizeof(*c));
|
||||
ZERO(*c);
|
||||
|
||||
c->fd = fd;
|
||||
c->poller = p;
|
||||
c->callmaster = m;
|
||||
|
||||
ZERO(i);
|
||||
i.fd = fd;
|
||||
i.closed = control_closed;
|
||||
i.readable = control_incoming;
|
||||
i.ptr = c;
|
||||
if (poller_add_item(p, &i))
|
||||
goto fail2;
|
||||
|
||||
return c;
|
||||
|
||||
fail2:
|
||||
free(c);
|
||||
fail:
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
#ifndef __CONTROL_H__
|
||||
#define __CONTROL_H__
|
||||
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <glib.h>
|
||||
|
||||
|
||||
|
||||
struct poller;
|
||||
struct control;
|
||||
struct streambuf;
|
||||
struct callmaster;
|
||||
|
||||
|
||||
|
||||
struct control_stream {
|
||||
GList link; /* must be first */
|
||||
|
||||
int fd;
|
||||
struct streambuf *inbuf;
|
||||
struct streambuf *outbuf;
|
||||
struct sockaddr_in inaddr;
|
||||
|
||||
struct control *control;
|
||||
struct poller *poller;
|
||||
};
|
||||
|
||||
|
||||
struct control {
|
||||
int fd;
|
||||
|
||||
GList *stream_head;
|
||||
|
||||
struct poller *poller;
|
||||
struct callmaster *callmaster;
|
||||
};
|
||||
|
||||
|
||||
struct control *control_new(struct poller *, u_int32_t, u_int16_t, struct callmaster *);
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,128 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "ipt_MEDIAPROXY.h"
|
||||
|
||||
#include "aux.h"
|
||||
#include "kernel.h"
|
||||
|
||||
|
||||
|
||||
|
||||
#if 1
|
||||
#define PREFIX "/proc/mediaproxy"
|
||||
#else
|
||||
#define PREFIX "/tmp/mediaproxy"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int kernel_create_table(unsigned int id) {
|
||||
char str[64];
|
||||
int fd;
|
||||
int i;
|
||||
|
||||
fd = open(PREFIX "/control", O_WRONLY | O_TRUNC);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
sprintf(str, "add %u\n", id);
|
||||
i = write(fd, str, strlen(str));
|
||||
if (i == -1)
|
||||
goto fail;
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int kernel_open_table(unsigned int id) {
|
||||
char str[64];
|
||||
int fd;
|
||||
struct mediaproxy_message msg;
|
||||
int i;
|
||||
|
||||
sprintf(str, PREFIX "/%u/control", id);
|
||||
fd = open(str, O_WRONLY | O_TRUNC);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
ZERO(msg);
|
||||
msg.cmd = MMG_NOOP;
|
||||
i = write(fd, &msg, sizeof(msg));
|
||||
if (i <= 0)
|
||||
goto fail;
|
||||
|
||||
return fd;
|
||||
|
||||
fail:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int kernel_add_stream(int fd, struct kernel_stream *info, int update) {
|
||||
struct mediaproxy_message msg;
|
||||
|
||||
ZERO(msg);
|
||||
msg.cmd = update ? MMG_UPDATE : MMG_ADD;
|
||||
msg.target.target_port = info->local_port;
|
||||
msg.target.src_ip = info->src.ip;
|
||||
msg.target.dst_ip = info->dest.ip;
|
||||
msg.target.src_port = info->src.port;
|
||||
msg.target.dst_port = info->dest.port;
|
||||
msg.target.mirror_ip = info->mirror.ip;
|
||||
msg.target.mirror_port = info->mirror.port;
|
||||
msg.target.tos = info->tos;
|
||||
|
||||
return write(fd, &msg, sizeof(msg)) <= 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
int kernel_del_stream(int fd, u_int16_t p) {
|
||||
struct mediaproxy_message msg;
|
||||
|
||||
ZERO(msg);
|
||||
msg.cmd = MMG_DEL;
|
||||
msg.target.target_port = p;
|
||||
|
||||
return write(fd, &msg, sizeof(msg)) <= 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
GList *kernel_list(unsigned int id) {
|
||||
char str[64];
|
||||
int fd;
|
||||
struct mediaproxy_list_entry *buf;
|
||||
GList *li = NULL;
|
||||
int ret;
|
||||
|
||||
sprintf(str, PREFIX "/%u/blist", id);
|
||||
fd = open(str, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
|
||||
|
||||
for (;;) {
|
||||
buf = malloc(sizeof(*buf));
|
||||
ret = read(fd, buf, sizeof(*buf));
|
||||
if (ret != sizeof(*buf))
|
||||
break;
|
||||
li = g_list_prepend(li, buf);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
close(fd);
|
||||
|
||||
return li;
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
#ifndef __KERNEL_H__
|
||||
#define __KERNEL_H__
|
||||
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <glib.h>
|
||||
|
||||
|
||||
|
||||
|
||||
struct ip_port {
|
||||
u_int32_t ip;
|
||||
u_int16_t port;
|
||||
};
|
||||
|
||||
struct kernel_stream {
|
||||
u_int16_t local_port;
|
||||
struct ip_port src;
|
||||
struct ip_port dest;
|
||||
struct ip_port mirror;
|
||||
unsigned char tos;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
int kernel_create_table(unsigned int);
|
||||
int kernel_open_table(unsigned int);
|
||||
|
||||
int kernel_add_stream(int, struct kernel_stream *, int);
|
||||
int kernel_del_stream(int, u_int16_t);
|
||||
GList *kernel_list(unsigned int);
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,12 @@
|
||||
#ifndef __LOG_H__
|
||||
#define __LOG_H__
|
||||
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
|
||||
#define mylog(x,y...) syslog(x,y)
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,199 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/resource.h>
|
||||
#include <glib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "poller.h"
|
||||
#include "control.h"
|
||||
#include "aux.h"
|
||||
#include "log.h"
|
||||
#include "call.h"
|
||||
#include "kernel.h"
|
||||
|
||||
|
||||
|
||||
|
||||
#define die(x...) do { fprintf(stderr, x); exit(-1); } while(0)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static char *pidfile;
|
||||
static gboolean foreground;
|
||||
static u_int32_t ip;
|
||||
static u_int32_t listenp;
|
||||
static u_int16_t listenport;
|
||||
static int tos;
|
||||
static int table;
|
||||
static int timeout;
|
||||
static int silent_timeout;
|
||||
|
||||
|
||||
|
||||
|
||||
static void signals(void) {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
|
||||
static int rlim(int res, rlim_t val) {
|
||||
struct rlimit rlim;
|
||||
|
||||
ZERO(rlim);
|
||||
rlim.rlim_cur = rlim.rlim_max = val;
|
||||
return setrlimit(res, &rlim);
|
||||
}
|
||||
|
||||
static void resources(void) {
|
||||
int tryv;
|
||||
|
||||
rlim(RLIMIT_CORE, RLIM_INFINITY);
|
||||
for (tryv = ((1<<16) - 1); tryv && rlim(RLIMIT_NOFILE, tryv) == -1; tryv >>= 1)
|
||||
;
|
||||
|
||||
rlim(RLIMIT_DATA, RLIM_INFINITY);
|
||||
rlim(RLIMIT_RSS, RLIM_INFINITY);
|
||||
rlim(RLIMIT_AS, RLIM_INFINITY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void options(int *argc, char ***argv) {
|
||||
static char *ips;
|
||||
static char *listenps;
|
||||
static GOptionEntry e[] = {
|
||||
{ "table", 't', 0, G_OPTION_ARG_INT, &table, "Kernel table to use", "INT" },
|
||||
{ "ip", 'i', 0, G_OPTION_ARG_STRING, &ips, "Local IP address", "IP" },
|
||||
{ "listen", 'l', 0, G_OPTION_ARG_STRING, &listenps, "Port to listen on", "[IP:]PORT" },
|
||||
{ "tos", 'T', 0, G_OPTION_ARG_INT, &tos, "TOS value to set on streams", "INT" },
|
||||
{ "timeout", 'o', 0, G_OPTION_ARG_INT, &timeout, "RTP timeout", "SECS" },
|
||||
{ "silent-timeout",'s',0,G_OPTION_ARG_INT, &silent_timeout,"RTP timeout for muted", "SECS" },
|
||||
{ "pidfile", 'p', 0, G_OPTION_ARG_STRING, &pidfile, "Write PID to file", "FILE" },
|
||||
{ "foreground", 'f', 0, G_OPTION_ARG_NONE, &foreground, "Don't fork to background", NULL },
|
||||
{ NULL, }
|
||||
};
|
||||
|
||||
GOptionContext *c;
|
||||
GError *er = NULL;
|
||||
char *p;
|
||||
|
||||
c = g_option_context_new(" - next-generation media proxy");
|
||||
g_option_context_add_main_entries(c, e, NULL);
|
||||
if (!g_option_context_parse(c, argc, argv, &er))
|
||||
die("Bad command line: %s\n", er->message);
|
||||
|
||||
if (!ips)
|
||||
die("Missing option IP\n");
|
||||
if (!listenps)
|
||||
die("Missing option LISTEN\n");
|
||||
|
||||
ip = inet_addr(ips);
|
||||
if (ip == -1)
|
||||
die("Invalid IP\n");
|
||||
|
||||
p = strchr(listenps, ':');
|
||||
if (p) {
|
||||
*p++ = 0;
|
||||
listenp = inet_addr(listenps);
|
||||
if (listenp == -1)
|
||||
die("Invalid IP\n");
|
||||
listenport = atoi(p);
|
||||
}
|
||||
else {
|
||||
if (strchr(listenps, '.'))
|
||||
die("Invalid port\n");
|
||||
listenport = atoi(listenps);
|
||||
}
|
||||
if (!listenport)
|
||||
die("Invalid port\n");
|
||||
if (p)
|
||||
*--p = ':';
|
||||
|
||||
if (tos < 0 || tos > 255)
|
||||
die("Invalid TOS value");
|
||||
|
||||
if (timeout <= 0)
|
||||
timeout = 60;
|
||||
if (silent_timeout <= 0)
|
||||
silent_timeout = 3600;
|
||||
}
|
||||
|
||||
|
||||
static void daemonize(void) {
|
||||
printf("Going to background...\n");
|
||||
if (fork())
|
||||
_exit(0);
|
||||
freopen("/dev/null", "r", stdin);
|
||||
freopen("/dev/null", "w", stdout);
|
||||
freopen("/dev/null", "w", stderr);
|
||||
setpgrp();
|
||||
}
|
||||
|
||||
static void wpidfile(void) {
|
||||
FILE *fp;
|
||||
|
||||
if (!pidfile)
|
||||
return;
|
||||
|
||||
fp = fopen(pidfile, "w");
|
||||
if (fp) {
|
||||
fprintf(fp, "%u\n", getpid());
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
struct poller *p;
|
||||
struct callmaster *m;
|
||||
struct control *c;
|
||||
int kfd;
|
||||
int ret;
|
||||
|
||||
options(&argc, &argv);
|
||||
signals();
|
||||
resources();
|
||||
|
||||
|
||||
if (kernel_create_table(table))
|
||||
die("Failed to create kernel table %i\n", table);
|
||||
kfd = kernel_open_table(table);
|
||||
if (kfd == -1)
|
||||
die("Failed to open kernel table %i\n", table);
|
||||
|
||||
p = poller_new();
|
||||
if (!p)
|
||||
die("poller creation failed\n");
|
||||
|
||||
m = callmaster_new(p);
|
||||
if (!m)
|
||||
return -1;
|
||||
m->kernelfd = kfd;
|
||||
m->kernelid = table;
|
||||
m->ip = ip;
|
||||
m->timeout = timeout;
|
||||
m->silent_timeout = silent_timeout;
|
||||
m->tos = tos;
|
||||
|
||||
c = control_new(p, listenp, listenport, m);
|
||||
if (!c)
|
||||
die("Failed to open control connection port\n");
|
||||
|
||||
mylog(LOG_INFO, "Startup complete");
|
||||
|
||||
if (!foreground)
|
||||
daemonize();
|
||||
wpidfile();
|
||||
|
||||
for (;;) {
|
||||
ret = poller_poll(p, 100);
|
||||
if (ret == -1)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include "poller.h"
|
||||
|
||||
|
||||
void dummy(int a, void *b) {
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
struct poller *p;
|
||||
struct poller_item i;
|
||||
|
||||
p = poller_new();
|
||||
if (!p) {
|
||||
fprintf(stderr, "poller creation failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(p->items_size == 0);
|
||||
assert(p->pollfds_size == 0);
|
||||
|
||||
i.readable = dummy;
|
||||
i.writeable = dummy;
|
||||
i.closed = dummy;
|
||||
|
||||
|
||||
i.fd = 3;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
i.fd = 4;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
i.fd = 2;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
i.fd = 6;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
i.fd = 0;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
i.fd = 1;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
i.fd = 5;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
i.fd = 7;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
i.fd = 9;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
|
||||
|
||||
assert(poller_del_item(p, 10) == -1);
|
||||
assert(poller_del_item(p, 6) == 0);
|
||||
assert(poller_del_item(p, 8) == -1);
|
||||
assert(poller_del_item(p, 0) == 0);
|
||||
assert(poller_del_item(p, 3) == 0);
|
||||
assert(poller_del_item(p, 11) == -1);
|
||||
assert(poller_del_item(p, 9) == 0);
|
||||
assert(poller_del_item(p, 11) == -1);
|
||||
assert(poller_del_item(p, 4) == 0);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,311 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "poller.h"
|
||||
#include "aux.h"
|
||||
|
||||
|
||||
|
||||
|
||||
#define POLLER_BSEARCH(b,l,k,e) mybsearch(b, l, sizeof(struct pollfd), k, OFFSET_OF(struct pollfd, fd), sizeof(*(k)), e)
|
||||
|
||||
|
||||
|
||||
|
||||
struct timer_item {
|
||||
void (*func)(void *);
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct poller *poller_new(void) {
|
||||
struct poller *p;
|
||||
|
||||
p = malloc(sizeof(*p));
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->now = time(NULL);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
int poller_add_item(struct poller *p, struct poller_item *i) {
|
||||
struct poller_item *ip;
|
||||
struct pollfd *pf;
|
||||
int idx;
|
||||
unsigned int u;
|
||||
|
||||
if (!p || !i)
|
||||
return -1;
|
||||
if (i->fd < 0)
|
||||
return -1;
|
||||
if (!i->readable && !i->writeable)
|
||||
return -1;
|
||||
if (!i->closed)
|
||||
return -1;
|
||||
|
||||
if (i->fd < p->items_size && p->items[i->fd])
|
||||
return -1;
|
||||
|
||||
idx = POLLER_BSEARCH(p->pollfds, p->pollfds_size, &i->fd, 0);
|
||||
assert(idx < 0);
|
||||
|
||||
idx *= -1;
|
||||
idx--;
|
||||
|
||||
p->pollfds_size++;
|
||||
p->pollfds = realloc(p->pollfds, p->pollfds_size * sizeof(*p->pollfds));
|
||||
memmove(p->pollfds + idx + 1, p->pollfds + idx, (p->pollfds_size - idx - 1) * sizeof(*p->pollfds));
|
||||
pf = &p->pollfds[idx];
|
||||
|
||||
pf->fd = i->fd;
|
||||
pf->events = POLLHUP | POLLERR | ((i->writeable && i->blocked) ? POLLOUT : 0) | (i->readable ? POLLIN : 0);
|
||||
pf->revents = 0;
|
||||
|
||||
if (i->fd >= p->items_size) {
|
||||
u = p->items_size;
|
||||
p->items_size = i->fd + 1;
|
||||
p->items = realloc(p->items, sizeof(*p->items) * p->items_size);
|
||||
memset(p->items + u, 0, sizeof(*p->items) * (p->items_size - u - 1));
|
||||
}
|
||||
|
||||
ip = malloc(sizeof(*ip));
|
||||
memcpy(ip, i, sizeof(*ip));
|
||||
p->items[i->fd] = ip;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int poller_del_item(struct poller *p, int fd) {
|
||||
int idx;
|
||||
|
||||
if (!p || fd < 0)
|
||||
return -1;
|
||||
if (fd >= p->items_size)
|
||||
return -1;
|
||||
if (!p->items || !p->items[fd])
|
||||
return -1;
|
||||
if (!p->pollfds || !p->pollfds_size)
|
||||
return -1;
|
||||
|
||||
idx = POLLER_BSEARCH(p->pollfds, p->pollfds_size, &fd, 1);
|
||||
assert(idx != -1);
|
||||
|
||||
memmove(p->pollfds + idx, p->pollfds + idx + 1, (p->pollfds_size - idx - 1) * sizeof(*p->pollfds));
|
||||
p->pollfds_size--;
|
||||
p->pollfds = realloc(p->pollfds, p->pollfds_size * sizeof(*p->pollfds));
|
||||
|
||||
if (p->pollfds_work) {
|
||||
idx = POLLER_BSEARCH(p->pollfds_work, p->pollfds_work_size, &fd, 1);
|
||||
|
||||
if (idx != -1)
|
||||
p->pollfds_work[idx].fd = -1;
|
||||
}
|
||||
|
||||
free(p->items[fd]);
|
||||
p->items[fd] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int poller_update_item(struct poller *p, struct poller_item *i) {
|
||||
struct poller_item *np;
|
||||
|
||||
if (!p || !i)
|
||||
return -1;
|
||||
if (i->fd < 0)
|
||||
return -1;
|
||||
if (!i->readable && !i->writeable)
|
||||
return -1;
|
||||
if (!i->closed)
|
||||
return -1;
|
||||
|
||||
if (i->fd >= p->items_size || !(np = p->items[i->fd]))
|
||||
return poller_add_item(p, i);
|
||||
|
||||
np->ptr = i->ptr;
|
||||
np->readable = i->readable;
|
||||
np->writeable = i->writeable;
|
||||
np->closed = i->closed;
|
||||
np->timer = i->timer;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int poller_poll(struct poller *p, int timeout) {
|
||||
struct pollfd *pfd, *pf;
|
||||
int ret, i;
|
||||
struct poller_item *it;
|
||||
int idx;
|
||||
time_t last;
|
||||
int do_timer;
|
||||
GList *li;
|
||||
struct timer_item *ti;
|
||||
|
||||
if (!p)
|
||||
return -1;
|
||||
if (!p->pollfds || !p->pollfds_size)
|
||||
return -1;
|
||||
if (!p->items || !p->items_size)
|
||||
return -1;
|
||||
|
||||
p->pollfds_work_size = i = p->pollfds_size;
|
||||
p->pollfds_work = pfd = malloc(sizeof(*pfd) * i);
|
||||
memcpy(pfd, p->pollfds, sizeof(*pfd) * i);
|
||||
|
||||
do_timer = 0;
|
||||
last = p->now;
|
||||
p->now = time(NULL);
|
||||
if (last != p->now) {
|
||||
do_timer = 1;
|
||||
ret = i;
|
||||
|
||||
for (li = p->timers; li; li = li->next) {
|
||||
ti = li->data;
|
||||
ti->func(ti->ptr);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ret = poll(pfd, i, timeout);
|
||||
if (errno == EINTR)
|
||||
ret = 0;
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
pf = pfd;
|
||||
for (pf = pfd; i; pf++) {
|
||||
i--;
|
||||
|
||||
if (pf->fd < 0)
|
||||
continue;
|
||||
|
||||
it = (pf->fd < p->items_size) ? p->items[pf->fd] : NULL;
|
||||
if (!it)
|
||||
continue;
|
||||
|
||||
if (do_timer) {
|
||||
if (it->timer)
|
||||
it->timer(it->fd, it->ptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it->error) {
|
||||
it->closed(it->fd, it->ptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((pf->revents & (POLLERR | POLLHUP)))
|
||||
it->closed(it->fd, it->ptr);
|
||||
else if ((pf->revents & POLLOUT)) {
|
||||
it->blocked = 0;
|
||||
|
||||
idx = POLLER_BSEARCH(p->pollfds, p->pollfds_size, &it->fd, 1);
|
||||
assert(idx != -1);
|
||||
|
||||
p->pollfds[idx].events &= ~POLLOUT;
|
||||
|
||||
it->writeable(it->fd, it->ptr);
|
||||
}
|
||||
else if ((pf->revents & POLLIN))
|
||||
it->readable(it->fd, it->ptr);
|
||||
else if (!pf->revents)
|
||||
continue;
|
||||
else
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
out:
|
||||
free(pfd);
|
||||
p->pollfds_work = NULL;
|
||||
p->pollfds_work_size = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void poller_blocked(struct poller *p, int fd) {
|
||||
int idx;
|
||||
|
||||
if (!p || fd < 0)
|
||||
return;
|
||||
if (fd >= p->items_size)
|
||||
return;
|
||||
if (!p->items || !p->items[fd])
|
||||
return;
|
||||
if (!p->pollfds || !p->pollfds_size)
|
||||
return;
|
||||
if (!p->items[fd]->writeable)
|
||||
return;
|
||||
|
||||
p->items[fd]->blocked = 1;
|
||||
|
||||
idx = POLLER_BSEARCH(p->pollfds, p->pollfds_size, &fd, 1);
|
||||
assert(idx != -1);
|
||||
|
||||
p->pollfds[idx].events |= POLLOUT;
|
||||
}
|
||||
|
||||
void poller_error(struct poller *p, int fd) {
|
||||
if (!p || fd < 0)
|
||||
return;
|
||||
if (fd >= p->items_size)
|
||||
return;
|
||||
if (!p->items || !p->items[fd])
|
||||
return;
|
||||
if (!p->pollfds || !p->pollfds_size)
|
||||
return;
|
||||
if (!p->items[fd]->writeable)
|
||||
return;
|
||||
|
||||
p->items[fd]->error = 1;
|
||||
p->items[fd]->blocked = 1;
|
||||
}
|
||||
|
||||
int poller_isblocked(struct poller *p, int fd) {
|
||||
if (!p || fd < 0)
|
||||
return -1;
|
||||
if (fd >= p->items_size)
|
||||
return -1;
|
||||
if (!p->items || !p->items[fd])
|
||||
return -1;
|
||||
if (!p->pollfds || !p->pollfds_size)
|
||||
return -1;
|
||||
if (!p->items[fd]->writeable)
|
||||
return -1;
|
||||
|
||||
return p->items[fd]->blocked;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int poller_timer(struct poller *p, void (*f)(void *), void *ptr) {
|
||||
struct timer_item *i;
|
||||
|
||||
if (!p || !f)
|
||||
return -1;
|
||||
|
||||
i = malloc(sizeof(*i));
|
||||
ZERO(*i);
|
||||
|
||||
i->func = f;
|
||||
i->ptr = ptr;
|
||||
|
||||
p->timers = g_list_prepend(p->timers, i);
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
#ifndef __POLLER_H__
|
||||
#define __POLLER_H__
|
||||
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <glib.h>
|
||||
|
||||
|
||||
|
||||
struct poller_item {
|
||||
int fd;
|
||||
void *ptr;
|
||||
|
||||
void (*readable)(int, void *);
|
||||
void (*writeable)(int, void *);
|
||||
void (*closed)(int, void *);
|
||||
void (*timer)(int, void *);
|
||||
|
||||
int blocked:1;
|
||||
int error:1;
|
||||
};
|
||||
|
||||
struct poller {
|
||||
struct poller_item **items;
|
||||
unsigned int items_size;
|
||||
struct pollfd *pollfds;
|
||||
unsigned int pollfds_size;
|
||||
GList *timers;
|
||||
|
||||
time_t now;
|
||||
|
||||
struct pollfd *pollfds_work;
|
||||
unsigned int pollfds_work_size;
|
||||
};
|
||||
|
||||
|
||||
struct poller *poller_new(void);
|
||||
int poller_add_item(struct poller *, struct poller_item *);
|
||||
int poller_update_item(struct poller *, struct poller_item *);
|
||||
int poller_del_item(struct poller *, int);
|
||||
int poller_poll(struct poller *, int);
|
||||
void poller_blocked(struct poller *, int);
|
||||
int poller_isblocked(struct poller *, int);
|
||||
void poller_error(struct poller *, int);
|
||||
|
||||
int poller_timer(struct poller *, void (*)(void *), void *);
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,172 @@
|
||||
#include <stdio.h>
|
||||
#include <glib.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "streambuf.h"
|
||||
#include "poller.h"
|
||||
#include "aux.h"
|
||||
|
||||
|
||||
|
||||
struct streambuf *streambuf_new(struct poller *p, int fd) {
|
||||
struct streambuf *b;
|
||||
|
||||
b = malloc(sizeof(*b));
|
||||
ZERO(*b);
|
||||
|
||||
b->buf = g_string_new("");
|
||||
b->fd = fd;
|
||||
b->poller = p;
|
||||
b->active = p->now;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
void streambuf_destroy(struct streambuf *b) {
|
||||
g_string_free(b->buf, TRUE);
|
||||
free(b);
|
||||
}
|
||||
|
||||
|
||||
int streambuf_writeable(struct streambuf *b) {
|
||||
int ret;
|
||||
unsigned int out;
|
||||
|
||||
for (;;) {
|
||||
if (!b->buf->len)
|
||||
break;
|
||||
|
||||
out = (b->buf->len > 1024) ? 1024 : b->buf->len;
|
||||
ret = write(b->fd, b->buf->str, out);
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
return -1;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (ret > 0) {
|
||||
g_string_erase(b->buf, 0, ret);
|
||||
b->active = b->poller->now;
|
||||
}
|
||||
|
||||
if (ret != out) {
|
||||
poller_blocked(b->poller, b->fd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int streambuf_readable(struct streambuf *b) {
|
||||
int ret;
|
||||
char buf[1024];
|
||||
|
||||
for (;;) {
|
||||
ret = read(b->fd, buf, 1024);
|
||||
|
||||
if (ret == 0)
|
||||
return -1;
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
break;
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_string_append_len(b->buf, buf, ret);
|
||||
b->active = b->poller->now;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
char *streambuf_getline(struct streambuf *b) {
|
||||
char *p;
|
||||
int len;
|
||||
char *s = NULL;
|
||||
|
||||
for (;;) {
|
||||
if (s)
|
||||
free(s);
|
||||
|
||||
p = memchr(b->buf->str, '\n', b->buf->len);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
len = p - b->buf->str;
|
||||
if (len == 0) {
|
||||
g_string_erase(b->buf, 0, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
s = malloc(len + 1);
|
||||
memcpy(s, b->buf->str, len);
|
||||
s[len] = '\0';
|
||||
g_string_erase(b->buf, 0, len + 1);
|
||||
|
||||
if (s[--len] == '\r') {
|
||||
if (len == 0)
|
||||
continue;
|
||||
s[len] = '\0';
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
unsigned int streambuf_bufsize(struct streambuf *b) {
|
||||
return b->buf->len;
|
||||
}
|
||||
|
||||
|
||||
void streambuf_printf(struct streambuf *b, char *f, ...) {
|
||||
va_list va;
|
||||
GString *gs;
|
||||
|
||||
va_start(va, f);
|
||||
gs = g_string_new("");
|
||||
g_string_vprintf(gs, f, va);
|
||||
va_end(va);
|
||||
|
||||
streambuf_write(b, gs->str, gs->len);
|
||||
g_string_free(gs, TRUE);
|
||||
}
|
||||
|
||||
void streambuf_write(struct streambuf *b, char *s, unsigned int len) {
|
||||
unsigned int out;
|
||||
int ret;
|
||||
|
||||
while (len && !poller_isblocked(b->poller, b->fd)) {
|
||||
out = (len > 1024) ? 1024 : len;
|
||||
ret = write(b->fd, s, out);
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
poller_error(b->poller, b->fd);
|
||||
break;
|
||||
}
|
||||
poller_blocked(b->poller, b->fd);
|
||||
break;
|
||||
}
|
||||
if (ret == 0)
|
||||
break;
|
||||
|
||||
s += ret;
|
||||
len -= ret;
|
||||
b->active = b->poller->now;
|
||||
}
|
||||
|
||||
if (b->buf->len > 5242880)
|
||||
poller_error(b->poller, b->fd);
|
||||
else if (len)
|
||||
g_string_append_len(b->buf, s, len);
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
#ifndef __BUFFER_H__
|
||||
#define __BUFFER_H__
|
||||
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <glib.h>
|
||||
|
||||
|
||||
|
||||
struct poller;
|
||||
|
||||
|
||||
|
||||
struct streambuf {
|
||||
GString *buf;
|
||||
int fd;
|
||||
struct poller *poller;
|
||||
time_t active;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct streambuf *streambuf_new(struct poller *, int);
|
||||
void streambuf_destroy(struct streambuf *);
|
||||
int streambuf_writeable(struct streambuf *);
|
||||
int streambuf_readable(struct streambuf *);
|
||||
char *streambuf_getline(struct streambuf *);
|
||||
unsigned int streambuf_bufsize(struct streambuf *);
|
||||
void streambuf_printf(struct streambuf *, char *, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
void streambuf_write(struct streambuf *, char *, unsigned int);
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,5 @@
|
||||
mediaproxy-ng (0.1) unstable; urgency=low
|
||||
|
||||
* Initial release.
|
||||
|
||||
-- Michael Prokop <mprokop@sipwise.com> Thu, 11 Feb 2010 23:06:08 +0100
|
@ -0,0 +1 @@
|
||||
5
|
@ -0,0 +1,19 @@
|
||||
Source: mediaproxy-ng
|
||||
Section: net
|
||||
Priority: extra
|
||||
Maintainer: Michael Prokop <mprokop@sipwise.com>
|
||||
Build-Depends: debhelper (>= 5), iptables-dev, libglib2.0-dev, libpcre3-dev
|
||||
Standards-Version: 3.8.4
|
||||
Homepage: http://sipwise.com/
|
||||
|
||||
Package: mediaproxy-ng-daemon
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Description: TODO
|
||||
TODO
|
||||
|
||||
Package: mediaproxy-ng-iptables
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Description: TODO
|
||||
TODO
|
@ -0,0 +1,7 @@
|
||||
Upstream Author: The Sipwise Team - http://sipwise.com/
|
||||
Copyright: Copyright (c) 2007-2010 Sipwise GmbH, Austria
|
||||
License: All software included in this package is
|
||||
Copyright (c) Sipwise GmbH, Austria.
|
||||
All rights reserved. You may not copy, distribute
|
||||
or modify without prior written permission from
|
||||
Sipwise GmbH, Austria.
|
@ -0,0 +1 @@
|
||||
usr/sbin
|
@ -0,0 +1 @@
|
||||
daemon/mediaproxy-ng /usr/sbin/
|
@ -0,0 +1 @@
|
||||
iptables-extension/libipt_MEDIAPROXY.so /lib/xtables/
|
@ -0,0 +1,81 @@
|
||||
#!/usr/bin/make -f
|
||||
# -*- makefile -*-
|
||||
# Sample debian/rules that uses debhelper.
|
||||
# This file was originally written by Joey Hess and Craig Small.
|
||||
# As a special exception, when this file is copied by dh-make into a
|
||||
# dh-make output file, you may use that output file without restriction.
|
||||
# This special exception was added by Craig Small in version 0.37 of dh-make.
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
b=$(CURDIR)/debian/build
|
||||
|
||||
build: build-stamp
|
||||
|
||||
build-stamp:
|
||||
dh_testdir
|
||||
cd iptables-extension/ && gcc -O2 -Wall -shared -o libipt_MEDIAPROXY.so libipt_MEDIAPROXY-1.4.c && cd ..
|
||||
cd daemon && $(MAKE) && cd ..
|
||||
touch $@
|
||||
|
||||
clean:
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
rm -f build-stamp
|
||||
rm -f iptables-extension/libipt_MEDIAPROXY.so
|
||||
rm -f daemon/mediaproxy-ng daemon/build_time.h daemon/.depend kernel-module/.ipt_MEDIAPROXY.o.d
|
||||
rm -rf kernel-module/.tmp_versions
|
||||
dh_clean
|
||||
-rm -rf debian/build
|
||||
|
||||
install: build
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_clean -k
|
||||
dh_installdirs
|
||||
|
||||
mediaproxy-ng-daemon: install
|
||||
@echo "--- Building: $@"
|
||||
dh_installdirs -p$@ -P$(b)/$@
|
||||
dh_link -p$@ -P$(b)/$@
|
||||
dh_installdocs -p$@ -P$(b)/$@
|
||||
dh_installchangelogs -p$@ -P$(b)/$@
|
||||
dh_install -p$@ -P$(b)/$@
|
||||
dh_strip -p$@ -P$(b)/$@
|
||||
dh_compress -p$@ -P$(b)/$@
|
||||
dh_fixperms -p$@ -P$(b)/$@
|
||||
dh_makeshlibs -p$@ -P$(b)/$@ -V
|
||||
dh_installdeb -p$@ -P$(b)/$@
|
||||
dh_shlibdeps -p$@ -P$(b)/$@
|
||||
dh_installdebconf -p$@ -P$(b)/$@
|
||||
dh_gencontrol -p$@ -P$(b)/$@
|
||||
dh_md5sums -p$@ -P$(b)/$@
|
||||
dh_builddeb -p$@ -P$(b)/$@
|
||||
|
||||
mediaproxy-ng-iptables: install
|
||||
@echo "--- Building: $@"
|
||||
dh_installdirs -p$@ -P$(b)/$@
|
||||
dh_link -p$@ -P$(b)/$@
|
||||
dh_installdocs -p$@ -P$(b)/$@
|
||||
dh_installchangelogs -p$@ -P$(b)/$@
|
||||
dh_install -p$@ -P$(b)/$@
|
||||
dh_strip -p$@ -P$(b)/$@
|
||||
dh_compress -p$@ -P$(b)/$@
|
||||
dh_fixperms -p$@ -P$(b)/$@
|
||||
dh_makeshlibs -p$@ -P$(b)/$@ -V
|
||||
dh_installdeb -p$@ -P$(b)/$@
|
||||
dh_shlibdeps -p$@ -P$(b)/$@
|
||||
dh_installdebconf -p$@ -P$(b)/$@
|
||||
dh_gencontrol -p$@ -P$(b)/$@
|
||||
dh_md5sums -p$@ -P$(b)/$@
|
||||
dh_builddeb -p$@ -P$(b)/$@
|
||||
|
||||
# Build architecture-dependent files here.
|
||||
binary-all: build install
|
||||
|
||||
# Build architecture-independent files here.
|
||||
binary-indep: build install mediaproxy-ng-daemon mediaproxy-ng-iptables
|
||||
|
||||
binary: binary-indep binary-arch
|
||||
.PHONY: build clean binary-indep binary-arch binary install
|
@ -0,0 +1,81 @@
|
||||
/* gcc -O2 -Wall -shared -o libipt_MEDIAPROXY.so libipt_MEDIAPROXY.c */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <xtables.h>
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#include "../kernel-module/ipt_MEDIAPROXY.h"
|
||||
|
||||
|
||||
static void help(void) {
|
||||
printf(
|
||||
"MEDIAPROXY target options:\n"
|
||||
" --id <id>\n"
|
||||
" Unique ID for this instance\n"
|
||||
);
|
||||
}
|
||||
|
||||
static int parse(int c,
|
||||
char **argv,
|
||||
int invert,
|
||||
unsigned int *flags,
|
||||
const void *entry,
|
||||
struct xt_entry_target **target) {
|
||||
|
||||
struct ipt_mediaproxy_info *info = (void *) *target;
|
||||
|
||||
if (c == '1') {
|
||||
info->id = atoi(optarg);
|
||||
if (flags)
|
||||
*flags = 1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void final_check(unsigned int flags) {
|
||||
if (!flags)
|
||||
exit_error(PARAMETER_PROBLEM, "You must specify --id");
|
||||
}
|
||||
|
||||
static void print(const void *ip, const struct xt_entry_target *target, int numeric) {
|
||||
struct ipt_mediaproxy_info *info = (void *) target;
|
||||
|
||||
printf("id %u", info->id);
|
||||
}
|
||||
|
||||
static void save(const void *ip, const struct xt_entry_target *target) {
|
||||
struct ipt_mediaproxy_info *info = (void *) target;
|
||||
|
||||
printf("--id %u", info->id);
|
||||
}
|
||||
|
||||
static struct option opts[] = {
|
||||
{ "id", 1, NULL, '1' },
|
||||
{ NULL, },
|
||||
};
|
||||
|
||||
|
||||
static struct xtables_target mediaproxy = {
|
||||
.name = "MEDIAPROXY",
|
||||
.family = AF_INET,
|
||||
.version = "1.4.2",
|
||||
.size = XT_ALIGN(sizeof(struct ipt_mediaproxy_info)),
|
||||
.userspacesize = XT_ALIGN(sizeof(struct ipt_mediaproxy_info)),
|
||||
.help = help,
|
||||
.parse = parse,
|
||||
.final_check = final_check,
|
||||
.print = print,
|
||||
.save = save,
|
||||
.extra_opts = opts,
|
||||
};
|
||||
|
||||
void _init(void) {
|
||||
xtables_register_target(&mediaproxy);
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/* gcc -O2 -Wall -shared -o libipt_MEDIAPROXY.so libipt_MEDIAPROXY.c */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <iptables.h>
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#include "../kernel-module/ipt_MEDIAPROXY.h"
|
||||
|
||||
|
||||
static void help(void) {
|
||||
printf(
|
||||
"MEDIAPROXY target options:\n"
|
||||
" --id <id>\n"
|
||||
" Unique ID for this instance\n"
|
||||
);
|
||||
}
|
||||
|
||||
static int parse(int c,
|
||||
char **argv,
|
||||
int invert,
|
||||
unsigned int *flags,
|
||||
const struct ipt_entry *entry,
|
||||
struct ipt_entry_target **target) {
|
||||
|
||||
struct ipt_mediaproxy_info *info = (void *) (*target)->data;
|
||||
|
||||
if (c == '1') {
|
||||
info->id = atoi(optarg);
|
||||
if (flags)
|
||||
*flags = 1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void final_check(unsigned int flags) {
|
||||
if (!flags)
|
||||
exit_error(PARAMETER_PROBLEM, "You must specify --id");
|
||||
}
|
||||
|
||||
static void print(const struct ipt_ip *ip, const struct ipt_entry_target *target, int numeric) {
|
||||
struct ipt_mediaproxy_info *info = (void *) target->data;
|
||||
|
||||
printf("id %u", info->id);
|
||||
}
|
||||
|
||||
static void save(const struct ipt_ip *ip, const struct ipt_entry_target *target) {
|
||||
struct ipt_mediaproxy_info *info = (void *) target->data;
|
||||
|
||||
printf("--id %u", info->id);
|
||||
}
|
||||
|
||||
static struct option opts[] = {
|
||||
{ "id", 1, NULL, '1' },
|
||||
{ NULL, },
|
||||
};
|
||||
|
||||
|
||||
static struct iptables_target mediaproxy = {
|
||||
.name = "MEDIAPROXY",
|
||||
.version = "1.3.6",
|
||||
.size = IPT_ALIGN(sizeof(struct ipt_mediaproxy_info)),
|
||||
.userspacesize = IPT_ALIGN(sizeof(struct ipt_mediaproxy_info)),
|
||||
.help = help,
|
||||
.parse = parse,
|
||||
.final_check = final_check,
|
||||
.print = print,
|
||||
.save = save,
|
||||
.extra_opts = opts,
|
||||
};
|
||||
|
||||
void _init(void) {
|
||||
register_target(&mediaproxy);
|
||||
}
|
@ -0,0 +1 @@
|
||||
obj-m += ipt_MEDIAPROXY.o
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,75 @@
|
||||
#ifndef IPT_RTPPROXY_H
|
||||
#define IPT_RTPPROXY_H
|
||||
|
||||
struct ipt_mediaproxy_info {
|
||||
u_int32_t id;
|
||||
};
|
||||
|
||||
struct mediaproxy_stats {
|
||||
u_int64_t packets;
|
||||
u_int64_t bytes;
|
||||
u_int64_t errors;
|
||||
};
|
||||
|
||||
struct mediaproxy_target_info {
|
||||
u_int16_t target_port;
|
||||
|
||||
u_int32_t src_ip;
|
||||
u_int32_t dst_ip;
|
||||
u_int16_t src_port;
|
||||
u_int16_t dst_port;
|
||||
|
||||
u_int32_t mirror_ip;
|
||||
u_int16_t mirror_port;
|
||||
|
||||
unsigned char tos;
|
||||
};
|
||||
|
||||
struct mediaproxy_message {
|
||||
enum {
|
||||
MMG_NOOP = 1,
|
||||
MMG_ADD,
|
||||
MMG_DEL,
|
||||
MMG_UPDATE,
|
||||
} cmd;
|
||||
|
||||
struct mediaproxy_target_info target;
|
||||
};
|
||||
|
||||
struct mediaproxy_list_entry {
|
||||
struct mediaproxy_target_info target;
|
||||
struct mediaproxy_stats stats;
|
||||
};
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
struct mediaproxy_target {
|
||||
atomic_t refcnt;
|
||||
u_int32_t table;
|
||||
struct mediaproxy_target_info target;
|
||||
|
||||
spinlock_t lock;
|
||||
struct mediaproxy_stats stats;
|
||||
};
|
||||
|
||||
struct mediaproxy_table {
|
||||
atomic_t refcnt;
|
||||
rwlock_t target_lock;
|
||||
pid_t pid;
|
||||
|
||||
u_int32_t id;
|
||||
struct proc_dir_entry *proc;
|
||||
struct proc_dir_entry *status;
|
||||
struct proc_dir_entry *control;
|
||||
struct proc_dir_entry *list;
|
||||
struct proc_dir_entry *blist;
|
||||
|
||||
struct mediaproxy_target **target[256];
|
||||
|
||||
unsigned int buckets;
|
||||
unsigned int targets;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Socket;
|
||||
|
||||
my $t = $ARGV[0] || "0";
|
||||
|
||||
open(X, "<", "/proc/mediaproxy/$t/blist") or die;
|
||||
my $buf;
|
||||
while (sysread(X, $buf, 48)) {
|
||||
my @b = unpack("Sa2 a4a4 SS a4 Sa2 LLLLLL", $buf);
|
||||
for (2,3,6) {
|
||||
$b[$_] = inet_ntoa($b[$_]);
|
||||
}
|
||||
for (9,11,13) {
|
||||
$b[$_] += $b[$_ + 1] * 2**32;
|
||||
}
|
||||
printf("%5u %15s:%-5u -> %15s:%-5u (-> %15s:%-5u) [%llu %llu %llu]\n", @b[0,2,4,3,5,6,7,9,11,13]);
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Socket;
|
||||
|
||||
$| = 1;
|
||||
|
||||
open(F, "> /proc/mediaproxy/1/control");
|
||||
{
|
||||
my $x = select(F);
|
||||
$| = 1;
|
||||
select($x);
|
||||
}
|
||||
#print F (pack("I SS LLSS LS S", 0, 0, -1, 0, 0, 0, 0, 0, 0, -1));
|
||||
#sleep(10);
|
||||
|
||||
print("add 9876 -> 1234/6543\n");
|
||||
syswrite(F, pack("I SS a4a4 SS a4 S S", 1, 9876, -1, inet_aton("192.168.231.132"), inet_aton("192.168.231.1"), 1234, 6543, "", 0, -1));
|
||||
sleep(30);
|
||||
|
||||
print("add fail\n");
|
||||
syswrite(F, pack("I SS a4a4 SS a4 S S", 1, 9876, -1, inet_aton("192.168.231.132"), inet_aton("192.168.231.1"), 1234, 6543, inet_aton("192.168.231.1"), 6789, -1));
|
||||
sleep(30);
|
||||
|
||||
print("update 9876 -> 1234/6543 & 6789\n");
|
||||
syswrite(F, pack("I SS a4a4 SS a4 S S", 3, 9876, -1, inet_aton("192.168.231.132"), inet_aton("192.168.231.1"), 1234, 6543, inet_aton("192.168.231.1"), 6789, -1));
|
||||
sleep(30);
|
||||
|
||||
print("update 9876 -> 2345/7890 & 4321\n");
|
||||
syswrite(F, pack("I SS a4a4 SS a4 S S", 3, 9876, -1, inet_aton("192.168.231.132"), inet_aton("192.168.231.1"), 2345, 7890, inet_aton("192.168.231.1"), 4321, -1));
|
||||
sleep(30);
|
||||
|
||||
print("add fail\n");
|
||||
syswrite(F, pack("I SS a4a4 SS a4 S S", 1, 9876, -1, inet_aton("192.168.231.132"), inet_aton("192.168.231.1"), 1234, 6543, inet_aton("192.168.231.1"), 6789, -1));
|
||||
sleep(30);
|
||||
|
||||
print("update 9876 -> 1234/6543\n");
|
||||
syswrite(F, pack("I SS a4a4 SS a4 S S", 3, 9876, -1, inet_aton("192.168.231.132"), inet_aton("192.168.231.1"), 1234, 6543, "", 0, -1));
|
||||
sleep(30);
|
||||
|
||||
print("delete\n");
|
||||
syswrite(F, pack("I SS a4a4 SS a4 S S", 2, 9876, -1, "", "", 0, 0, "", 0, -1));
|
||||
sleep(30);
|
||||
|
||||
print("delete fail\n");
|
||||
syswrite(F, pack("I SS a4a4 SS a4 S S", 2, 9876, -1, "", "", 0, 0, "", 0, -1));
|
||||
sleep(30);
|
||||
|
||||
print("update fail\n");
|
||||
syswrite(F, pack("I SS a4a4 SS a4 S S", 3, 9876, -1, inet_aton("192.168.231.132"), inet_aton("192.168.231.1"), 1234, 6543, "", 0, -1));
|
||||
sleep(30);
|
||||
|
||||
close(F);
|
Loading…
Reference in new issue