Update to upcoming 3.0 release (2.9.9 for now)

Merged from branch rfuchs/3.0

Conflicts:
	daemon/sdp.c
	debian/changelog
	tests/simulator-ng.pl
	utils/ng-client
git.mgm/mediaproxy-ng/master
Richard Fuchs 11 years ago
parent caa5edd58a
commit f8f741b584

@ -21,6 +21,7 @@ flags = [
'-pthread',
'-I../kernel-module/',
'-D_GNU_SOURCE',
'-D__DEBUG=1',
'-DMEDIAPROXY_VERSION="dummy"',
'-DMP_PLUGIN_DIR="/usr/lib/mediaproxy-ng"',
'-O2',

@ -47,7 +47,7 @@ endif
SRCS= main.c kernel.c poller.c aux.c control_tcp.c streambuf.c call.c control_udp.c redis.c \
bencode.c cookie_cache.c udp_listener.c control_ng.c sdp.c str.c stun.c rtcp.c \
crypto.c rtp.c
crypto.c rtp.c call_interfaces.c dtls.c log.c
OBJS= $(SRCS:.c=.o)

@ -15,6 +15,13 @@
#include <pthread.h>
#include <sys/resource.h>
#include <stdio.h>
#include <stdlib.h>
#if 0 && defined(__DEBUG)
#define __THREAD_DEBUG 1
#endif
@ -65,6 +72,16 @@ static inline int g_hash_table_contains(GHashTable *h, const void *k) {
}
#endif
static inline void g_queue_move(GQueue *dst, GQueue *src) {
GList *l;
while ((l = g_queue_pop_head_link(src)))
g_queue_push_tail_link(dst, l);
}
static inline void g_queue_truncate(GQueue *q, unsigned int len) {
while (q->length > len)
g_queue_pop_tail(q);
}
static inline void strmove(char **d, char **s) {
if (*d)
@ -192,31 +209,131 @@ static inline int strmemcmp(const void *mem, int len, const char *str) {
return memcmp(mem, str, len);
}
/* XXX replace with better source of randomness */
static inline void random_string(unsigned char *buf, int len) {
while (len--)
*buf++ = random() % 0x100;
}
typedef pthread_mutex_t mutex_t;
typedef pthread_rwlock_t rwlock_t;
typedef pthread_cond_t cond_t;
#define mutex_init(m) pthread_mutex_init(m, NULL)
#define mutex_destroy(m) pthread_mutex_destroy(m)
#define mutex_lock(m) pthread_mutex_lock(m)
#define mutex_trylock(m) pthread_mutex_trylock(m)
#define mutex_unlock(m) pthread_mutex_unlock(m)
#define mutex_init(m) __debug_mutex_init(m, __FILE__, __LINE__)
#define mutex_destroy(m) __debug_mutex_destroy(m, __FILE__, __LINE__)
#define mutex_lock(m) __debug_mutex_lock(m, __FILE__, __LINE__)
#define mutex_trylock(m) __debug_mutex_trylock(m, __FILE__, __LINE__)
#define mutex_unlock(m) __debug_mutex_unlock(m, __FILE__, __LINE__)
#define MUTEX_STATIC_INIT PTHREAD_MUTEX_INITIALIZER
#define rwlock_init(l) pthread_rwlock_init(l, NULL)
#define rwlock_lock_r(l) pthread_rwlock_rdlock(l)
#define rwlock_unlock_r(l) pthread_rwlock_unlock(l)
#define rwlock_lock_w(l) pthread_rwlock_wrlock(l)
#define rwlock_unlock_w(l) pthread_rwlock_unlock(l)
#define cond_init(c) pthread_cond_init(c, NULL)
#define cond_wait(c,m) pthread_cond_wait(c,m)
#define cond_signal(c) pthread_cond_signal(c)
#define cond_broadcast(c) pthread_cond_broadcast(c)
#define rwlock_init(l) __debug_rwlock_init(l, __FILE__, __LINE__)
#define rwlock_destroy(l) __debug_rwlock_destroy(l, __FILE__, __LINE__)
#define rwlock_lock_r(l) __debug_rwlock_lock_r(l, __FILE__, __LINE__)
#define rwlock_unlock_r(l) __debug_rwlock_unlock_r(l, __FILE__, __LINE__)
#define rwlock_lock_w(l) __debug_rwlock_lock_w(l, __FILE__, __LINE__)
#define rwlock_unlock_w(l) __debug_rwlock_unlock_w(l, __FILE__, __LINE__)
#define cond_init(c) __debug_cond_init(c, __FILE__, __LINE__)
#define cond_wait(c,m) __debug_cond_wait(c,m, __FILE__, __LINE__)
#define cond_signal(c) __debug_cond_signal(c, __FILE__, __LINE__)
#define cond_broadcast(c) __debug_cond_broadcast(c, __FILE__, __LINE__)
#define COND_STATIC_INIT PTHREAD_COND_INITIALIZER
#ifndef __THREAD_DEBUG
#define __debug_mutex_init(m, F, L) pthread_mutex_init(m, NULL)
#define __debug_mutex_destroy(m, F, L) pthread_mutex_destroy(m)
#define __debug_mutex_lock(m, F, L) pthread_mutex_lock(m)
#define __debug_mutex_trylock(m, F, L) pthread_mutex_trylock(m)
#define __debug_mutex_unlock(m, F, L) pthread_mutex_unlock(m)
#define __debug_rwlock_init(l, F, L) pthread_rwlock_init(l, NULL)
#define __debug_rwlock_destroy(l, F, L) pthread_rwlock_destroy(l)
#define __debug_rwlock_lock_r(l, F, L) pthread_rwlock_rdlock(l)
#define __debug_rwlock_unlock_r(l, F, L) pthread_rwlock_unlock(l)
#define __debug_rwlock_lock_w(l, F, L) pthread_rwlock_wrlock(l)
#define __debug_rwlock_unlock_w(l, F, L) pthread_rwlock_unlock(l)
#define __debug_cond_init(c, F, L) pthread_cond_init(c, NULL)
#define __debug_cond_wait(c, m, F, L) pthread_cond_wait(c,m)
#define __debug_cond_signal(c, F, L) pthread_cond_signal(c)
#define __debug_cond_broadcast(c, F, L) pthread_cond_broadcast(c)
#else
#include "log.h"
static inline int __debug_mutex_init(mutex_t *m, const char *file, unsigned int line) {
mylog(LOG_DEBUG, "mutex_init(%p) at %s:%u", m, file, line);
return pthread_mutex_init(m, NULL);
}
static inline int __debug_mutex_destroy(mutex_t *m, const char *file, unsigned int line) {
mylog(LOG_DEBUG, "mutex_destroy(%p) at %s:%u", m, file, line);
return pthread_mutex_destroy(m);
}
static inline int __debug_mutex_lock(mutex_t *m, const char *file, unsigned int line) {
int ret;
mylog(LOG_DEBUG, "mutex_lock(%p) at %s:%u ...", m, file, line);
ret = pthread_mutex_lock(m);
mylog(LOG_DEBUG, "mutex_lock(%p) at %s:%u returning %i", m, file, line, ret);
return ret;
}
static inline int __debug_mutex_trylock(mutex_t *m, const char *file, unsigned int line) {
int ret;
mylog(LOG_DEBUG, "mutex_trylock(%p) at %s:%u ...", m, file, line);
ret = pthread_mutex_trylock(m);
mylog(LOG_DEBUG, "mutex_trylock(%p) at %s:%u returning %i", m, file, line, ret);
return ret;
}
static inline int __debug_mutex_unlock(mutex_t *m, const char *file, unsigned int line) {
mylog(LOG_DEBUG, "mutex_unlock(%p) at %s:%u", m, file, line);
return pthread_mutex_unlock(m);
}
static inline int __debug_rwlock_init(rwlock_t *m, const char *file, unsigned int line) {
mylog(LOG_DEBUG, "rwlock_init(%p) at %s:%u", m, file, line);
return pthread_rwlock_init(m, NULL);
}
static inline int __debug_rwlock_destroy(rwlock_t *m, const char *file, unsigned int line) {
mylog(LOG_DEBUG, "rwlock_destroy(%p) at %s:%u", m, file, line);
return pthread_rwlock_destroy(m);
}
static inline int __debug_rwlock_lock_r(rwlock_t *m, const char *file, unsigned int line) {
int ret;
mylog(LOG_DEBUG, "rwlock_lock_r(%p) at %s:%u ...", m, file, line);
ret = pthread_rwlock_rdlock(m);
mylog(LOG_DEBUG, "rwlock_lock_r(%p) at %s:%u returning %i", m, file, line, ret);
return ret;
}
static inline int __debug_rwlock_lock_w(rwlock_t *m, const char *file, unsigned int line) {
int ret;
mylog(LOG_DEBUG, "rwlock_lock_w(%p) at %s:%u ...", m, file, line);
ret = pthread_rwlock_wrlock(m);
mylog(LOG_DEBUG, "rwlock_lock_w(%p) at %s:%u returning %i", m, file, line, ret);
return ret;
}
static inline int __debug_rwlock_unlock_r(rwlock_t *m, const char *file, unsigned int line) {
mylog(LOG_DEBUG, "rwlock_unlock_r(%p) at %s:%u", m, file, line);
return pthread_rwlock_unlock(m);
}
static inline int __debug_rwlock_unlock_w(rwlock_t *m, const char *file, unsigned int line) {
mylog(LOG_DEBUG, "rwlock_unlock_w(%p) at %s:%u", m, file, line);
return pthread_rwlock_unlock(m);
}
#define __debug_cond_init(c, F, L) pthread_cond_init(c, NULL)
#define __debug_cond_wait(c, m, F, L) pthread_cond_wait(c,m)
#define __debug_cond_signal(c, F, L) pthread_cond_signal(c)
#define __debug_cond_broadcast(c, F, L) pthread_cond_broadcast(c)
#endif
void threads_join_all(int);
void thread_create_detach(void (*)(void *), void *);

@ -96,7 +96,7 @@ int bencode_buffer_init(bencode_buffer_t *buf) {
return 0;
}
static void *__bencode_alloc(bencode_buffer_t *buf, unsigned int size) {
void *bencode_buffer_alloc(bencode_buffer_t *buf, unsigned int size) {
struct __bencode_buffer_piece *piece;
void *ret;
@ -143,7 +143,7 @@ void bencode_buffer_free(bencode_buffer_t *buf) {
static bencode_item_t *__bencode_item_alloc(bencode_buffer_t *buf, unsigned int payload) {
bencode_item_t *ret;
ret = __bencode_alloc(buf, sizeof(struct bencode_item) + payload);
ret = bencode_buffer_alloc(buf, sizeof(struct bencode_item) + payload);
if (!ret)
return NULL;
ret->buffer = buf;
@ -218,7 +218,7 @@ static bencode_item_t *__bencode_string_alloc(bencode_buffer_t *buf, const void
}
bencode_item_t *bencode_string_len_dup(bencode_buffer_t *buf, const char *s, int len) {
char *sd = __bencode_alloc(buf, len);
char *sd = bencode_buffer_alloc(buf, len);
if (!sd)
return NULL;
memcpy(sd, s, len);
@ -361,7 +361,7 @@ struct iovec *bencode_iovec(bencode_item_t *root, int *cnt, unsigned int head, u
assert(cnt != NULL);
assert(root->iov_cnt > 0);
ret = __bencode_alloc(root->buffer, sizeof(*ret) * (root->iov_cnt + head + tail));
ret = bencode_buffer_alloc(root->buffer, sizeof(*ret) * (root->iov_cnt + head + tail));
if (!ret)
return NULL;
*cnt = __bencode_iovec_dump(ret + head, root);
@ -376,7 +376,7 @@ char *bencode_collapse(bencode_item_t *root, int *len) {
return NULL;
assert(root->str_len > 0);
ret = __bencode_alloc(root->buffer, root->str_len + 1);
ret = bencode_buffer_alloc(root->buffer, root->str_len + 1);
if (!ret)
return NULL;
l = __bencode_str_dump(ret, root);
@ -693,7 +693,7 @@ void bencode_buffer_destroy_add(bencode_buffer_t *buf, free_func_t func, void *p
if (!p)
return;
li = __bencode_alloc(buf, sizeof(*li));
li = bencode_buffer_alloc(buf, sizeof(*li));
if (!li)
return;
li->ptr = p;

@ -72,6 +72,9 @@ struct bencode_buffer {
* Returns 0 on success or -1 on failure (if no memory could be allocated). */
int bencode_buffer_init(bencode_buffer_t *buf);
/* Allocate a piece of memory from the given buffer object */
void *bencode_buffer_alloc(bencode_buffer_t *, unsigned int);
/* Destroys a previously initialized bencode_buffer_t object. All memory used by the object is freed
* and all objects created through it become invalid. */
void bencode_buffer_free(bencode_buffer_t *buf);

File diff suppressed because it is too large Load Diff

@ -8,63 +8,93 @@
#include <glib.h>
#include <time.h>
#include <pcre.h>
#include <openssl/x509.h>
enum stream_address_format {
SAF_TCP,
SAF_UDP,
SAF_NG,
SAF_ICE,
};
enum stream_direction {
DIR_UNKNOWN = 0,
DIR_INTERNAL,
DIR_EXTERNAL,
};
enum call_opmode {
OP_OFFER = 0,
OP_ANSWER = 1,
OP_OTHER,
};
enum transport_protocol_index {
PROTO_RTP_AVP = 0,
PROTO_RTP_SAVP,
PROTO_RTP_AVPF,
PROTO_RTP_SAVPF,
PROTO_UDP_TLS_RTP_SAVP,
PROTO_UDP_TLS_RTP_SAVPF,
};
struct call_monologue;
#include "control_tcp.h"
#include "control_udp.h"
#include "obj.h"
#include "aux.h"
#include "bencode.h"
#include "str.h"
#include "crypto.h"
#include "dtls.h"
#define MAX_RTP_PACKET_SIZE 8192
#define RTP_BUFFER_HEAD_ROOM 128
#define RTP_BUFFER_TAIL_ROOM 256
#define RTP_BUFFER_TAIL_ROOM 512
#define RTP_BUFFER_SIZE (MAX_RTP_PACKET_SIZE + RTP_BUFFER_HEAD_ROOM + RTP_BUFFER_TAIL_ROOM)
#ifdef __DEBUG
#define __C_DBG(x...) ilog(LOG_DEBUG, x)
#else
#define __C_DBG(x...) ((void)0)
#endif
struct poller;
struct control_stream;
struct peer;
struct callstream;
struct call;
struct callmaster;
struct redis;
struct crypto_suite;
struct mediaproxy_srtp;
struct streamhandler;
struct sdp_ng_flags;
typedef bencode_buffer_t call_buffer_t;
#define call_buffer_alloc bencode_buffer_alloc
#define call_buffer_init bencode_buffer_init
#define call_buffer_free bencode_buffer_free
enum stream_address_format {
SAF_TCP,
SAF_UDP,
SAF_NG,
SAF_ICE,
};
enum stream_direction {
DIR_UNKNOWN = 0,
DIR_INTERNAL,
DIR_EXTERNAL,
};
enum call_opmode {
OP_OFFER = 0,
OP_ANSWER = 1,
OP_OTHER,
};
enum transport_protocol {
PROTO_UNKNOWN = 0,
PROTO_RTP_AVP,
PROTO_RTP_SAVP,
PROTO_RTP_AVPF,
PROTO_RTP_SAVPF,
__PROTO_LAST
struct transport_protocol {
enum transport_protocol_index index;
const char *name;
int srtp:1;
int avpf:1;
};
extern const char *transport_protocol_strings[__PROTO_LAST];
extern const struct transport_protocol transport_protocols[];
struct stats {
u_int64_t packets;
@ -72,91 +102,161 @@ struct stats {
u_int64_t errors;
};
struct stream {
struct udp_fd {
int fd;
u_int16_t localport;
};
struct endpoint {
struct in6_addr ip46;
u_int16_t port;
int num;
enum transport_protocol protocol;
};
struct stream_input {
struct stream stream;
struct stream_params {
unsigned int index; /* starting with 1 */
str type;
struct endpoint rtp_endpoint;
struct endpoint rtcp_endpoint;
unsigned int consecutive_ports;
const struct transport_protocol *protocol;
struct crypto_params crypto;
unsigned int sdes_tag;
enum stream_direction direction[2];
int consecutive_num;
struct crypto_context crypto;
int has_rtcp:1;
int is_rtcp:1;
int desired_family;
struct dtls_fingerprint fingerprint;
int no_rtcp:1;
int implicit_rtcp:1;
int rtcp_mux:1;
int send:1;
int recv:1;
int asymmetric:1;
int setup_active:1;
int setup_passive:1;
};
struct udp_fd {
int fd;
u_int16_t localport;
struct stream_fd {
struct obj obj;
struct udp_fd fd; /* RO */
struct call *call; /* RO */
struct packet_stream *stream; /* LOCK: call->master_lock */
struct crypto_context crypto; /* IN direction, LOCK: stream->in_lock */
struct dtls_connection dtls; /* LOCK: stream->in_lock */
};
struct streamrelay;
struct mediaproxy_srtp;
struct streamhandler;
struct endpoint_map {
struct endpoint endpoint;
GQueue sfds;
int wildcard:1;
};
struct streamrelay {
struct udp_fd fd;
struct stream peer;
struct stream peer_advertised;
unsigned char idx;
struct peer *up;
struct streamrelay *other;
struct stats stats;
struct stats kstats;
time_t last;
const struct streamhandler *handler;
struct crypto_context_pair crypto;
int stun:1;
int rtcp:1;
int rtcp_mux:1;
struct packet_stream {
mutex_t in_lock,
out_lock;
/* Both locks valid only with call->master_lock held in R.
* Preempted by call->master_lock held in W.
* If both in/out are to be locked, in_lock must be locked first. */
struct call_media *media; /* RO */
struct call *call; /* RO */
struct stream_fd *sfd; /* LOCK: call->master_lock */
struct packet_stream *rtp_sink; /* LOCK: call->master_lock */
struct packet_stream *rtcp_sink; /* LOCK: call->master_lock */
struct packet_stream *rtcp_sibling; /* LOCK: call->master_lock */
const struct streamhandler *handler; /* LOCK: in_lock */
struct endpoint endpoint; /* LOCK: out_lock */
struct endpoint advertised_endpoint; /* RO */
struct crypto_context crypto; /* OUT direction, LOCK: out_lock */
struct stats stats; /* LOCK: in_lock */
struct stats kernel_stats; /* LOCK: in_lock */
time_t last_packet; /* LOCK: in_lock */
X509 *dtls_cert; /* LOCK: in_lock */
/* in_lock must be held for SETTING these: */
/* (XXX replace with atomic ops where appropriate) */
int rtp:1;
int rtcp:1;
int implicit_rtcp:1;
int fallback_rtcp:1;
int stun:1;
int filled:1;
int confirmed:1;
int kernelized:1;
int no_kernel_support:1;
int has_handler:1;
int fingerprint_verified:1;
};
struct relays_cache {
struct udp_fd relays_A[16];
struct udp_fd relays_B[16];
struct udp_fd *array_ptrs[2];
int relays_open;
};
struct peer {
struct streamrelay rtps[2];
str tag;
char *codec;
unsigned char idx;
struct callstream *up;
struct peer *other;
/* protected by call->master_lock, except the RO elements */
struct call_media {
struct call_monologue *monologue; /* RO */
struct call *call; /* RO */
unsigned int index; /* RO */
str type; /* RO */
const struct transport_protocol *protocol;
int desired_family;
str ice_ufrag;
str ice_pwd;
int kernelized:1;
int filled:1;
int confirmed:1;
struct {
struct crypto_params params;
unsigned int tag;
} sdes_in,
sdes_out;
struct dtls_fingerprint fingerprint; /* as received */
GQueue streams; /* normally RTP + RTCP */
GSList *endpoint_maps;
int initialized:1;
int asymmetric:1;
int send:1;
int recv:1;
int rtcp_mux:1;
int rtcp_mux_override:1;
int dtls:1;
int sdes:1;
int setup_active:1;
int setup_passive:1;
};
struct callstream {
struct obj obj;
mutex_t lock;
struct peer peers[2];
struct call *call;
int num;
/* half a dialogue */
/* protected by call->master_lock, except the RO elements */
struct call_monologue {
struct call *call; /* RO */
str tag;
time_t created; /* RO */
GHashTable *other_tags;
struct call_monologue *active_dialogue;
GQueue medias;
};
struct call {
struct obj obj;
struct callmaster *callmaster;
struct callmaster *callmaster; /* RO */
mutex_t chunk_lock;
GStringChunk *chunk;
mutex_t buffer_lock;
call_buffer_t buffer;
mutex_t lock;
GQueue *callstreams;
GHashTable *branches;
/* everything below protected by master_lock */
rwlock_t master_lock;
GSList *monologues;
GHashTable *tags;
//GHashTable *branches;
GSList *streams;
GSList *stream_fds;
struct dtls_cert *dtls_cert; /* for outgoing */
str callid;
str callid;
char redis_uuid[37];
time_t created;
time_t lookup_done;
time_t last_signal;
};
struct callmaster_config {
@ -175,15 +275,40 @@ struct callmaster_config {
unsigned char tos;
};
struct callmaster;
struct callmaster {
struct obj obj;
rwlock_t hashlock;
GHashTable *callhash;
mutex_t portlock;
u_int16_t lastport;
BIT_ARRAY_DECLARE(ports_used, 0x10000);
mutex_t statspslock;
struct stats statsps; /* per second stats, running timer */
mutex_t statslock;
struct stats stats; /* copied from statsps once a second */
struct poller *poller;
pcre *info_re;
pcre_extra *info_ree;
pcre *streams_re;
pcre_extra *streams_ree;
struct callmaster_config conf;
};
struct call_stats {
time_t newest;
struct stats totals[4]; /* rtp in, rtcp in, rtp out, rtcp out */
};
struct callmaster *callmaster_new(struct poller *);
void callmaster_config(struct callmaster *m, struct callmaster_config *c);
void callmaster_exclude_port(struct callmaster *m, u_int16_t p);
int callmaster_has_ipv6(struct callmaster *);
void callmaster_msg_mh_src(struct callmaster *, struct msghdr *);
void callmaster_get_all_calls(struct callmaster *m, GQueue *q);
str *call_request_tcp(char **, struct callmaster *);
@ -204,39 +329,51 @@ const char *call_query_ng(bencode_item_t *, struct callmaster *, bencode_item_t
void calls_dump_redis(struct callmaster *);
struct call *call_get_or_create(const str *callid, const str *viabranch, struct callmaster *m);
struct callstream *callstream_new(struct call *ca, int num);
void callstream_init(struct callstream *s, struct relays_cache *);
void kernelize(struct callstream *c);
int call_stream_address(char *o, struct peer *p, enum stream_address_format format, int *len);
int call_stream_address_alt(char *o, struct peer *p, enum stream_address_format format, int *len);
struct call *call_get_or_create(const str *callid, struct callmaster *m);
struct call *call_get_opmode(const str *callid, struct callmaster *m, enum call_opmode opmode);
struct call_monologue *call_get_mono_dialogue(struct call *call, const str *fromtag, const str *totag);
int monologue_offer_answer(struct call_monologue *monologue, GQueue *streams, const struct sdp_ng_flags *flags);
int call_delete_branch(struct callmaster *m, const str *callid, const str *branch,
const str *fromtag, const str *totag, bencode_item_t *output);
void stats_query(struct call *call, const str *fromtag, const str *totag, struct call_stats *stats,
void (*cb)(struct packet_stream *, void *), void *arg);
void relays_cache_init(struct relays_cache *c);
int relays_cache_want_ports(struct relays_cache *c, int portA, int portB, struct call *call);
void relays_cache_cleanup(struct relays_cache *c, struct callmaster *m);
void kernelize(struct packet_stream *);
int call_stream_address_alt(char *, struct packet_stream *, enum stream_address_format, int *);
int call_stream_address(char *, struct packet_stream *, enum stream_address_format, int *);
enum transport_protocol transport_protocol(const str *s);
const struct transport_protocol *transport_protocol(const str *s);
static inline char *call_strdup(struct call *c, const char *s) {
static inline void *call_malloc(struct call *c, size_t l) {
void *ret;
mutex_lock(&c->buffer_lock);
ret = call_buffer_alloc(&c->buffer, l);
mutex_unlock(&c->buffer_lock);
return ret;
}
static inline char *call_strdup_len(struct call *c, const char *s, unsigned int len) {
char *r;
r = call_malloc(c, len + 1);
memcpy(r, s, len);
r[len] = 0;
return r;
}
static inline char *call_strdup(struct call *c, const char *s) {
if (!s)
return NULL;
mutex_lock(&c->chunk_lock);
r = g_string_chunk_insert(c->chunk, s);
mutex_unlock(&c->chunk_lock);
return r;
return call_strdup_len(c, s, strlen(s));
}
static inline str *call_str_cpy_len(struct call *c, str *out, const char *in, int len) {
if (!in) {
*out = STR_NULL;
return out;
}
mutex_lock(&c->chunk_lock);
out->s = g_string_chunk_insert_len(c->chunk, in, len);
mutex_unlock(&c->chunk_lock);
out->s = call_strdup_len(c, in, len);
out->len = len;
return out;
}
@ -248,9 +385,8 @@ static inline str *call_str_cpy_c(struct call *c, str *out, const char *in) {
}
static inline str *call_str_dup(struct call *c, const str *in) {
str *out;
mutex_lock(&c->chunk_lock);
out = str_chunk_insert(c->chunk, in);
mutex_unlock(&c->chunk_lock);
out = call_malloc(c, sizeof(*out));
call_str_cpy_len(c, out, in->s, in->len);
return out;
}
static inline str *call_str_init_dup(struct call *c, char *s) {
@ -258,7 +394,22 @@ static inline str *call_str_init_dup(struct call *c, char *s) {
str_init(&t, s);
return call_str_dup(c, &t);
}
static inline int callmaster_has_ipv6(struct callmaster *m) {
return is_addr_unspecified(&m->conf.ipv6) ? 0 : 1;
}
static inline void callmaster_exclude_port(struct callmaster *m, u_int16_t p) {
/* XXX atomic bit field? */
mutex_lock(&m->portlock);
bit_array_set(m->ports_used, p);
mutex_unlock(&m->portlock);
}
static inline struct packet_stream *packet_stream_sink(struct packet_stream *ps) {
struct packet_stream *ret;
ret = ps->rtp_sink;
if (!ret)
ret = ps->rtcp_sink;
return ret;
}
#endif

@ -0,0 +1,722 @@
#include "call_interfaces.h"
#include <stdio.h>
#include <unistd.h>
#include <glib.h>
#include <stdlib.h>
#include <pcre.h>
#include "call.h"
#include "aux.h"
#include "log.h"
#include "redis.h"
#include "sdp.h"
#include "bencode.h"
#include "str.h"
#include "control_tcp.h"
#include "control_udp.h"
static int call_stream_address_gstring(GString *o, struct packet_stream *ps, enum stream_address_format format) {
int len, ret;
char buf[64]; /* 64 bytes ought to be enough for anybody */
ret = call_stream_address(buf, ps, format, &len);
g_string_append_len(o, buf, len);
return ret;
}
static str *streams_print(GQueue *s, int start, int end, const char *prefix, enum stream_address_format format) {
GString *o;
int i, af, port;
GList *l;
struct call_media *media;
struct packet_stream *ps;
o = g_string_new_str();
if (prefix)
g_string_append_printf(o, "%s ", prefix);
for (i = start; i < end; i++) {
for (l = s->head; l; l = l->next) {
media = l->data;
if (media->index == i)
goto found;
}
ilog(LOG_WARNING, "Requested media index %i not found", i);
goto out;
found:
if (!media->streams.head) {
ilog(LOG_WARNING, "Media has no streams");
goto out;
}
ps = media->streams.head->data;
if (format == SAF_TCP)
call_stream_address_gstring(o, ps, format);
port = ps->sfd ? ps->sfd->fd.localport : 0;
g_string_append_printf(o, (format == 1) ? "%i " : " %i", port);
if (format == SAF_UDP) {
af = call_stream_address_gstring(o, ps, format);
g_string_append_printf(o, " %c", (af == AF_INET) ? '4' : '6');
}
}
out:
g_string_append(o, "\n");
return g_string_free_str(o);
}
static int addr_parse_udp(struct stream_params *sp, char **out) {
u_int32_t ip4;
const char *cp;
char c;
int i;
ZERO(*sp);
if (out[RE_UDP_UL_ADDR4] && *out[RE_UDP_UL_ADDR4]) {
ip4 = inet_addr(out[RE_UDP_UL_ADDR4]);
if (ip4 == -1)
goto fail;
in4_to_6(&sp->rtp_endpoint.ip46, ip4);
}
else if (out[RE_UDP_UL_ADDR6] && *out[RE_UDP_UL_ADDR6]) {
if (inet_pton(AF_INET6, out[RE_UDP_UL_ADDR6], &sp->rtp_endpoint.ip46) != 1)
goto fail;
}
else
goto fail;
sp->rtp_endpoint.port = atoi(out[RE_UDP_UL_PORT]);
if (!sp->rtp_endpoint.port && strcmp(out[RE_UDP_UL_PORT], "0"))
goto fail;
if (out[RE_UDP_UL_FLAGS] && *out[RE_UDP_UL_FLAGS]) {
i = 0;
for (cp =out[RE_UDP_UL_FLAGS]; *cp && i < 2; cp++) {
c = chrtoupper(*cp);
if (c == 'E')
sp->direction[i++] = DIR_EXTERNAL;
else if (c == 'I')
sp->direction[i++] = DIR_INTERNAL;
}
}
if (out[RE_UDP_UL_NUM] && *out[RE_UDP_UL_NUM])
sp->index = atoi(out[RE_UDP_UL_NUM]);
if (!sp->index)
sp->index = 1;
sp->consecutive_ports = 1;
return 0;
fail:
return -1;
}
static str *call_update_lookup_udp(char **out, struct callmaster *m, enum call_opmode opmode) {
struct call *c;
struct call_monologue *monologue;
GQueue q = G_QUEUE_INIT;
struct stream_params sp;
str *ret, callid, viabranch, fromtag, totag = STR_NULL;
str_init(&callid, out[RE_UDP_UL_CALLID]);
str_init(&viabranch, out[RE_UDP_UL_VIABRANCH]);
str_init(&fromtag, out[RE_UDP_UL_FROMTAG]);
if (opmode == OP_ANSWER)
str_init(&totag, out[RE_UDP_UL_TOTAG]);
c = call_get_opmode(&callid, m, opmode);
if (!c) {
ilog(LOG_WARNING, "["STR_FORMAT"] Got UDP LOOKUP for unknown call-id",
STR_FMT(&callid));
return str_sprintf("%s 0 " IPF "\n", out[RE_UDP_COOKIE], IPP(m->conf.ipv4));
}
monologue = call_get_mono_dialogue(c, &fromtag, &totag);
if (addr_parse_udp(&sp, out))
goto fail;
g_queue_push_tail(&q, &sp);
/* XXX return value */
monologue_offer_answer(monologue, &q, NULL);
g_queue_clear(&q);
ret = streams_print(&monologue->medias, sp.index, sp.index, out[RE_UDP_COOKIE], SAF_UDP);
rwlock_unlock_w(&c->master_lock);
redis_update(c, m->conf.redis);
ilog(LOG_INFO, "Returning to SIP proxy: "STR_FORMAT"", STR_FMT(ret));
goto out;
fail:
rwlock_unlock_w(&c->master_lock);
ilog(LOG_WARNING, "Failed to parse a media stream: %s/%s:%s", out[RE_UDP_UL_ADDR4], out[RE_UDP_UL_ADDR6], out[RE_UDP_UL_PORT]);
ret = str_sprintf("%s E8\n", out[RE_UDP_COOKIE]);
out:
obj_put(c);
return ret;
}
str *call_update_udp(char **out, struct callmaster *m) {
return call_update_lookup_udp(out, m, OP_OFFER);
}
str *call_lookup_udp(char **out, struct callmaster *m) {
return call_update_lookup_udp(out, m, OP_ANSWER);
}
static int info_parse_func(char **a, void **ret, void *p) {
GHashTable *ih = p;
g_hash_table_replace(ih, a[0], a[1]);
return -1;
}
static void info_parse(const char *s, GHashTable *ih, struct callmaster *m) {
pcre_multi_match(m->info_re, m->info_ree, s, 2, info_parse_func, ih, NULL);
}
static int streams_parse_func(char **a, void **ret, void *p) {
struct stream_params *sp;
u_int32_t ip;
int *i;
i = p;
sp = g_slice_alloc0(sizeof(*sp));
ip = inet_addr(a[0]);
if (ip == -1)
goto fail;
in4_to_6(&sp->rtp_endpoint.ip46, ip);
sp->rtp_endpoint.port = atoi(a[1]);
sp->index = ++(*i);
sp->consecutive_ports = 1;
sp->rtcp_endpoint = sp->rtp_endpoint;
sp->rtcp_endpoint.port++;
if (!sp->rtp_endpoint.port && strcmp(a[1], "0"))
goto fail;
*ret = sp;
return 0;
fail:
ilog(LOG_WARNING, "Failed to parse a media stream: %s:%s", a[0], a[1]);
g_slice_free1(sizeof(*sp), sp);
return -1;
}
static void streams_parse(const char *s, struct callmaster *m, GQueue *q) {
int i;
i = 0;
pcre_multi_match(m->streams_re, m->streams_ree, s, 3, streams_parse_func, &i, q);
}
static void streams_free(GQueue *q) {
struct stream_params *s;
while ((s = g_queue_pop_head(q))) {
if (s->crypto.mki)
free(s->crypto.mki);
g_slice_free1(sizeof(*s), s);
}
}
static str *call_request_lookup_tcp(char **out, struct callmaster *m, enum call_opmode opmode) {
struct call *c;
struct call_monologue *monologue;
GQueue s = G_QUEUE_INIT;
str *ret = NULL, callid, fromtag, totag = STR_NULL;
GHashTable *infohash;
str_init(&callid, out[RE_TCP_RL_CALLID]);
infohash = g_hash_table_new(g_str_hash, g_str_equal);
c = call_get_opmode(&callid, m, opmode);
if (!c) {
ilog(LOG_WARNING, "["STR_FORMAT"] Got LOOKUP for unknown call-id", STR_FMT(&callid));
goto out;
}
info_parse(out[RE_TCP_RL_INFO], infohash, m);
streams_parse(out[RE_TCP_RL_STREAMS], m, &s);
str_init(&fromtag, g_hash_table_lookup(infohash, "fromtag"));
if (!fromtag.s) {
ilog(LOG_WARNING, "No from-tag in message");
goto out2;
}
if (opmode == OP_ANSWER) {
str_init(&totag, g_hash_table_lookup(infohash, "totag"));
if (!totag.s) {
ilog(LOG_WARNING, "No to-tag in message");
goto out2;
}
}
monologue = call_get_mono_dialogue(c, &fromtag, &totag);
/* XXX return value */
monologue_offer_answer(monologue, &s, NULL);
ret = streams_print(&monologue->medias, 1, s.length, NULL, SAF_TCP);
rwlock_unlock_w(&c->master_lock);
out2:
streams_free(&s);
redis_update(c, m->conf.redis);
ilog(LOG_INFO, "Returning to SIP proxy: "STR_FORMAT"", STR_FMT0(ret));
obj_put(c);
out:
g_hash_table_destroy(infohash);
return ret;
}
str *call_request_tcp(char **out, struct callmaster *m) {
return call_request_lookup_tcp(out, m, OP_OFFER);
}
str *call_lookup_tcp(char **out, struct callmaster *m) {
return call_request_lookup_tcp(out, m, OP_ANSWER);
}
str *call_delete_udp(char **out, struct callmaster *m) {
str callid, branch, fromtag, totag;
__C_DBG("got delete for callid '%s' and viabranch '%s'",
out[RE_UDP_DQ_CALLID], out[RE_UDP_DQ_VIABRANCH]);
str_init(&callid, out[RE_UDP_DQ_CALLID]);
str_init(&branch, out[RE_UDP_DQ_VIABRANCH]);
str_init(&fromtag, out[RE_UDP_DQ_FROMTAG]);
str_init(&totag, out[RE_UDP_DQ_TOTAG]);
if (call_delete_branch(m, &callid, &branch, &fromtag, &totag, NULL))
return str_sprintf("%s E8\n", out[RE_UDP_COOKIE]);
return str_sprintf("%s 0\n", out[RE_UDP_COOKIE]);
}
str *call_query_udp(char **out, struct callmaster *m) {
struct call *c;
str *ret, callid, fromtag, totag;
struct call_stats stats;
__C_DBG("got query for callid '%s'", out[RE_UDP_DQ_CALLID]);
str_init(&callid, out[RE_UDP_DQ_CALLID]);
str_init(&fromtag, out[RE_UDP_DQ_FROMTAG]);
str_init(&totag, out[RE_UDP_DQ_TOTAG]);
c = call_get_opmode(&callid, m, OP_OTHER);
if (!c) {
ilog(LOG_INFO, "["STR_FORMAT"] Call-ID to query not found", STR_FMT(&callid));
goto err;
}
stats_query(c, &fromtag, &totag, &stats, NULL, NULL);
rwlock_unlock_w(&c->master_lock);
ret = str_sprintf("%s %lld "UINT64F" "UINT64F" "UINT64F" "UINT64F"\n", out[RE_UDP_COOKIE],
(long long int) m->conf.silent_timeout - (poller_now - stats.newest),
stats.totals[0].packets, stats.totals[1].packets,
stats.totals[2].packets, stats.totals[3].packets);
goto out;
err:
if (c)
rwlock_unlock_w(&c->master_lock);
ret = str_sprintf("%s E8\n", out[RE_UDP_COOKIE]);
goto out;
out:
if (c)
obj_put(c);
return ret;
}
void call_delete_tcp(char **out, struct callmaster *m) {
str callid;
str_init(&callid, out[RE_TCP_D_CALLID]);
call_delete_branch(m, &callid, NULL, NULL, NULL, NULL);
}
static void call_status_iterator(struct call *c, struct control_stream *s) {
// GList *l;
// struct callstream *cs;
// struct peer *p;
// struct streamrelay *r1, *r2;
// struct streamrelay *rx1, *rx2;
// struct callmaster *m;
// char addr1[64], addr2[64], addr3[64];
// m = c->callmaster;
// mutex_lock(&c->master_lock);
control_stream_printf(s, "session "STR_FORMAT" - - - - %i\n",
STR_FMT(&c->callid),
(int) (poller_now - c->created));
/* XXX restore function */
// mutex_unlock(&c->master_lock);
}
void calls_status_tcp(struct callmaster *m, struct control_stream *s) {
struct stats st;
GQueue q = G_QUEUE_INIT;
struct call *c;
mutex_lock(&m->statslock);
st = m->stats;
mutex_unlock(&m->statslock);
callmaster_get_all_calls(m, &q);
control_stream_printf(s, "proxy %u "UINT64F"/"UINT64F"/"UINT64F"\n",
g_queue_get_length(&q),
st.bytes, st.bytes - st.errors,
st.bytes * 2 - st.errors);
while (q.head) {
c = g_queue_pop_head(&q);
call_status_iterator(c, s);
obj_put(c);
}
}
static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *input) {
bencode_item_t *list, *it;
int diridx;
str s;
ZERO(*out);
if ((list = bencode_dictionary_get_expect(input, "flags", BENCODE_LIST))) {
for (it = list->child; it; it = it->sibling) {
if (!bencode_strcmp(it, "trust address"))
out->trust_address = 1;
else if (!bencode_strcmp(it, "symmetric"))
out->symmetric = 1;
else if (!bencode_strcmp(it, "asymmetric"))
out->asymmetric = 1;
else if (!bencode_strcmp(it, "trust-address"))
out->trust_address = 1;
}
}
if ((list = bencode_dictionary_get_expect(input, "replace", BENCODE_LIST))) {
for (it = list->child; it; it = it->sibling) {
if (!bencode_strcmp(it, "origin"))
out->replace_origin = 1;
else if (!bencode_strcmp(it, "session connection"))
out->replace_sess_conn = 1;
else if (!bencode_strcmp(it, "session-connection"))
out->replace_sess_conn = 1;
}
}
diridx = 0;
if ((list = bencode_dictionary_get_expect(input, "direction", BENCODE_LIST))) {
for (it = list->child; it && diridx < 2; it = it->sibling) {
if (!bencode_strcmp(it, "internal"))
out->directions[diridx++] = DIR_INTERNAL;
else if (!bencode_strcmp(it, "external"))
out->directions[diridx++] = DIR_EXTERNAL;
}
}
list = bencode_dictionary_get_expect(input, "received from", BENCODE_LIST);
if (!list)
list = bencode_dictionary_get_expect(input, "received-from", BENCODE_LIST);
if (list && (it = list->child)) {
bencode_get_str(it, &out->received_from_family);
bencode_get_str(it->sibling, &out->received_from_address);
}
if (bencode_dictionary_get_str(input, "ICE", &s)) {
if (!str_cmp(&s, "remove"))
out->ice_remove = 1;
else if (!str_cmp(&s, "force"))
out->ice_force = 1;
}
if ((list = bencode_dictionary_get_expect(input, "rtcp-mux", BENCODE_LIST))) {
for (it = list->child; it; it = it->sibling) {
if (!bencode_strcmp(it, "offer"))
out->rtcp_mux_offer = 1;
else if (!bencode_strcmp(it, "demux"))
out->rtcp_mux_demux = 1;
else if (!bencode_strcmp(it, "accept"))
out->rtcp_mux_accept = 1;
else if (!bencode_strcmp(it, "reject"))
out->rtcp_mux_reject = 1;
}
}
bencode_dictionary_get_str(input, "transport protocol", &out->transport_protocol_str);
if (!out->transport_protocol_str.s)
bencode_dictionary_get_str(input, "transport-protocol", &out->transport_protocol_str);
out->transport_protocol = transport_protocol(&out->transport_protocol_str);
bencode_dictionary_get_str(input, "media address", &out->media_address);
if (bencode_dictionary_get_str(input, "address family", &out->address_family_str))
out->address_family = address_family(&out->address_family_str);
}
static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster *m,
bencode_item_t *output, enum call_opmode opmode)
{
str sdp, fromtag, totag = STR_NULL, callid;
char *errstr;
GQueue parsed = G_QUEUE_INIT;
GQueue streams = G_QUEUE_INIT;
struct call *call;
struct call_monologue *monologue;
int ret;
struct sdp_ng_flags flags;
struct sdp_chopper *chopper;
if (!bencode_dictionary_get_str(input, "sdp", &sdp))
return "No SDP body in message";
if (!bencode_dictionary_get_str(input, "call-id", &callid))
return "No call-id in message";
if (!bencode_dictionary_get_str(input, "from-tag", &fromtag))
return "No from-tag in message";
if (opmode == OP_ANSWER) {
if (!bencode_dictionary_get_str(input, "to-tag", &totag))
return "No to-tag in message";
}
//bencode_dictionary_get_str(input, "via-branch", &viabranch);
if (sdp_parse(&sdp, &parsed))
return "Failed to parse SDP";
call_ng_process_flags(&flags, input);
flags.opmode = opmode;
errstr = "Incomplete SDP specification";
if (sdp_streams(&parsed, &streams, &flags))
goto out;
call = call_get_opmode(&callid, m, opmode);
errstr = "Unknown call-id";
if (!call)
goto out;
monologue = call_get_mono_dialogue(call, &fromtag, &totag);
chopper = sdp_chopper_new(&sdp);
bencode_buffer_destroy_add(output->buffer, (free_func_t) sdp_chopper_destroy, chopper);
/* XXX return value */
monologue_offer_answer(monologue, &streams, &flags);
ret = sdp_replace(chopper, &parsed, monologue, &flags);
rwlock_unlock_w(&call->master_lock);
redis_update(call, m->conf.redis);
obj_put(call);
errstr = "Error rewriting SDP";
if (ret)
goto out;
bencode_dictionary_add_iovec(output, "sdp", &g_array_index(chopper->iov, struct iovec, 0),
chopper->iov_num, chopper->str_len);
bencode_dictionary_add_string(output, "result", "ok");
errstr = NULL;
out:
sdp_free(&parsed);
streams_free(&streams);
return errstr;
}
const char *call_offer_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) {
return call_offer_answer_ng(input, m, output, OP_OFFER);
}
const char *call_answer_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) {
return call_offer_answer_ng(input, m, output, OP_ANSWER);
}
const char *call_delete_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) {
str fromtag, totag, viabranch, callid;
bencode_item_t *flags, *it;
int fatal = 0;
if (!bencode_dictionary_get_str(input, "call-id", &callid))
return "No call-id in message";
if (!bencode_dictionary_get_str(input, "from-tag", &fromtag))
return "No from-tag in message";
bencode_dictionary_get_str(input, "to-tag", &totag);
bencode_dictionary_get_str(input, "via-branch", &viabranch);
flags = bencode_dictionary_get_expect(input, "flags", BENCODE_LIST);
if (flags) {
for (it = flags->child; it; it = it->sibling) {
if (!bencode_strcmp(it, "fatal"))
fatal = 1;
}
}
if (call_delete_branch(m, &callid, &viabranch, &fromtag, &totag, output)) {
if (fatal)
return "Call-ID not found or tags didn't match";
bencode_dictionary_add_string(output, "warning", "Call-ID not found or tags didn't match");
}
bencode_dictionary_add_string(output, "result", "ok");
return NULL;
}
#if 0
static bencode_item_t *peer_address(bencode_buffer_t *b, struct stream *s) {
bencode_item_t *d;
char buf[64];
d = bencode_dictionary(b);
if (IN6_IS_ADDR_V4MAPPED(&s->ip46)) {
bencode_dictionary_add_string(d, "family", "IPv4");
inet_ntop(AF_INET, &(s->ip46.s6_addr32[3]), buf, sizeof(buf));
}
else {
bencode_dictionary_add_string(d, "family", "IPv6");
inet_ntop(AF_INET6, &s->ip46, buf, sizeof(buf));
}
bencode_dictionary_add_string_dup(d, "address", buf);
bencode_dictionary_add_integer(d, "port", s->port);
return d;
}
#endif
#if 0
static bencode_item_t *stats_encode(bencode_buffer_t *b, struct stats *s) {
bencode_item_t *d;
d = bencode_dictionary(b);
bencode_dictionary_add_integer(d, "packets", s->packets);
bencode_dictionary_add_integer(d, "bytes", s->bytes);
bencode_dictionary_add_integer(d, "errors", s->errors);
return d;
}
#endif
#if 0
static bencode_item_t *streamrelay_stats(bencode_buffer_t *b, struct packet_stream *ps) {
bencode_item_t *d;
d = bencode_dictionary(b);
// XXX
//bencode_dictionary_add(d, "counters", stats_encode(b, &r->stats));
//bencode_dictionary_add(d, "peer address", peer_address(b, &r->peer));
//bencode_dictionary_add(d, "advertised peer address", peer_address(b, &r->peer_advertised));
bencode_dictionary_add_integer(d, "local port", ps->fd.localport);
return d;
}
#endif
#if 0
static bencode_item_t *rtp_rtcp_stats(bencode_buffer_t *b, struct stats *rtp, struct stats *rtcp) {
bencode_item_t *s;
s = bencode_dictionary(b);
bencode_dictionary_add(s, "rtp", stats_encode(b, rtp));
bencode_dictionary_add(s, "rtcp", stats_encode(b, rtcp));
return s;
}
#endif
#if 0
XXX
static bencode_item_t *peer_stats(bencode_buffer_t *b, struct peer *p) {
bencode_item_t *d, *s;
d = bencode_dictionary(b);
bencode_dictionary_add_str_dup(d, "tag", &p->tag);
if (p->codec)
bencode_dictionary_add_string(d, "codec", p->codec);
if (p->kernelized)
bencode_dictionary_add_string(d, "status", "in kernel");
else if (p->confirmed)
bencode_dictionary_add_string(d, "status", "confirmed peer address");
else if (p->filled)
bencode_dictionary_add_string(d, "status", "known but unconfirmed peer address");
else
bencode_dictionary_add_string(d, "status", "unknown peer address");
s = bencode_dictionary_add_dictionary(d, "stats");
bencode_dictionary_add(s, "rtp", streamrelay_stats(b, &p->rtps[0]));
bencode_dictionary_add(s, "rtcp", streamrelay_stats(b, &p->rtps[1]));
return d;
}
static void ng_stats_cb(struct peer *p, struct peer *px, void *streams) {
bencode_item_t *stream;
stream = bencode_list_add_list(streams);
bencode_list_add(stream, peer_stats(stream->buffer, p));
bencode_list_add(stream, peer_stats(stream->buffer, px));
}
#endif
/* call must be locked */
void ng_call_stats(struct call *call, const str *fromtag, const str *totag, bencode_item_t *output) {
//bencode_item_t *streams, *dict;
// struct call_stats stats;
// bencode_dictionary_add_integer(output, "created", call->created);
//streams = bencode_dictionary_add_list(output, "streams");
//stats_query(call, fromtag, totag, &stats, ng_stats_cb, streams); XXX
// dict = bencode_dictionary_add_dictionary(output, "totals");
// bencode_dictionary_add(dict, "input", rtp_rtcp_stats(output->buffer, &stats.totals[0], &stats.totals[1]));
// bencode_dictionary_add(dict, "output", rtp_rtcp_stats(output->buffer, &stats.totals[2], &stats.totals[3]));
}
const char *call_query_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) {
str callid, fromtag, totag;
struct call *call;
if (!bencode_dictionary_get_str(input, "call-id", &callid))
return "No call-id in message";
call = call_get_opmode(&callid, m, OP_OTHER);
if (!call)
return "Unknown call-id";
bencode_dictionary_get_str(input, "from-tag", &fromtag);
bencode_dictionary_get_str(input, "to-tag", &totag);
bencode_dictionary_add_string(output, "result", "ok");
ng_call_stats(call, &fromtag, &totag, output);
rwlock_unlock_w(&call->master_lock);
return NULL;
}

@ -0,0 +1,18 @@
#ifndef _CALL_INTERFACES_H_
#define _CALL_INTERFACES_H_
#include "str.h"
#include "bencode.h"
struct call;
void ng_call_stats(struct call *call, const str *fromtag, const str *totag, bencode_item_t *output);
#endif

@ -67,7 +67,7 @@ static void control_ng_incoming(struct obj *obj, str *buf, struct sockaddr_in6 *
str_chr_str(&data, buf, ' ');
if (!data.s || data.s == buf->s) {
mylog(LOG_WARNING, "Received invalid data on NG port (no cookie) from %s: %.*s", addr, STR_FMT(buf));
ilog(LOG_WARNING, "Received invalid data on NG port (no cookie) from %s: "STR_FORMAT, addr, STR_FMT(buf));
return;
}
@ -85,7 +85,7 @@ static void control_ng_incoming(struct obj *obj, str *buf, struct sockaddr_in6 *
to_send = cookie_cache_lookup(&c->cookie_cache, &cookie);
if (to_send) {
mylog(LOG_INFO, "Detected command from %s as a duplicate", addr);
ilog(LOG_INFO, "Detected command from %s as a duplicate", addr);
resp = NULL;
goto send_only;
}
@ -103,7 +103,7 @@ static void control_ng_incoming(struct obj *obj, str *buf, struct sockaddr_in6 *
log_str = g_string_sized_new(256);
g_string_append_printf(log_str, "Got valid command from %s: %.*s - ", addr, STR_FMT(&cmd));
pretty_print(dict, log_str);
mylog(LOG_INFO, "%.*s", (int) log_str->len, log_str->str);
ilog(LOG_INFO, "%.*s", (int) log_str->len, log_str->str);
g_string_free(log_str, TRUE);
errstr = NULL;
@ -126,7 +126,7 @@ static void control_ng_incoming(struct obj *obj, str *buf, struct sockaddr_in6 *
goto send_resp;
err_send:
mylog(LOG_WARNING, "Protocol error in packet from %s: %s [%.*s]", addr, errstr, STR_FMT(&data));
ilog(LOG_WARNING, "Protocol error in packet from %s: %s ["STR_FORMAT"]", addr, errstr, STR_FMT(&data));
bencode_dictionary_add_string(resp, "result", "error");
bencode_dictionary_add_string(resp, "error-reason", errstr);
goto send_resp;
@ -136,7 +136,7 @@ send_resp:
to_send = &reply;
send_only:
mylog(LOG_INFO, "Returning to SIP proxy: %.*s", STR_FMT(to_send));
ilog(LOG_INFO, "Returning to SIP proxy: "STR_FORMAT, STR_FMT(to_send));
ZERO(mh);
mh.msg_name = sin;
@ -162,6 +162,7 @@ send_only:
out:
bencode_buffer_free(&bencbuf);
log_info_clear();
}

@ -56,7 +56,7 @@ static void control_stream_closed(int fd, void *p, uintptr_t u) {
struct control_tcp *c;
GList *l = NULL;
mylog(LOG_INFO, "Control connection from " DF " closed", DP(s->inaddr));
ilog(LOG_INFO, "Control connection from " DF " closed", DP(s->inaddr));
c = s->control;
@ -105,11 +105,11 @@ static int control_stream_parse(struct control_stream *s, char *line) {
ret = pcre_exec(c->parse_re, c->parse_ree, line, strlen(line), 0, 0, ovec, G_N_ELEMENTS(ovec));
if (ret <= 0) {
mylog(LOG_WARNING, "Unable to parse command line from " DF ": %s", DP(s->inaddr), line);
ilog(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);
ilog(LOG_INFO, "Got valid command from " DF ": %s", DP(s->inaddr), line);
pcre_get_substring_list(line, ovec, ret, (const char ***) &out);
@ -137,6 +137,7 @@ static int control_stream_parse(struct control_stream *s, char *line) {
}
pcre_free(out);
log_info_clear();
return -1;
}
@ -166,7 +167,7 @@ static void control_stream_readable(int fd, void *p, uintptr_t u) {
while ((line = streambuf_getline(s->inbuf))) {
mutex_unlock(&s->lock);
mylog(LOG_DEBUG, "Got control line from " DF ": %s", DP(s->inaddr), line);
ilog(LOG_DEBUG, "Got control line from " DF ": %s", DP(s->inaddr), line);
ret = control_stream_parse(s, line);
free(line);
if (ret)
@ -175,7 +176,7 @@ static void control_stream_readable(int fd, void *p, uintptr_t u) {
}
if (streambuf_bufsize(s->inbuf) > 1024) {
mylog(LOG_WARNING, "Buffer length exceeded in control connection from " DF, DP(s->inaddr));
ilog(LOG_WARNING, "Buffer length exceeded in control connection from " DF, DP(s->inaddr));
goto close;
}
@ -226,7 +227,7 @@ next:
}
nonblock(nfd);
mylog(LOG_INFO, "New control connection from " DF, DP(sin));
ilog(LOG_INFO, "New control connection from " DF, DP(sin));
s = obj_alloc0("control_stream", sizeof(*s), control_stream_free);

@ -31,11 +31,11 @@ static void control_udp_incoming(struct obj *obj, str *buf, struct sockaddr_in6
if (ret <= 0) {
ret = pcre_exec(u->fallback_re, NULL, buf->s, buf->len, 0, 0, ovec, G_N_ELEMENTS(ovec));
if (ret <= 0) {
mylog(LOG_WARNING, "Unable to parse command line from udp:%s: %.*s", addr, STR_FMT(buf));
ilog(LOG_WARNING, "Unable to parse command line from udp:%s: %.*s", addr, STR_FMT(buf));
return;
}
mylog(LOG_WARNING, "Failed to properly parse UDP command line '%.*s' from %s, using fallback RE", STR_FMT(buf), addr);
ilog(LOG_WARNING, "Failed to properly parse UDP command line '%.*s' from %s, using fallback RE", STR_FMT(buf), addr);
pcre_get_substring_list(buf->s, ovec, ret, (const char ***) &out);
@ -68,14 +68,14 @@ static void control_udp_incoming(struct obj *obj, str *buf, struct sockaddr_in6
return;
}
mylog(LOG_INFO, "Got valid command from udp:%s: %.*s", addr, STR_FMT(buf));
ilog(LOG_INFO, "Got valid command from udp:%s: %.*s", addr, STR_FMT(buf));
pcre_get_substring_list(buf->s, ovec, ret, (const char ***) &out);
str_init(&cookie, (void *) out[RE_UDP_COOKIE]);
reply = cookie_cache_lookup(&u->cookie_cache, &cookie);
if (reply) {
mylog(LOG_INFO, "Detected command from udp:%s as a duplicate", addr);
ilog(LOG_INFO, "Detected command from udp:%s as a duplicate", addr);
sendto(u->udp_listener.fd, reply->s, reply->len, 0, (struct sockaddr *) sin, sizeof(*sin));
free(reply);
goto out;
@ -131,6 +131,7 @@ static void control_udp_incoming(struct obj *obj, str *buf, struct sockaddr_in6
out:
pcre_free(out);
log_info_clear();
}
struct control_udp *control_udp_new(struct poller *p, struct in6_addr ip, u_int16_t port, struct callmaster *m) {

@ -29,11 +29,14 @@ static int aes_f8_encrypt_rtcp(struct crypto_context *c, struct rtcp_packet *r,
static int aes_cm_session_key_init(struct crypto_context *c);
static int aes_f8_session_key_init(struct crypto_context *c);
static int evp_session_key_cleanup(struct crypto_context *c);
static int null_crypt_rtp(struct crypto_context *c, struct rtp_header *r, str *s, u_int64_t idx);
static int null_crypt_rtcp(struct crypto_context *c, struct rtcp_packet *r, str *s, u_int64_t idx);
/* all lengths are in bytes */
const struct crypto_suite crypto_suites[] = {
{
.name = "AES_CM_128_HMAC_SHA1_80",
.dtls_name = "SRTP_AES128_CM_SHA1_80",
.master_key_len = 16,
.master_salt_len = 14,
.session_key_len = 16,
@ -57,6 +60,7 @@ const struct crypto_suite crypto_suites[] = {
},
{
.name = "AES_CM_128_HMAC_SHA1_32",
.dtls_name = "SRTP_AES128_CM_SHA1_32",
.master_key_len = 16,
.master_salt_len = 14,
.session_key_len = 16,
@ -80,6 +84,7 @@ const struct crypto_suite crypto_suites[] = {
},
{
.name = "F8_128_HMAC_SHA1_80",
// .dtls_name = "SRTP_AES128_F8_SHA1_80",
.master_key_len = 16,
.master_salt_len = 14,
.session_key_len = 16,
@ -101,6 +106,76 @@ const struct crypto_suite crypto_suites[] = {
.session_key_init = aes_f8_session_key_init,
.session_key_cleanup = evp_session_key_cleanup,
},
{
.name = "F8_128_HMAC_SHA1_32",
// .dtls_name = "SRTP_AES128_F8_SHA1_32",
.master_key_len = 16,
.master_salt_len = 14,
.session_key_len = 16,
.session_salt_len = 14,
.srtp_lifetime = 1ULL << 48,
.srtcp_lifetime = 1ULL << 31,
.kernel_cipher = MPC_AES_F8,
.kernel_hmac = MPH_HMAC_SHA1,
.srtp_auth_tag = 4,
.srtcp_auth_tag = 10,
.srtp_auth_key_len = 20,
.srtcp_auth_key_len = 20,
.encrypt_rtp = aes_f8_encrypt_rtp,
.decrypt_rtp = aes_f8_encrypt_rtp,
.encrypt_rtcp = aes_f8_encrypt_rtcp,
.decrypt_rtcp = aes_f8_encrypt_rtcp,
.hash_rtp = hmac_sha1_rtp,
.hash_rtcp = hmac_sha1_rtcp,
.session_key_init = aes_f8_session_key_init,
.session_key_cleanup = evp_session_key_cleanup,
},
{
.name = "NULL_HMAC_SHA1_80",
// .dtls_name = "SRTP_NULL_SHA1_80",
.master_key_len = 16,
.master_salt_len = 14,
.session_key_len = 0,
.session_salt_len = 0,
.srtp_lifetime = 1ULL << 48,
.srtcp_lifetime = 1ULL << 31,
.kernel_cipher = MPC_NULL,
.kernel_hmac = MPH_HMAC_SHA1,
.srtp_auth_tag = 10,
.srtcp_auth_tag = 10,
.srtp_auth_key_len = 20,
.srtcp_auth_key_len = 20,
.encrypt_rtp = null_crypt_rtp,
.decrypt_rtp = null_crypt_rtp,
.encrypt_rtcp = null_crypt_rtcp,
.decrypt_rtcp = null_crypt_rtcp,
.hash_rtp = hmac_sha1_rtp,
.hash_rtcp = hmac_sha1_rtcp,
.session_key_cleanup = evp_session_key_cleanup,
},
{
.name = "NULL_HMAC_SHA1_32",
// .dtls_name = "SRTP_NULL_SHA1_32",
.master_key_len = 16,
.master_salt_len = 14,
.session_key_len = 0,
.session_salt_len = 0,
.srtp_lifetime = 1ULL << 48,
.srtcp_lifetime = 1ULL << 31,
.kernel_cipher = MPC_NULL,
.kernel_hmac = MPH_HMAC_SHA1,
.srtp_auth_tag = 4,
.srtcp_auth_tag = 10,
.srtp_auth_key_len = 20,
.srtcp_auth_key_len = 20,
.encrypt_rtp = null_crypt_rtp,
.decrypt_rtp = null_crypt_rtp,
.encrypt_rtcp = null_crypt_rtcp,
.decrypt_rtcp = null_crypt_rtcp,
.hash_rtp = hmac_sha1_rtp,
.hash_rtcp = hmac_sha1_rtcp,
.session_key_cleanup = evp_session_key_cleanup,
},
};
const int num_crypto_suites = G_N_ELEMENTS(crypto_suites);
@ -134,7 +209,7 @@ const struct crypto_suite *crypto_find_suite(const str *s) {
/* rfc 3711 section 4.1 and 4.1.1
* "in" and "out" MAY point to the same buffer */
static void aes_ctr_128(char *out, str *in, EVP_CIPHER_CTX *ecc, const char *iv) {
static void aes_ctr_128(unsigned char *out, str *in, EVP_CIPHER_CTX *ecc, const unsigned char *iv) {
unsigned char ivx[16];
unsigned char key_block[16];
unsigned char *p, *q;
@ -182,13 +257,13 @@ done:
;
}
static void aes_ctr_128_no_ctx(char *out, str *in, const char *key, const char *iv) {
static void aes_ctr_128_no_ctx(unsigned char *out, str *in, const unsigned char *key, const unsigned char *iv) {
EVP_CIPHER_CTX ctx;
unsigned char block[16];
int len;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, (const unsigned char *) key, NULL);
EVP_EncryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, key, NULL);
aes_ctr_128(out, in, &ctx, iv);
EVP_EncryptFinal_ex(&ctx, block, &len);
EVP_CIPHER_CTX_cleanup(&ctx);
@ -199,10 +274,10 @@ static void aes_ctr_128_no_ctx(char *out, str *in, const char *key, const char *
* x: 112 bits
* n <= 256
* out->len := n / 8 */
static void prf_n(str *out, const char *key, const char *x) {
char iv[16];
char o[32];
char in[32];
static void prf_n(str *out, const unsigned char *key, const unsigned char *x) {
unsigned char iv[16];
unsigned char o[32];
unsigned char in[32];
str in_s;
assert(sizeof(o) >= out->len);
@ -211,7 +286,7 @@ static void prf_n(str *out, const char *key, const char *x) {
memcpy(iv, x, 14);
/* iv[14] = iv[15] = 0; := x << 16 */
ZERO(in); /* outputs the key stream */
str_init_len(&in_s, in, out->len > 16 ? 32 : 16);
str_init_len(&in_s, (void *) in, out->len > 16 ? 32 : 16);
aes_ctr_128_no_ctx(o, &in_s, key, iv);
memcpy(out->s, o, out->len);
@ -230,27 +305,27 @@ int crypto_gen_session_key(struct crypto_context *c, str *out, unsigned char lab
* key_derivation_rate == 0 --> r == 0 */
key_id[0] = label;
memcpy(x, c->master_salt, 14);
memcpy(x, c->params.master_salt, 14);
for (i = 13 - index_len; i < 14; i++)
x[i] = key_id[i - (13 - index_len)] ^ x[i];
prf_n(out, c->master_key, (char *) x);
prf_n(out, c->params.master_key, x);
#if CRYPTO_DEBUG
mylog(LOG_DEBUG, "Generated session key: master key "
ilog(LOG_DEBUG, "Generated session key: master key "
"%02x%02x%02x%02x..., "
"master salt "
"%02x%02x%02x%02x..., "
"label %02x, length %i, result "
"%02x%02x%02x%02x...",
(unsigned char) c->master_key[0],
(unsigned char) c->master_key[1],
(unsigned char) c->master_key[2],
(unsigned char) c->master_key[3],
(unsigned char) c->master_salt[0],
(unsigned char) c->master_salt[1],
(unsigned char) c->master_salt[2],
(unsigned char) c->master_salt[3],
c->params.master_key[0],
c->params.master_key[1],
c->params.master_key[2],
c->params.master_key[3],
c->params.master_salt[0],
c->params.master_salt[1],
c->params.master_salt[2],
c->params.master_salt[3],
label, out->len,
(unsigned char) out->s[0],
(unsigned char) out->s[1],
@ -278,7 +353,7 @@ static int aes_cm_encrypt(struct crypto_context *c, u_int32_t ssrc, str *s, u_in
ivi[2] ^= idxh;
ivi[3] ^= idxl;
aes_ctr_128(s->s, s, c->session_key_ctx[0], (char *) iv);
aes_ctr_128((void *) s->s, s, c->session_key_ctx[0], iv);
return 0;
}
@ -389,15 +464,15 @@ static int hmac_sha1_rtp(struct crypto_context *c, char *out, str *in, u_int64_t
HMAC_CTX hc;
u_int32_t roc;
HMAC_Init(&hc, c->session_auth_key, c->crypto_suite->srtp_auth_key_len, EVP_sha1());
HMAC_Init(&hc, c->session_auth_key, c->params.crypto_suite->srtp_auth_key_len, EVP_sha1());
HMAC_Update(&hc, (unsigned char *) in->s, in->len);
roc = htonl((index & 0xffffffff0000ULL) >> 16);
HMAC_Update(&hc, (unsigned char *) &roc, sizeof(roc));
HMAC_Final(&hc, hmac, NULL);
HMAC_CTX_cleanup(&hc);
assert(sizeof(hmac) >= c->crypto_suite->srtp_auth_tag);
memcpy(out, hmac, c->crypto_suite->srtp_auth_tag);
assert(sizeof(hmac) >= c->params.crypto_suite->srtp_auth_tag);
memcpy(out, hmac, c->params.crypto_suite->srtp_auth_tag);
return 0;
}
@ -406,11 +481,11 @@ static int hmac_sha1_rtp(struct crypto_context *c, char *out, str *in, u_int64_t
static int hmac_sha1_rtcp(struct crypto_context *c, char *out, str *in) {
unsigned char hmac[20];
HMAC(EVP_sha1(), c->session_auth_key, c->crypto_suite->srtcp_auth_key_len,
HMAC(EVP_sha1(), c->session_auth_key, c->params.crypto_suite->srtcp_auth_key_len,
(unsigned char *) in->s, in->len, hmac, NULL);
assert(sizeof(hmac) >= c->crypto_suite->srtcp_auth_tag);
memcpy(out, hmac, c->crypto_suite->srtcp_auth_tag);
assert(sizeof(hmac) >= c->params.crypto_suite->srtcp_auth_tag);
memcpy(out, hmac, c->params.crypto_suite->srtcp_auth_tag);
return 0;
}
@ -433,8 +508,8 @@ static int aes_f8_session_key_init(struct crypto_context *c) {
aes_cm_session_key_init(c);
k_e_len = c->crypto_suite->session_key_len;
k_s_len = c->crypto_suite->session_salt_len;
k_e_len = c->params.crypto_suite->session_key_len;
k_s_len = c->params.crypto_suite->session_salt_len;
key = (unsigned char *) c->session_key;
/* m = k_s || 0x555..5 */
@ -468,3 +543,10 @@ static int evp_session_key_cleanup(struct crypto_context *c) {
return 0;
}
static int null_crypt_rtp(struct crypto_context *c, struct rtp_header *r, str *s, u_int64_t idx) {
return 0;
}
static int null_crypt_rtcp(struct crypto_context *c, struct rtcp_packet *r, str *s, u_int64_t idx) {
return 0;
}

@ -9,6 +9,14 @@
#define SRTP_MAX_MASTER_KEY_LEN 16
#define SRTP_MAX_MASTER_SALT_LEN 14
#define SRTP_MAX_SESSION_KEY_LEN 16
#define SRTP_MAX_SESSION_SALT_LEN 14
#define SRTP_MAX_SESSION_AUTH_LEN 20
struct crypto_context;
struct rtp_header;
struct rtcp_packet;
@ -22,6 +30,7 @@ typedef int (*session_key_cleanup_func)(struct crypto_context *);
struct crypto_suite {
const char *name;
const char *dtls_name;
unsigned int
master_key_len,
master_salt_len,
@ -44,35 +53,34 @@ struct crypto_suite {
hash_func_rtcp hash_rtcp;
session_key_init_func session_key_init;
session_key_cleanup_func session_key_cleanup;
const char *dtls_profile_code;
};
struct crypto_context {
struct crypto_params {
const struct crypto_suite *crypto_suite;
/* we only support one master key for now */
char master_key[16];
char master_salt[14];
u_int64_t mki;
unsigned char master_key[SRTP_MAX_MASTER_KEY_LEN];
unsigned char master_salt[SRTP_MAX_MASTER_SALT_LEN];
unsigned char *mki;
unsigned int mki_len;
unsigned int tag;
};
struct crypto_context {
struct crypto_params params;
char session_key[SRTP_MAX_SESSION_KEY_LEN]; /* k_e */
char session_salt[SRTP_MAX_SESSION_SALT_LEN]; /* k_s */
char session_auth_key[SRTP_MAX_SESSION_AUTH_LEN];
u_int64_t last_index;
/* XXX replay list */
/* <from, to>? */
char session_key[16]; /* k_e */
char session_salt[14]; /* k_s */
char session_auth_key[20];
void *session_key_ctx[2];
int have_session_key:1;
};
struct crypto_context_pair {
struct crypto_context in,
out;
};
@ -87,41 +95,57 @@ int crypto_gen_session_key(struct crypto_context *, str *, unsigned char, int);
static inline int crypto_encrypt_rtp(struct crypto_context *c, struct rtp_header *rtp,
str *payload, u_int64_t index)
{
return c->crypto_suite->encrypt_rtp(c, rtp, payload, index);
return c->params.crypto_suite->encrypt_rtp(c, rtp, payload, index);
}
static inline int crypto_decrypt_rtp(struct crypto_context *c, struct rtp_header *rtp,
str *payload, u_int64_t index)
{
return c->crypto_suite->decrypt_rtp(c, rtp, payload, index);
return c->params.crypto_suite->decrypt_rtp(c, rtp, payload, index);
}
static inline int crypto_encrypt_rtcp(struct crypto_context *c, struct rtcp_packet *rtcp,
str *payload, u_int64_t index)
{
return c->crypto_suite->encrypt_rtcp(c, rtcp, payload, index);
return c->params.crypto_suite->encrypt_rtcp(c, rtcp, payload, index);
}
static inline int crypto_decrypt_rtcp(struct crypto_context *c, struct rtcp_packet *rtcp,
str *payload, u_int64_t index)
{
return c->crypto_suite->decrypt_rtcp(c, rtcp, payload, index);
return c->params.crypto_suite->decrypt_rtcp(c, rtcp, payload, index);
}
static inline int crypto_init_session_key(struct crypto_context *c) {
return c->crypto_suite->session_key_init(c);
return c->params.crypto_suite->session_key_init(c);
}
static inline void crypto_params_cleanup(struct crypto_params *p) {
if (p->mki)
free(p->mki);
p->mki = NULL;
}
static inline void crypto_cleanup(struct crypto_context *c) {
if (!c->crypto_suite)
if (!c->params.crypto_suite)
return;
if (c->crypto_suite->session_key_cleanup)
c->crypto_suite->session_key_cleanup(c);
if (c->params.crypto_suite->session_key_cleanup)
c->params.crypto_suite->session_key_cleanup(c);
c->have_session_key = 0;
crypto_params_cleanup(&c->params);
}
static inline void crypto_context_move(struct crypto_context *dst, struct crypto_context *src) {
int i;
if (src == dst)
return;
crypto_cleanup(dst);
*dst = *src;
for (i = 0; i < G_N_ELEMENTS(src->session_key_ctx); i++)
src->session_key_ctx[i] = NULL;
static inline void crypto_reset(struct crypto_context *c) {
crypto_cleanup(c);
c->last_index = 0;
}
static inline void crypto_params_copy(struct crypto_params *o, const struct crypto_params *i) {
crypto_params_cleanup(o);
*o = *i;
if (o->mki_len > 255)
o->mki_len = 0;
if (o->mki_len) {
o->mki = malloc(i->mki_len);
memcpy(o->mki, i->mki, i->mki_len);
}
}
static inline void crypto_init(struct crypto_context *c, const struct crypto_params *p) {
crypto_cleanup(c);
crypto_params_copy(&c->params, p);
}

@ -0,0 +1,681 @@
#include "dtls.h"
#include <stdio.h>
#include <string.h>
#include <glib.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <openssl/err.h>
#include <time.h>
#include "str.h"
#include "aux.h"
#include "crypto.h"
#include "log.h"
#include "call.h"
#include "poller.h"
#define DTLS_DEBUG 0
#if DTLS_DEBUG
#define __DBG(x...) ilog(LOG_DEBUG, x)
#else
#define __DBG(x...) ((void)0)
#endif
#define CERT_EXPIRY_TIME (60*60*24*30) /* 30 days */
static char ciphers_str[1024];
static unsigned int sha_1_func(unsigned char *, X509 *);
static unsigned int sha_224_func(unsigned char *, X509 *);
static unsigned int sha_256_func(unsigned char *, X509 *);
static unsigned int sha_384_func(unsigned char *, X509 *);
static unsigned int sha_512_func(unsigned char *, X509 *);
static const struct dtls_hash_func hash_funcs[] = {
{
.name = "sha-1",
.num_bytes = 160 / 8,
.__func = sha_1_func,
},
{
.name = "sha-224",
.num_bytes = 224 / 8,
.__func = sha_224_func,
},
{
.name = "sha-256",
.num_bytes = 256 / 8,
.__func = sha_256_func,
},
{
.name = "sha-384",
.num_bytes = 384 / 8,
.__func = sha_384_func,
},
{
.name = "sha-512",
.num_bytes = 512 / 8,
.__func = sha_512_func,
},
};
const int num_hash_funcs = G_N_ELEMENTS(hash_funcs);
static struct dtls_cert *__dtls_cert;
static rwlock_t __dtls_cert_lock;
const struct dtls_hash_func *dtls_find_hash_func(const str *s) {
int i;
const struct dtls_hash_func *hf;
for (i = 0; i < num_hash_funcs; i++) {
hf = &hash_funcs[i];
if (strlen(hf->name) != s->len)
continue;
if (!strncasecmp(s->s, hf->name, s->len))
return hf;
}
return NULL;
}
static void cert_free(void *p) {
struct dtls_cert *cert = p;
if (cert->pkey)
EVP_PKEY_free(cert->pkey);
if (cert->x509)
X509_free(cert->x509);
}
static int cert_init() {
X509 *x509 = NULL;
EVP_PKEY *pkey = NULL;
BIGNUM *exponent = NULL, *serial_number = NULL;
RSA *rsa = NULL;
ASN1_INTEGER *asn1_serial_number;
X509_NAME *name;
struct dtls_cert *new_cert;
ilog(LOG_INFO, "Generating new DTLS certificate");
/* objects */
pkey = EVP_PKEY_new();
exponent = BN_new();
rsa = RSA_new();
serial_number = BN_new();
name = X509_NAME_new();
x509 = X509_new();
if (!exponent || !pkey || !rsa || !serial_number || !name || !x509)
goto err;
/* key */
if (!BN_set_word(exponent, 0x10001))
goto err;
if (!RSA_generate_key_ex(rsa, 1024, exponent, NULL))
goto err;
if (!EVP_PKEY_assign_RSA(pkey, rsa))
goto err;
/* x509 cert */
if (!X509_set_pubkey(x509, pkey))
goto err;
/* serial */
if (!BN_pseudo_rand(serial_number, 64, 0, 0))
goto err;
asn1_serial_number = X509_get_serialNumber(x509);
if (!asn1_serial_number)
goto err;
if (!BN_to_ASN1_INTEGER(serial_number, asn1_serial_number))
goto err;
/* version 1 */
if (!X509_set_version(x509, 0L))
goto err;
/* common name */
if (!X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_UTF8,
(unsigned char *) "mediaproxy-ng", -1, -1, 0))
goto err;
if (!X509_set_subject_name(x509, name))
goto err;
if (!X509_set_issuer_name(x509, name))
goto err;
/* cert lifetime */
if (!X509_gmtime_adj(X509_get_notBefore(x509), -60*60*24))
goto err;
if (!X509_gmtime_adj(X509_get_notAfter(x509), CERT_EXPIRY_TIME))
goto err;
/* sign it */
if (!X509_sign(x509, pkey, EVP_sha1()))
goto err;
/* digest */
new_cert = obj_alloc0("dtls_cert", sizeof(*new_cert), cert_free);
new_cert->fingerprint.hash_func = &hash_funcs[0];
dtls_fingerprint_hash(&new_cert->fingerprint, x509);
new_cert->x509 = x509;
new_cert->pkey = pkey;
new_cert->expires = time(NULL) + CERT_EXPIRY_TIME;
/* swap out certs */
rwlock_lock_w(&__dtls_cert_lock);
if (__dtls_cert)
obj_put(__dtls_cert);
__dtls_cert = new_cert;
rwlock_unlock_w(&__dtls_cert_lock);
/* cleanup */
BN_free(exponent);
BN_free(serial_number);
X509_NAME_free(name);
return 0;
err:
ilog(LOG_ERROR, "Failed to generate DTLS certificate");
if (pkey)
EVP_PKEY_free(pkey);
if (exponent)
BN_free(exponent);
if (rsa)
RSA_free(rsa);
if (x509)
X509_free(x509);
if (serial_number)
BN_free(serial_number);
return -1;
}
int dtls_init() {
int i;
char *p;
rwlock_init(&__dtls_cert_lock);
if (cert_init())
return -1;
p = ciphers_str;
for (i = 0; i < num_crypto_suites; i++) {
if (!crypto_suites[i].dtls_name)
continue;
p += sprintf(p, "%s:", crypto_suites[i].dtls_name);
}
assert(p != ciphers_str);
assert(p - ciphers_str < sizeof(ciphers_str));
p[-1] = '\0';
return 0;
}
static void __dtls_timer(void *p) {
struct dtls_cert *c;
long int left;
c = dtls_cert();
left = c->expires - poller_now;
if (left > CERT_EXPIRY_TIME/2)
goto out;
cert_init();
out:
obj_put(c);
}
void dtls_timer(struct poller *p) {
poller_add_timer(p, __dtls_timer, NULL);
}
static unsigned int generic_func(unsigned char *o, X509 *x, const EVP_MD *md) {
unsigned int n;
assert(md != NULL);
X509_digest(x, md, o, &n);
return n;
}
static unsigned int sha_1_func(unsigned char *o, X509 *x) {
const EVP_MD *md;
md = EVP_sha1();
return generic_func(o, x, md);
}
static unsigned int sha_224_func(unsigned char *o, X509 *x) {
const EVP_MD *md;
md = EVP_sha224();
return generic_func(o, x, md);
}
static unsigned int sha_256_func(unsigned char *o, X509 *x) {
const EVP_MD *md;
md = EVP_sha256();
return generic_func(o, x, md);
}
static unsigned int sha_384_func(unsigned char *o, X509 *x) {
const EVP_MD *md;
md = EVP_sha384();
return generic_func(o, x, md);
}
static unsigned int sha_512_func(unsigned char *o, X509 *x) {
const EVP_MD *md;
md = EVP_sha512();
return generic_func(o, x, md);
}
struct dtls_cert *dtls_cert() {
struct dtls_cert *ret;
rwlock_lock_r(&__dtls_cert_lock);
ret = obj_get(__dtls_cert);
rwlock_unlock_r(&__dtls_cert_lock);
return ret;
}
static int verify_callback(int ok, X509_STORE_CTX *store) {
SSL *ssl;
struct stream_fd *sfd;
struct packet_stream *ps;
struct call_media *media;
ssl = X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx());
sfd = SSL_get_app_data(ssl);
if (sfd->dtls.ssl != ssl)
return 0;
ps = sfd->stream;
if (!ps)
return 0;
if (ps->fingerprint_verified)
return 1;
media = ps->media;
if (!media)
return 0;
ps->dtls_cert = X509_STORE_CTX_get_current_cert(store);
if (!media->fingerprint.hash_func)
return 1; /* delay verification */
if (dtls_verify_cert(ps))
return 0;
return 1;
}
int dtls_verify_cert(struct packet_stream *ps) {
unsigned char fp[DTLS_MAX_DIGEST_LEN];
struct call_media *media;
media = ps->media;
if (!media)
return -1;
if (!ps->dtls_cert)
return -1;
dtls_hash(media->fingerprint.hash_func, ps->dtls_cert, fp);
if (memcmp(media->fingerprint.digest, fp, media->fingerprint.hash_func->num_bytes)) {
ilog(LOG_WARNING, "DTLS: Peer certificate rejected - fingerprint mismatch");
__DBG("fingerprint expected: %02x%02x%02x%02x%02x%02x%02x%02x received: %02x%02x%02x%02x%02x%02x%02x%02x",
media->fingerprint.digest[0], media->fingerprint.digest[1],
media->fingerprint.digest[2], media->fingerprint.digest[3],
media->fingerprint.digest[4], media->fingerprint.digest[5],
media->fingerprint.digest[6], media->fingerprint.digest[7],
fp[0], fp[1], fp[2], fp[3],
fp[4], fp[5], fp[6], fp[7]);
return -1;
}
ps->fingerprint_verified = 1;
ilog(LOG_INFO, "DTLS: Peer certificate accepted");
return 0;
}
static int try_connect(struct dtls_connection *d) {
int ret, code;
if (d->connected)
return 0;
__DBG("try_connect(%i)", d->active);
if (d->active)
ret = SSL_connect(d->ssl);
else
ret = SSL_accept(d->ssl);
code = SSL_get_error(d->ssl, ret);
ret = 0;
switch (code) {
case SSL_ERROR_NONE:
ilog(LOG_DEBUG, "DTLS handshake successful");
d->connected = 1;
ret = 1;
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
break;
default:
ret = ERR_peek_last_error();
ilog(LOG_ERROR, "DTLS error: %i (%s)", code, ERR_reason_error_string(ret));
ret = -1;
break;
}
return ret;
}
int dtls_connection_init(struct packet_stream *ps, int active, struct dtls_cert *cert) {
struct dtls_connection *d = &ps->sfd->dtls;
unsigned long err;
__DBG("dtls_connection_init(%i)", active);
if (d->init) {
if (d->active == active)
goto connect;
dtls_connection_cleanup(d);
}
d->ssl_ctx = SSL_CTX_new(active ? DTLSv1_client_method() : DTLSv1_server_method());
if (!d->ssl_ctx)
goto error;
if (SSL_CTX_use_certificate(d->ssl_ctx, cert->x509) != 1)
goto error;
if (SSL_CTX_use_PrivateKey(d->ssl_ctx, cert->pkey) != 1)
goto error;
SSL_CTX_set_verify(d->ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
verify_callback);
SSL_CTX_set_verify_depth(d->ssl_ctx, 4);
SSL_CTX_set_cipher_list(d->ssl_ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
if (SSL_CTX_set_tlsext_use_srtp(d->ssl_ctx, ciphers_str))
goto error;
d->ssl = SSL_new(d->ssl_ctx);
if (!d->ssl)
goto error;
d->r_bio = BIO_new(BIO_s_mem());
d->w_bio = BIO_new(BIO_s_mem());
if (!d->r_bio || !d->w_bio)
goto error;
SSL_set_app_data(d->ssl, ps->sfd); /* XXX obj reference here? */
SSL_set_bio(d->ssl, d->r_bio, d->w_bio);
SSL_set_mode(d->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
d->init = 1;
d->active = active;
connect:
dtls(ps, NULL, NULL);
return 0;
error:
err = ERR_peek_last_error();
if (d->r_bio)
BIO_free(d->r_bio);
if (d->w_bio)
BIO_free(d->w_bio);
if (d->ssl)
SSL_free(d->ssl);
if (d->ssl_ctx)
SSL_CTX_free(d->ssl_ctx);
ZERO(*d);
ilog(LOG_ERROR, "Failed to init DTLS connection: %s", ERR_reason_error_string(err));
return -1;
}
static int dtls_setup_crypto(struct packet_stream *ps, struct dtls_connection *d) {
const char *err;
SRTP_PROTECTION_PROFILE *spp;
int i;
const struct crypto_suite *cs;
unsigned char keys[2 * (SRTP_MAX_MASTER_KEY_LEN + SRTP_MAX_MASTER_SALT_LEN)];
struct crypto_params client, server;
err = "no SRTP protection profile negotiated";
spp = SSL_get_selected_srtp_profile(d->ssl);
if (!spp)
goto error;
for (i = 0; i < num_crypto_suites; i++) {
cs = &crypto_suites[i];
if (!cs->dtls_name)
continue;
if (!strcmp(cs->dtls_name, spp->name))
goto found;
}
err = "unknown SRTP protection profile negotiated";
goto error;
found:
i = SSL_export_keying_material(d->ssl, keys, sizeof(keys), "EXTRACTOR-dtls_srtp",
strlen("EXTRACTOR-dtls_srtp"), NULL, 0, 0);
err = "failed to export keying material";
if (i != 1)
goto error;
/* got everything XXX except MKI */
ZERO(client);
ZERO(server);
i = 0;
client.crypto_suite = server.crypto_suite = cs;
memcpy(client.master_key, &keys[i], cs->master_key_len);
i += cs->master_key_len;
memcpy(server.master_key, &keys[i], cs->master_key_len);
i += cs->master_key_len;
memcpy(client.master_salt, &keys[i], cs->master_salt_len);
i += cs->master_salt_len;
memcpy(server.master_salt, &keys[i], cs->master_salt_len);
__DBG("SRTP keys negotiated: "
"c-m: %02x%02x%02x%02x%02x%02x%02x%02x "
"c-s: %02x%02x%02x%02x%02x%02x%02x%02x "
"s-m: %02x%02x%02x%02x%02x%02x%02x%02x "
"s-s: %02x%02x%02x%02x%02x%02x%02x%02x",
client.master_key[0], client.master_key[1], client.master_key[2], client.master_key[3],
client.master_key[4], client.master_key[5], client.master_key[6], client.master_key[7],
client.master_salt[0], client.master_salt[1], client.master_salt[2], client.master_salt[3],
client.master_salt[4], client.master_salt[5], client.master_salt[6], client.master_salt[7],
server.master_key[0], server.master_key[1], server.master_key[2], server.master_key[3],
server.master_key[4], server.master_key[5], server.master_key[6], server.master_key[7],
server.master_salt[0], server.master_salt[1], server.master_salt[2], server.master_salt[3],
server.master_salt[4], server.master_salt[5], server.master_salt[6], server.master_salt[7]);
ilog(LOG_INFO, "DTLS-SRTP successfully negotiated");
if (d->active) {
/* we're the client */
crypto_init(&ps->crypto, &client);
crypto_init(&ps->sfd->crypto, &server);
}
else {
/* we're the server */
crypto_init(&ps->crypto, &server);
crypto_init(&ps->sfd->crypto, &client);
}
return 0;
error:
if (!spp)
ilog(LOG_ERROR, "Failed to set up SRTP after DTLS negotiation: %s", err);
else
ilog(LOG_ERROR, "Failed to set up SRTP after DTLS negotiation: %s (profile \"%s\")",
err, spp->name);
return -1;
}
int dtls(struct packet_stream *ps, const str *s, struct sockaddr_in6 *fsin) {
struct dtls_connection *d = &ps->sfd->dtls;
int ret;
unsigned char buf[0x10000], ctrl[256];
struct msghdr mh;
struct iovec iov;
struct sockaddr_in6 sin;
if (s)
__DBG("dtls packet input: len %u %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
s->len,
(unsigned char) s->s[0], (unsigned char) s->s[1], (unsigned char) s->s[2], (unsigned char) s->s[3],
(unsigned char) s->s[4], (unsigned char) s->s[5], (unsigned char) s->s[6], (unsigned char) s->s[7],
(unsigned char) s->s[8], (unsigned char) s->s[9], (unsigned char) s->s[10], (unsigned char) s->s[11],
(unsigned char) s->s[12], (unsigned char) s->s[13], (unsigned char) s->s[14], (unsigned char) s->s[15]);
if (d->connected)
return 0;
if (!d->init || !d->ssl)
return -1;
if (s) {
BIO_write(d->r_bio, s->s, s->len);
/* we understand this as preference of DTLS over SDES */
ps->media->sdes = 0;
}
ret = try_connect(d);
if (ret == -1) {
if (ps->sfd)
ilog(LOG_ERROR, "DTLS error on local port %hu", ps->sfd->fd.localport);
/* fatal error */
d->init = 0;
/* XXX ?? */
return 0;
}
else if (ret == 1) {
/* connected! */
if (dtls_setup_crypto(ps, d))
/* XXX ?? */ ;
if (ps->rtp && ps->rtcp && ps->rtcp_sibling && ps->media->rtcp_mux) {
if (dtls_setup_crypto(ps->rtcp_sibling, d))
/* XXX ?? */ ;
}
}
ret = BIO_ctrl_pending(d->w_bio);
if (ret <= 0)
return 0;
if (ret > sizeof(buf)) {
ilog(LOG_ERROR, "BIO buffer overflow");
BIO_reset(d->w_bio);
return 0;
}
ret = BIO_read(d->w_bio, buf, ret);
if (ret <= 0)
return 0;
__DBG("dtls packet output: len %u %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
ret,
buf[0], buf[1], buf[2], buf[3],
buf[4], buf[5], buf[6], buf[7],
buf[8], buf[9], buf[10], buf[11],
buf[12], buf[13], buf[14], buf[15]);
if (!fsin) {
ZERO(sin);
sin.sin6_family = AF_INET6;
sin.sin6_addr = ps->endpoint.ip46;
sin.sin6_port = htons(ps->endpoint.port);
fsin = &sin;
}
ZERO(mh);
mh.msg_control = ctrl;
mh.msg_controllen = sizeof(ctrl);
mh.msg_name = fsin;
mh.msg_namelen = sizeof(*fsin);
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
ZERO(iov);
iov.iov_base = buf;
iov.iov_len = ret;
callmaster_msg_mh_src(ps->call->callmaster, &mh);
sendmsg(ps->sfd->fd.fd, &mh, 0);
return 0;
}
void dtls_connection_cleanup(struct dtls_connection *c) {
__DBG("dtls_connection_cleanup");
if (c->ssl_ctx)
SSL_CTX_free(c->ssl_ctx);
if (c->ssl)
SSL_free(c->ssl);
if (!c->init) {
if (c->r_bio)
BIO_free(c->r_bio);
if (c->w_bio)
BIO_free(c->w_bio);
}
ZERO(*c);
}

@ -0,0 +1,103 @@
#ifndef _DTLS_H_
#define _DTLS_H_
#include <time.h>
#include <openssl/x509.h>
#include <openssl/ssl.h>
#include <openssl/bio.h>
#include "str.h"
#include "obj.h"
#define DTLS_MAX_DIGEST_LEN 64
struct packet_stream;
struct sockaddr_in6;
struct poller;
struct dtls_hash_func {
const char *name;
unsigned int num_bytes;
unsigned int (*__func)(unsigned char *, X509 *);
};
struct dtls_fingerprint {
unsigned char digest[DTLS_MAX_DIGEST_LEN];
const struct dtls_hash_func *hash_func;
};
struct dtls_cert {
struct obj obj;
struct dtls_fingerprint fingerprint;
EVP_PKEY *pkey;
X509 *x509;
time_t expires;
};
struct dtls_connection {
SSL_CTX *ssl_ctx;
SSL *ssl;
BIO *r_bio, *w_bio;
int init:1,
active:1,
connected:1;
};
int dtls_init(void);
void dtls_timer(struct poller *);
int dtls_verify_cert(struct packet_stream *ps);
const struct dtls_hash_func *dtls_find_hash_func(const str *);
struct dtls_cert *dtls_cert(void);
int dtls_connection_init(struct packet_stream *, int active, struct dtls_cert *cert);
int dtls(struct packet_stream *, const str *s, struct sockaddr_in6 *sin);
void dtls_connection_cleanup(struct dtls_connection *);
static inline void __dtls_hash(const struct dtls_hash_func *hash_func, X509 *cert, unsigned char *out,
unsigned int bufsize)
{
unsigned int n;
assert(bufsize >= hash_func->num_bytes);
n = hash_func->__func(out, cert);
assert(n == hash_func->num_bytes);
}
#define dtls_hash(hash_func, cert, outbuf) __dtls_hash(hash_func, cert, outbuf, sizeof(outbuf))
static inline void dtls_fingerprint_hash(struct dtls_fingerprint *fp, X509 *cert) {
__dtls_hash(fp->hash_func, cert, fp->digest, sizeof(fp->digest));
}
static inline int is_dtls(const str *s) {
const unsigned char *b = (const void *) s->s;
if (s->len < 1)
return 0;
/* RFC 5764, 5.1.2 */
if (b[0] >= 20 && b[0] <= 63)
return 1;
return 0;
}
#endif

@ -85,7 +85,7 @@ int kernel_add_stream(int fd, struct mediaproxy_target_info *mti, int update) {
if (ret > 0)
return 0;
mylog(LOG_ERROR, "Failed to push relay stream to kernel: %s", strerror(errno));
ilog(LOG_ERROR, "Failed to push relay stream to kernel: %s", strerror(errno));
return -1;
}
@ -102,7 +102,7 @@ int kernel_del_stream(int fd, u_int16_t p) {
if (ret > 0)
return 0;
mylog(LOG_ERROR, "Failed to delete relay stream from kernel: %s", strerror(errno));
ilog(LOG_ERROR, "Failed to delete relay stream from kernel: %s", strerror(errno));
return -1;
}

@ -0,0 +1,55 @@
#include "log.h"
#include <syslog.h>
#include <stdarg.h>
#include <stdio.h>
#include <glib.h>
#include "str.h"
#include "call.h"
struct log_info __thread log_info;
volatile gint log_level = LOG_INFO;
void ilog(int prio, const char *fmt, ...) {
char prefix[256];
char *msg;
va_list ap;
int ret;
if (prio > g_atomic_int_get(&log_level))
return;
switch (log_info.e) {
case LOG_INFO_NONE:
prefix[0] = 0;
break;
case LOG_INFO_CALL:
snprintf(prefix, sizeof(prefix), "["STR_FORMAT"] ",
STR_FMT(&log_info.u.call->callid));
break;
case LOG_INFO_STREAM_FD:
if (log_info.u.stream_fd->call)
snprintf(prefix, sizeof(prefix), "["STR_FORMAT" port %5hu] ",
STR_FMT(&log_info.u.stream_fd->call->callid),
log_info.u.stream_fd->fd.localport);
break;
}
va_start(ap, fmt);
ret = vasprintf(&msg, fmt, ap);
va_end(ap);
if (ret < 0) {
syslog(LOG_ERROR, "Failed to print syslog message - message dropped");
return;
}
syslog(prio, "%s%s", prefix, msg);
free(msg);
}

@ -3,9 +3,74 @@
#include <syslog.h>
#include <glib.h>
struct log_info {
union {
struct call *call;
struct stream_fd *stream_fd;
} u;
enum {
LOG_INFO_NONE = 0,
LOG_INFO_CALL,
LOG_INFO_STREAM_FD,
} e;
};
extern struct log_info __thread log_info;
extern volatile gint log_level;
void ilog(int prio, const char *fmt, ...)__attribute__ ((format (printf, 2, 3)));
#include "obj.h"
static inline void log_info_clear() {
switch (log_info.e) {
case LOG_INFO_NONE:
return;
case LOG_INFO_CALL:
__obj_put((void *) log_info.u.call);
break;
case LOG_INFO_STREAM_FD:
__obj_put((void *) log_info.u.stream_fd);
break;
}
log_info.e = LOG_INFO_NONE;
}
static inline void log_info_call(struct call *c) {
log_info_clear();
if (!c)
return;
log_info.e = LOG_INFO_CALL;
log_info.u.call = __obj_get((void *) c);
}
static inline void log_info_stream_fd(struct stream_fd *sfd) {
log_info_clear();
if (!sfd)
return;
log_info.e = LOG_INFO_STREAM_FD;
log_info.u.stream_fd = __obj_get((void *) sfd);
}
#define mylog(x,y...) syslog(x,y)
#define LOG_ERROR LOG_ERR
#define LOG_WARN LOG_WARNING

@ -11,6 +11,7 @@
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <openssl/ssl.h>
#include "poller.h"
#include "control_tcp.h"
@ -22,6 +23,7 @@
#include "kernel.h"
#include "redis.h"
#include "sdp.h"
#include "dtls.h"
@ -85,7 +87,6 @@ static u_int32_t redis_ip;
static u_int16_t redis_port;
static int redis_db = -1;
static char *b2b_url;
static int log_level = LOG_INFO;
@ -114,17 +115,19 @@ static void sighandler(gpointer x) {
if (ret == SIGINT || ret == SIGTERM)
global_shutdown = 1;
else if (ret == SIGUSR1) {
if (log_level > 0) {
log_level--;
setlogmask(LOG_UPTO(log_level));
mylog(log_level, "Set log level to %d\n", log_level);
if (g_atomic_int_get(&log_level) > 0) {
g_atomic_int_add(&log_level, -1);
setlogmask(LOG_UPTO(g_atomic_int_get(&log_level)));
ilog(g_atomic_int_get(&log_level), "Set log level to %d\n",
g_atomic_int_get(&log_level));
}
}
else if (ret == SIGUSR2) {
if (log_level < 7) {
log_level++;
setlogmask(LOG_UPTO(log_level));
mylog(log_level, "Set log level to %d\n", log_level);
if (g_atomic_int_get(&log_level) < 7) {
g_atomic_int_add(&log_level, 1);
setlogmask(LOG_UPTO(g_atomic_int_get(&log_level)));
ilog(g_atomic_int_get(&log_level), "Set log level to %d\n",
g_atomic_int_get(&log_level));
}
}
else
@ -252,7 +255,7 @@ static void options(int *argc, char ***argv) {
{ "redis", 'r', 0, G_OPTION_ARG_STRING, &redisps, "Connect to Redis database", "IP:PORT" },
{ "redis-db", 'R', 0, G_OPTION_ARG_INT, &redis_db, "Which Redis DB to use", "INT" },
{ "b2b-url", 'b', 0, G_OPTION_ARG_STRING, &b2b_url, "XMLRPC URL of B2B UA" , "STRING" },
{ "log-level", 'L', 0, G_OPTION_ARG_INT, &log_level, "Mask log priorities above this level", "INT" },
{ "log-level", 'L', 0, G_OPTION_ARG_INT, (void *)&log_level, "Mask log priorities above this level", "INT" },
{ NULL, }
};
@ -353,6 +356,8 @@ static void init_everything() {
clock_gettime(CLOCK_REALTIME, &ts);
srandom(ts.tv_sec ^ ts.tv_nsec);
SSL_library_init();
SSL_load_error_strings();
#if !GLIB_CHECK_VERSION(2,32,0)
g_thread_init(NULL);
@ -361,6 +366,7 @@ static void init_everything() {
signals();
resources();
sdp_init();
dtls_init();
}
void redis_mod_verify(void *dlh) {
@ -370,6 +376,7 @@ void redis_mod_verify(void *dlh) {
dlresolve(redis_delete);
dlresolve(redis_wipe);
/*
check_struct_size(call);
check_struct_size(callstream);
check_struct_size(crypto_suite);
@ -398,6 +405,7 @@ void redis_mod_verify(void *dlh) {
check_struct_offset(stream, ip46);
check_struct_offset(stream, num);
check_struct_offset(stream, protocol);
*/
}
void create_everything(struct main_context *ctx) {
@ -411,7 +419,7 @@ void create_everything(struct main_context *ctx) {
if (table >= 0 && kernel_create_table(table)) {
fprintf(stderr, "FAILED TO CREATE KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table);
mylog(LOG_CRIT, "FAILED TO CREATE KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table);
ilog(LOG_CRIT, "FAILED TO CREATE KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table);
table = -1;
if (no_fallback)
exit(-1);
@ -420,7 +428,7 @@ void create_everything(struct main_context *ctx) {
kfd = kernel_open_table(table);
if (kfd == -1) {
fprintf(stderr, "FAILED TO OPEN KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table);
mylog(LOG_CRIT, "FAILED TO OPEN KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table);
ilog(LOG_CRIT, "FAILED TO OPEN KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table);
table = -1;
if (no_fallback)
exit(-1);
@ -435,6 +443,8 @@ void create_everything(struct main_context *ctx) {
if (!ctx->m)
die("callmaster creation failed\n");
dtls_timer(ctx->p);
ZERO(mc);
mc.kernelfd = kfd;
mc.kernelid = table;
@ -488,7 +498,7 @@ void create_everything(struct main_context *ctx) {
die("Cannot start up without Redis database\n");
}
callmaster_config(ctx->m, &mc);
ctx->m->conf = mc;
if (!foreground)
daemonize();
@ -519,7 +529,7 @@ int main(int argc, char **argv) {
options(&argc, &argv);
create_everything(&ctx);
mylog(LOG_INFO, "Startup complete, version %s", MEDIAPROXY_VERSION);
ilog(LOG_INFO, "Startup complete, version %s", MEDIAPROXY_VERSION);
thread_create_detach(sighandler, NULL);
thread_create_detach(timer_loop, ctx.p);
@ -535,7 +545,7 @@ int main(int argc, char **argv) {
threads_join_all(1);
mylog(LOG_INFO, "Version %s shutting down", MEDIAPROXY_VERSION);
ilog(LOG_INFO, "Version %s shutting down", MEDIAPROXY_VERSION);
return 0;
}

@ -9,7 +9,6 @@
#include <assert.h>
#include <stdlib.h>
#include "log.h"
@ -37,26 +36,60 @@ struct obj {
#if OBJ_DEBUG
#define OBJ_MAGIC 0xf1eef1ee
#define obj_alloc(t,a,b) __obj_alloc(a,b,t,__FILE__,__LINE__)
#define obj_alloc0(t,a,b) __obj_alloc0(a,b,t,__FILE__,__LINE__)
#define obj_hold(a) __obj_hold(a,__FILE__,__LINE__)
#define obj_get(a) __obj_get(a,__FILE__,__LINE__)
#define obj_put(a) __obj_put(a,__FILE__,__LINE__)
#define obj_hold(a) __obj_hold(&(a)->obj,__FILE__,__LINE__)
#define obj_get(a) __obj_get(&(a)->obj,__FILE__,__LINE__)
#define obj_put(a) __obj_put(&(a)->obj,__FILE__,__LINE__)
#define obj_hold_o(a) __obj_hold(a,__FILE__,__LINE__)
#define obj_get_o(a) __obj_get(a,__FILE__,__LINE__)
#define obj_put_o(a) __obj_put(a,__FILE__,__LINE__)
static inline void __obj_init(struct obj *o, unsigned int size, void (*free_func)(void *),
const char *type, const char *file, unsigned int line);
static inline void *__obj_alloc(unsigned int size, void (*free_func)(void *),
const char *type, const char *file, unsigned int line);
static inline void *__obj_alloc0(unsigned int size, void (*free_func)(void *),
const char *type, const char *file, unsigned int line);
static inline struct obj *__obj_hold(struct obj *o,
const char *type, const char *file, unsigned int line);
static inline void *__obj_get(struct obj *o,
const char *type, const char *file, unsigned int line);
static inline void __obj_put(struct obj *o,,
const char *type, const char *file, unsigned int line);
#else
#define obj_alloc(t,a,b) __obj_alloc(a,b)
#define obj_alloc0(t,a,b) __obj_alloc0(a,b)
#define obj_hold(a) __obj_hold(a)
#define obj_get(a) __obj_get(a)
#define obj_put(a) __obj_put(a)
#define obj_hold(a) __obj_hold(&(a)->obj)
#define obj_get(a) __obj_get(&(a)->obj)
#define obj_put(a) __obj_put(&(a)->obj)
#define obj_hold_o(a) __obj_hold(a)
#define obj_get_o(a) __obj_get(a)
#define obj_put_o(a) __obj_put(a)
static inline void __obj_init(struct obj *o, unsigned int size, void (*free_func)(void *));
static inline void *__obj_alloc(unsigned int size, void (*free_func)(void *));
static inline void *__obj_alloc0(unsigned int size, void (*free_func)(void *));
static inline struct obj *__obj_hold(struct obj *o);
static inline void *__obj_get(struct obj *o);
static inline void __obj_put(struct obj *o);
#endif
#include "log.h"
static inline void __obj_init(struct obj *o, unsigned int size, void (*free_func)(void *)
#if OBJ_DEBUG
, const char *type, const char *file, unsigned int line
@ -104,12 +137,11 @@ static inline void *__obj_alloc0(unsigned int size, void (*free_func)(void *)
return r;
}
static inline struct obj *__obj_hold(void *p
static inline struct obj *__obj_hold(struct obj *o
#if OBJ_DEBUG
, const char *file, unsigned int line
#endif
) {
struct obj *o = p;
#if OBJ_DEBUG
assert(o->magic == OBJ_MAGIC);
mylog(LOG_DEBUG, "obj_hold(%p, \"%s\"), refcnt before %u [%s:%u]",
@ -123,24 +155,23 @@ static inline struct obj *__obj_hold(void *p
return o;
}
static inline void *__obj_get(void *p
static inline void *__obj_get(struct obj *o
#if OBJ_DEBUG
, const char *file, unsigned int line
#endif
) {
return __obj_hold(p
return __obj_hold(o
#if OBJ_DEBUG
, file, line
#endif
);
}
static inline void __obj_put(void *p
static inline void __obj_put(struct obj *o
#if OBJ_DEBUG
, const char *file, unsigned int line
#endif
) {
struct obj *o = p;
#if OBJ_DEBUG
assert(o->magic == OBJ_MAGIC);
mylog(LOG_DEBUG, "obj_put(%p, \"%s\"), refcnt before %u [%s:%u]",

@ -92,7 +92,7 @@ static void poller_fd_timer(void *p) {
static void poller_item_free(void *p) {
struct poller_item_int *i = p;
obj_put(i->item.obj);
obj_put_o(i->item.obj);
}
@ -132,7 +132,7 @@ static int __poller_add_item(struct poller *p, struct poller_item *i, int has_lo
ip = obj_alloc0("poller_item_int", sizeof(*ip), poller_item_free);
memcpy(&ip->item, i, sizeof(*i));
obj_hold(ip->item.obj); /* new ref in *ip */
obj_hold_o(ip->item.obj); /* new ref in *ip */
p->items[i->fd] = obj_get(ip);
mutex_unlock(&p->lock);
@ -209,8 +209,8 @@ int poller_update_item(struct poller *p, struct poller_item *i) {
if (i->fd >= p->items_size || !(np = p->items[i->fd]))
return __poller_add_item(p, i, 1);
obj_hold(i->obj);
obj_put(np->item.obj);
obj_hold_o(i->obj);
obj_put_o(np->item.obj);
np->item.obj = i->obj;
np->item.uintp = i->uintp;
np->item.readable = i->readable;
@ -257,12 +257,12 @@ next:
found:
l = *ll;
*ll = (*ll)->next;
obj_put(l->data);
obj_put_o(l->data);
g_slist_free_1(l);
l = *kk;
*kk = (*kk)->next;
obj_put(l->data);
obj_put_o(l->data);
g_slist_free_1(l);
}
}
@ -443,19 +443,20 @@ out:
static void timer_item_free(void *p) {
struct timer_item *i = p;
obj_put(i->obj_ptr);
if (i->obj_ptr)
obj_put_o(i->obj_ptr);
}
static int poller_timer_link(struct poller *p, GSList **lp, void (*f)(void *), struct obj *o) {
struct timer_item *i;
if (!o || !f)
if (!f)
return -1;
i = obj_alloc0("timer_item", sizeof(*i), timer_item_free);
i->func = f;
i->obj_ptr = obj_hold(o);
i->obj_ptr = o ? obj_hold_o(o) : NULL;
mutex_lock(&p->timers_add_del_lock);
*lp = g_slist_prepend(*lp, i);

@ -7,7 +7,10 @@
#include <stdint.h>
#include <time.h>
#include <glib.h>
#include "obj.h"
struct obj;

@ -332,19 +332,22 @@ int rtcp_avpf2avp(str *s) {
static inline int check_session_keys(struct crypto_context *c) {
str s;
const char *err;
if (c->have_session_key)
return 0;
if (!c->crypto_suite)
err = "SRTCP output wanted, but no crypto suite was negotiated";
if (!c->params.crypto_suite)
goto error;
str_init_len(&s, c->session_key, c->crypto_suite->session_key_len);
err = "Failed to generate SRTCP session keys";
str_init_len_assert(&s, c->session_key, c->params.crypto_suite->session_key_len);
if (crypto_gen_session_key(c, &s, 0x03, SRTCP_R_LENGTH))
goto error;
str_init_len(&s, c->session_auth_key, c->crypto_suite->srtcp_auth_key_len);
str_init_len_assert(&s, c->session_auth_key, c->params.crypto_suite->srtcp_auth_key_len);
if (crypto_gen_session_key(c, &s, 0x04, SRTCP_R_LENGTH))
goto error;
str_init_len(&s, c->session_salt, c->crypto_suite->session_salt_len);
str_init_len_assert(&s, c->session_salt, c->params.crypto_suite->session_salt_len);
if (crypto_gen_session_key(c, &s, 0x05, SRTCP_R_LENGTH))
goto error;
@ -354,7 +357,7 @@ static inline int check_session_keys(struct crypto_context *c) {
return 0;
error:
mylog(LOG_ERROR, "Error generating SRTCP session keys");
ilog(LOG_ERROR, "%s", err);
return -1;
}
@ -382,7 +385,7 @@ static int rtcp_payload(struct rtcp_packet **out, str *p, const str *s) {
return 0;
error:
mylog(LOG_WARNING, "Error parsing RTCP header: %s", err);
ilog(LOG_WARNING, "Error parsing RTCP header: %s", err);
return -1;
}
@ -408,8 +411,8 @@ int rtcp_avp2savp(str *s, struct crypto_context *c) {
rtp_append_mki(s, c);
c->crypto_suite->hash_rtcp(c, s->s + s->len, &to_auth);
s->len += c->crypto_suite->srtcp_auth_tag;
c->params.crypto_suite->hash_rtcp(c, s->s + s->len, &to_auth);
s->len += c->params.crypto_suite->srtcp_auth_tag;
return 1;
}
@ -429,7 +432,7 @@ int rtcp_savp2avp(str *s, struct crypto_context *c) {
return -1;
if (srtp_payloads(&to_auth, &to_decrypt, &auth_tag, NULL,
c->crypto_suite->srtcp_auth_tag, c->mki_len,
c->params.crypto_suite->srtcp_auth_tag, c->params.mki_len,
s, &payload))
return -1;
@ -441,7 +444,7 @@ int rtcp_savp2avp(str *s, struct crypto_context *c) {
idx = ntohl(*idx_p);
assert(sizeof(hmac) >= auth_tag.len);
c->crypto_suite->hash_rtcp(c, hmac, &to_auth);
c->params.crypto_suite->hash_rtcp(c, hmac, &to_auth);
err = "authentication failed";
if (str_memcmp(&auth_tag, hmac))
goto error;
@ -457,7 +460,7 @@ int rtcp_savp2avp(str *s, struct crypto_context *c) {
return 0;
error:
mylog(LOG_WARNING, "Discarded invalid SRTCP packet: %s", err);
ilog(LOG_WARNING, "Discarded invalid SRTCP packet: %s", err);
return -1;
}

@ -21,19 +21,22 @@ struct rtp_extension {
static inline int check_session_keys(struct crypto_context *c) {
str s;
const char *err;
if (c->have_session_key)
return 0;
if (!c->crypto_suite)
err = "SRTP output wanted, but no crypto suite was negotiated";
if (!c->params.crypto_suite)
goto error;
str_init_len(&s, c->session_key, c->crypto_suite->session_key_len);
err = "Failed to generate SRTP session keys";
str_init_len_assert(&s, c->session_key, c->params.crypto_suite->session_key_len);
if (crypto_gen_session_key(c, &s, 0x00, 6))
goto error;
str_init_len(&s, c->session_auth_key, c->crypto_suite->srtp_auth_key_len);
str_init_len_assert(&s, c->session_auth_key, c->params.crypto_suite->srtp_auth_key_len);
if (crypto_gen_session_key(c, &s, 0x01, 6))
goto error;
str_init_len(&s, c->session_salt, c->crypto_suite->session_salt_len);
str_init_len_assert(&s, c->session_salt, c->params.crypto_suite->session_salt_len);
if (crypto_gen_session_key(c, &s, 0x02, 6))
goto error;
@ -43,7 +46,7 @@ static inline int check_session_keys(struct crypto_context *c) {
return 0;
error:
mylog(LOG_ERROR, "Error generating SRTP session keys");
ilog(LOG_ERROR, "%s", err);
return -1;
}
@ -85,7 +88,7 @@ static int rtp_payload(struct rtp_header **out, str *p, const str *s) {
return 0;
error:
mylog(LOG_WARNING, "Error parsing RTP header: %s", err);
ilog(LOG_WARNING, "Error parsing RTP header: %s", err);
return -1;
}
@ -121,31 +124,15 @@ static u_int64_t packet_index(struct crypto_context *c, struct rtp_header *rtp)
}
void rtp_append_mki(str *s, struct crypto_context *c) {
u_int32_t mki_part;
char *p;
if (!c->mki_len)
if (!c->params.mki_len)
return;
/* RTP_BUFFER_TAIL_ROOM guarantees enough room */
p = s->s + s->len;
memset(p, 0, c->mki_len);
if (c->mki_len > 4) {
mki_part = (c->mki & 0xffffffff00000000ULL) >> 32;
mki_part = htonl(mki_part);
if (c->mki_len < 8)
memcpy(p, ((char *) &mki_part) + (8 - c->mki_len), c->mki_len - 4);
else
memcpy(p + (c->mki_len - 8), &mki_part, 4);
}
mki_part = (c->mki & 0xffffffffULL);
mki_part = htonl(mki_part);
if (c->mki_len < 4)
memcpy(p, ((char *) &mki_part) + (4 - c->mki_len), c->mki_len);
else
memcpy(p + (c->mki_len - 4), &mki_part, 4);
s->len += c->mki_len;
memcpy(p, c->params.mki, c->params.mki_len);
s->len += c->params.mki_len;
}
/* rfc 3711, section 3.3 */
@ -170,9 +157,9 @@ int rtp_avp2savp(str *s, struct crypto_context *c) {
rtp_append_mki(s, c);
if (c->crypto_suite->srtp_auth_tag) {
c->crypto_suite->hash_rtp(c, s->s + s->len, &to_auth, index);
s->len += c->crypto_suite->srtp_auth_tag;
if (c->params.crypto_suite->srtp_auth_tag) {
c->params.crypto_suite->hash_rtp(c, s->s + s->len, &to_auth, index);
s->len += c->params.crypto_suite->srtp_auth_tag;
}
return 0;
@ -192,13 +179,13 @@ int rtp_savp2avp(str *s, struct crypto_context *c) {
index = packet_index(c, rtp);
if (srtp_payloads(&to_auth, &to_decrypt, &auth_tag, NULL,
c->crypto_suite->srtp_auth_tag, c->mki_len,
c->params.crypto_suite->srtp_auth_tag, c->params.mki_len,
s, &payload))
return -1;
if (auth_tag.len) {
assert(sizeof(hmac) >= auth_tag.len);
c->crypto_suite->hash_rtp(c, hmac, &to_auth, index);
c->params.crypto_suite->hash_rtp(c, hmac, &to_auth, index);
if (str_memcmp(&auth_tag, hmac))
goto error;
}
@ -211,7 +198,7 @@ int rtp_savp2avp(str *s, struct crypto_context *c) {
return 0;
error:
mylog(LOG_WARNING, "Discarded invalid SRTP packet: authentication failed");
ilog(LOG_WARNING, "Discarded invalid SRTP packet: authentication failed");
return -1;
}
@ -251,6 +238,6 @@ int srtp_payloads(str *to_auth, str *to_decrypt, str *auth_tag, str *mki,
return 0;
error:
mylog(LOG_WARNING, "Invalid SRTP/SRTCP packet received (short packet)");
ilog(LOG_WARNING, "Invalid SRTP/SRTCP packet received (short packet)");
return -1;
}

File diff suppressed because it is too large Load Diff

@ -7,22 +7,28 @@
struct sdp_ng_flags {
int desired_family[2];
enum call_opmode opmode;
str received_from_family;
str received_from_address;
str media_address;
str transport_protocol_str;
enum transport_protocol transport_protocol;
str address_family_str;
const struct transport_protocol *transport_protocol;
struct in6_addr parsed_received_from;
struct in6_addr parsed_media_address;
enum stream_direction directions[2];
int address_family;
int asymmetric:1,
symmetric:1,
trust_address:1,
replace_origin:1,
replace_sess_conn:1,
ice_remove:1,
ice_force:1;
ice_force:1,
rtcp_mux_offer:1,
rtcp_mux_demux:1,
rtcp_mux_accept:1,
rtcp_mux_reject:1;
};
struct sdp_chopper {
@ -37,11 +43,13 @@ struct sdp_chopper {
void sdp_init(void);
int sdp_parse(str *body, GQueue *sessions);
int sdp_streams(const GQueue *sessions, GQueue *streams, GHashTable *, struct sdp_ng_flags *);
int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *);
void sdp_free(GQueue *sessions);
int sdp_replace(struct sdp_chopper *, GQueue *, struct call *, enum call_opmode, struct sdp_ng_flags *, GHashTable *);
int sdp_replace(struct sdp_chopper *, GQueue *, struct call_monologue *, struct sdp_ng_flags *);
struct sdp_chopper *sdp_chopper_new(str *input);
void sdp_chopper_destroy(struct sdp_chopper *chop);
int address_family(const str *s);
#endif

@ -45,6 +45,8 @@ static inline int str_cmp_str0(const str *a, const str *b);
static inline str *str_init(str *out, char *s);
/* inits a str object from any binary string. returns out */
static inline str *str_init_len(str *out, char *s, int len);
static inline str *str_init_len_assert_len(str *out, char *s, int buflen, int len);
#define str_init_len_assert(out, s, len) str_init_len_assert_len(out, s, sizeof(s), len)
/* returns new str object allocated with malloc, including buffer */
static inline str *str_dup(const str *s);
/* returns new str object allocated from chunk, including buffer */
@ -141,6 +143,10 @@ static inline str *str_init_len(str *out, char *s, int len) {
out->len = len;
return out;
}
static inline str *str_init_len_assert_len(str *out, char *s, int buflen, int len) {
assert(buflen >= len);
return str_init_len(out, s, len);
}
static inline str *str_dup(const str *s) {
str *r;
r = malloc(sizeof(*r) + s->len + 1);

@ -158,7 +158,7 @@ static int stun_attributes(struct stun_attrs *out, str *s, u_int16_t *unknowns)
break;
default:
mylog(LOG_INFO, "Unknown STUN attribute: 0x%04x", type);
ilog(LOG_INFO, "Unknown STUN attribute: 0x%04x", type);
if ((type & 0x8000))
break;
unknowns[uc] = tlv->type;
@ -330,15 +330,15 @@ static int check_fingerprint(str *msg, struct stun_attrs *attrs) {
return 0;
}
static int check_auth(str *msg, struct stun_attrs *attrs, struct peer *peer) {
static int check_auth(str *msg, struct stun_attrs *attrs, struct call_media *media) {
u_int16_t lenX;
char digest[20];
str ufrag[2];
struct iovec iov[3];
if (!peer->ice_ufrag.s || !peer->ice_ufrag.len)
if (!media->ice_ufrag.s || !media->ice_ufrag.len)
return -1;
if (!peer->ice_pwd.s || !peer->ice_pwd.len)
if (!media->ice_pwd.s || !media->ice_pwd.len)
return -1;
ufrag[0] = attrs->username;
@ -350,7 +350,7 @@ static int check_auth(str *msg, struct stun_attrs *attrs, struct peer *peer) {
if (!ufrag[0].len || !ufrag[1].len)
return -1;
if (str_cmp_str(&ufrag[0], &peer->ice_ufrag))
if (str_cmp_str(&ufrag[0], &media->ice_ufrag))
return -1;
lenX = htons((attrs->msg_integrity_attr - msg->s) - 20 + 24);
@ -361,13 +361,13 @@ static int check_auth(str *msg, struct stun_attrs *attrs, struct peer *peer) {
iov[2].iov_base = msg->s + OFFSET_OF(struct header, cookie);
iov[2].iov_len = ntohs(lenX) + - 24 + 20 - OFFSET_OF(struct header, cookie);
__integrity(iov, G_N_ELEMENTS(iov), &peer->ice_pwd, digest);
__integrity(iov, G_N_ELEMENTS(iov), &media->ice_pwd, digest);
return memcmp(digest, attrs->msg_integrity.s, 20) ? -1 : 0;
}
static int stun_binding_success(int fd, struct header *req, struct stun_attrs *attrs,
struct sockaddr_in6 *sin, struct peer *peer)
struct sockaddr_in6 *sin, struct call_media *media)
{
struct header hdr;
struct xor_mapped_address xma;
@ -376,7 +376,7 @@ static int stun_binding_success(int fd, struct header *req, struct stun_attrs *a
struct msghdr mh;
struct iovec iov[4]; /* hdr, xma, mi, fp */
unsigned char buf[256];
struct callmaster *cm = peer->up->call->callmaster;
struct callmaster *cm = media->call->callmaster;
output_init(&mh, iov, sin, &hdr, STUN_BINDING_SUCCESS_RESPONSE, req->transaction, buf, sizeof(buf));
@ -395,7 +395,7 @@ static int stun_binding_success(int fd, struct header *req, struct stun_attrs *a
output_add(&mh, &xma, STUN_XOR_MAPPED_ADDRESS);
}
integrity(&mh, &mi, &peer->ice_pwd);
integrity(&mh, &mi, &media->ice_pwd);
fingerprint(&mh, &fp);
output_finish(&mh, cm);
@ -412,14 +412,14 @@ static inline int u_int16_t_arr_len(u_int16_t *arr) {
}
#define SLF " on port %hu from %s"
#define SLP sr->fd.localport, addr
#define SLF " from %s"
#define SLP addr
/* return values:
* 0 = stun packet processed successfully
* -1 = stun packet not processed, processing should continue as non-stun packet
* 1 = stun packet processed and "use candidate" was set
*/
int stun(str *b, struct streamrelay *sr, struct sockaddr_in6 *sin) {
int stun(str *b, struct packet_stream *ps, struct sockaddr_in6 *sin) {
struct header *req = (void *) b->s;
int msglen, method, class;
str attr_str;
@ -427,7 +427,7 @@ int stun(str *b, struct streamrelay *sr, struct sockaddr_in6 *sin) {
u_int16_t unknowns[UNKNOWNS_COUNT];
const char *err;
char addr[64];
struct callmaster *cm = sr->up->up->call->callmaster;
struct callmaster *cm = ps->call->callmaster;
smart_ntop_port(addr, sin, sizeof(addr));
@ -451,9 +451,9 @@ int stun(str *b, struct streamrelay *sr, struct sockaddr_in6 *sin) {
err = "failed to parse attributes";
if (unknowns[0] == 0xffff)
goto ignore;
mylog(LOG_WARNING, "STUN packet contained unknown "
ilog(LOG_WARNING, "STUN packet contained unknown "
"\"comprehension required\" attribute(s)" SLF, SLP);
stun_error_attrs(cm, sr->fd.fd, sin, req, 420, "Unknown attribute",
stun_error_attrs(cm, ps->sfd->fd.fd, sin, req, 420, "Unknown attribute",
STUN_UNKNOWN_ATTRIBUTES, unknowns,
u_int16_t_arr_len(unknowns) * 2);
return 0;
@ -475,23 +475,23 @@ int stun(str *b, struct streamrelay *sr, struct sockaddr_in6 *sin) {
err = "FINGERPRINT mismatch";
if (check_fingerprint(b, &attrs))
goto ignore;
if (check_auth(b, &attrs, sr->up))
if (check_auth(b, &attrs, ps->media))
goto unauth;
mylog(LOG_NOTICE, "Successful STUN binding request" SLF, SLP);
stun_binding_success(sr->fd.fd, req, &attrs, sin, sr->up);
ilog(LOG_NOTICE, "Successful STUN binding request" SLF, SLP);
stun_binding_success(ps->sfd->fd.fd, req, &attrs, sin, ps->media);
return attrs.use ? 1 : 0;
bad_req:
mylog(LOG_INFO, "Received invalid STUN packet" SLF ": %s", SLP, err);
stun_error(cm, sr->fd.fd, sin, req, 400, "Bad request");
ilog(LOG_INFO, "Received invalid STUN packet" SLF ": %s", SLP, err);
stun_error(cm, ps->sfd->fd.fd, sin, req, 400, "Bad request");
return 0;
unauth:
mylog(LOG_INFO, "STUN authentication mismatch" SLF, SLP);
stun_error(cm, sr->fd.fd, sin, req, 401, "Unauthorized");
ilog(LOG_INFO, "STUN authentication mismatch" SLF, SLP);
stun_error(cm, ps->sfd->fd.fd, sin, req, 401, "Unauthorized");
return 0;
ignore:
mylog(LOG_INFO, "Not handling potential STUN packet" SLF ": %s", SLP, err);
ilog(LOG_INFO, "Not handling potential STUN packet" SLF ": %s", SLP, err);
return -1;
}

@ -11,7 +11,7 @@
#define STUN_COOKIE 0x2112A442UL
static inline int is_stun(str *s) {
static inline int is_stun(const str *s) {
const unsigned char *b = (const void *) s->s;
const u_int32_t *u;
@ -29,7 +29,7 @@ static inline int is_stun(str *s) {
}
int stun(str *, struct streamrelay *, struct sockaddr_in6 *);
int stun(str *, struct packet_stream *, struct sockaddr_in6 *);
#endif

@ -10,6 +10,8 @@
#include "poller.h"
#include "aux.h"
#include "str.h"
#include "log.h"
#include "obj.h"
struct udp_listener_callback {
struct obj obj;
@ -26,7 +28,7 @@ static void udp_listener_incoming(int fd, void *p, uintptr_t x) {
struct sockaddr_in6 sin;
socklen_t sin_len;
int len;
char buf[8192];
char buf[0x10000];
char addr[64];
str str;
@ -39,7 +41,7 @@ static void udp_listener_incoming(int fd, void *p, uintptr_t x) {
if (errno == EINTR)
continue;
if (errno != EWOULDBLOCK && errno != EAGAIN)
mylog(LOG_WARNING, "Error reading from UDP socket");
ilog(LOG_WARNING, "Error reading from UDP socket");
return;
}
@ -58,7 +60,7 @@ int udp_listener_init(struct udp_listener *u, struct poller *p, struct in6_addr
cb = obj_alloc("udp_listener_callback", sizeof(*cb), NULL);
cb->func = func;
cb->p = obj_get(obj);
cb->p = obj_get_o(obj);
u->fd = socket(AF_INET6, SOCK_DGRAM, 0);
if (u->fd == -1)
@ -88,7 +90,7 @@ int udp_listener_init(struct udp_listener *u, struct poller *p, struct in6_addr
fail:
if (u->fd != -1)
close(u->fd);
obj_put(obj);
obj_put(&cb->obj);
obj_put_o(obj);
obj_put(cb);
return -1;
}

10
debian/changelog vendored

@ -1,3 +1,13 @@
ngcp-mediaproxy-ng (2.9.9) unstable; urgency=low
* Complete rewrite of internal media streams handling
* Support for DTLS-SRTP
* Support for unBUNDLE
* Improved support for rtcp-mux
* WIP
-- Richard Fuchs <rfuchs@sipwise.com> Tue, 04 Mar 2014 11:06:23 -0500
ngcp-mediaproxy-ng (2.4.0.0+0~mr3.3.0.0) unstable; urgency=low
[ Richard Fuchs ]

2
debian/control vendored

@ -8,7 +8,7 @@ Build-Depends: debhelper (>= 5),
libcurl3-openssl-dev | libcurl3-gnutls-dev,
libglib2.0-dev,
libpcre3-dev,
libssl-dev,
libssl-dev (>= 1.0.1),
libxmlrpc-c3-dev (>= 1.16.07) | libxmlrpc-core-c3-dev (>= 1.16.07),
markdown,
zlib1g-dev

@ -19,7 +19,7 @@ if [ "$1" = 'configure' ] ; then
# try to start the daemon
if [ -x /etc/init.d/ngcp-mediaproxy-ng-daemon ] ; then
invoke-rc.d ngcp-mediaproxy-ng-daemon start || true
invoke-rc.d ngcp-mediaproxy-ng-daemon restart || true
fi
fi

@ -33,7 +33,7 @@ MODULE_LICENSE("GPL");
#define MAX_ID 64 /* - 1 */
#define MAX_SKB_TAIL_ROOM (128 + 20)
#define MAX_SKB_TAIL_ROOM (sizeof(((struct mediaproxy_srtp *) 0)->mki) + 20)
#define MIPF "%i:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x:%u"
#define MIPP(x) (x).family, \
@ -945,8 +945,11 @@ static void proc_list_crypto_print(struct seq_file *f, struct mp_crypto_context
if (!hdr++)
seq_printf(f, " SRTP %s parameters:\n", label);
seq_printf(f, " cipher: %s\n", c->cipher->name ? : "<invalid>");
if (s->mki || s->mki_len)
seq_printf(f, " MKI: %llu length %u\n", (unsigned long long) s->mki, s->mki_len);
if (s->mki_len)
seq_printf(f, " MKI: length %u, %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x...\n",
s->mki_len,
s->mki[0], s->mki[1], s->mki[2], s->mki[3],
s->mki[4], s->mki[5], s->mki[6], s->mki[7]);
}
if (c->hmac && c->hmac->id != MPH_NULL) {
if (!hdr++)
@ -1064,7 +1067,7 @@ static int validate_srtp(struct mediaproxy_srtp *s) {
return -1;
if (s->auth_tag_len > 20)
return -1;
if (s->mki_len > 128)
if (s->mki_len > sizeof(s->mki))
return -1;
return 0;
}
@ -1930,29 +1933,13 @@ error:
/* XXX shared code */
static void rtp_append_mki(struct rtp_parsed *r, struct mediaproxy_srtp *c) {
u_int32_t mki_part;
unsigned char *p;
if (!c->mki_len)
return;
p = r->payload + r->payload_len;
memset(p, 0, c->mki_len);
if (c->mki_len > 4) {
mki_part = (c->mki & 0xffffffff00000000ULL) >> 32;
mki_part = htonl(mki_part);
if (c->mki_len < 8)
memcpy(p, ((char *) &mki_part) + (8 - c->mki_len), c->mki_len - 4);
else
memcpy(p + (c->mki_len - 8), &mki_part, 4);
}
mki_part = (c->mki & 0xffffffffULL);
mki_part = htonl(mki_part);
if (c->mki_len < 4)
memcpy(p, ((char *) &mki_part) + (4 - c->mki_len), c->mki_len);
else
memcpy(p + (c->mki_len - 4), &mki_part, 4);
memcpy(p, c->mki, c->mki_len);
r->payload_len += c->mki_len;
}
@ -2096,6 +2083,14 @@ static inline int is_muxed_rtcp(struct rtp_parsed *r) {
return 1;
}
static inline int is_dtls(struct rtp_parsed *r) {
if (r->header->m_pt < 20)
return 0;
if (r->header->m_pt > 63)
return 0;
return 1;
}
static unsigned int mediaproxy46(struct sk_buff *skb, struct mediaproxy_table *t) {
struct udphdr *uh;
struct mediaproxy_target *g;
@ -2147,6 +2142,8 @@ not_stun:
goto skip1;
if (g->target.rtcp_mux && is_muxed_rtcp(&rtp))
goto skip1;
if (g->target.dtls && is_dtls(&rtp))
goto skip1;
pkt_idx = packet_index(&g->decrypt, &g->target.decrypt, rtp.header);
if (srtp_auth_validate(&g->decrypt, &g->target.decrypt, &rtp, pkt_idx))
goto skip_error;

@ -46,7 +46,7 @@ struct mediaproxy_srtp {
enum mediaproxy_hmac hmac;
unsigned char master_key[16];
unsigned char master_salt[14];
u_int64_t mki;
unsigned char mki[256]; /* XXX uses too much memory? */
u_int64_t last_index;
unsigned int auth_tag_len; /* in bytes */
unsigned int mki_len;
@ -65,7 +65,8 @@ struct mediaproxy_target_info {
struct mediaproxy_srtp encrypt;
unsigned char tos;
int rtcp_mux:1;
int rtcp_mux:1,
dtls:1;
};
struct mediaproxy_message {

@ -12,10 +12,11 @@ use Time::HiRes;
use Crypt::Rijndael;
use Digest::SHA qw(hmac_sha1);
use MIME::Base64;
use Data::Dumper;
my ($NUM, $RUNTIME, $STREAMS, $PAYLOAD, $INTERVAL, $RTCP_INTERVAL, $STATS_INTERVAL)
= (1000, 30, 1, 160, 20, 5, 5);
my ($NODEL, $IP, $IPV6, $KEEPGOING, $REINVITES, $BRANCHES, $PROTOS, $DEST, $SUITES, $NOENC, $RTCPMUX);
my ($NODEL, $IP, $IPV6, $KEEPGOING, $REINVITES, $PROTOS, $DEST, $SUITES, $NOENC, $RTCPMUX, $BUNDLE, $LAZY);
GetOptions(
'no-delete' => \$NODEL,
'num-calls=i' => \$NUM,
@ -24,7 +25,6 @@ GetOptions(
'runtime=i' => \$RUNTIME,
'keep-going' => \$KEEPGOING, # don't stop sending rtp if a packet doesn't go through
'reinvites' => \$REINVITES,
'branches' => \$BRANCHES,
'max-streams=i' => \$STREAMS,
'protocols=s' => \$PROTOS, # "RTP/AVP,RTP/SAVP"
'destination=s' => \$DEST,
@ -35,6 +35,8 @@ GetOptions(
'suites=s' => \$SUITES,
'no-encrypt' => \$NOENC,
'rtcp-mux' => \$RTCPMUX,
'bundle' => \$BUNDLE,
'lazy-params' => \$LAZY,
) or die;
($IP || $IPV6) or die("at least one of --local-ip or --local-ipv6 must be given");
@ -81,7 +83,7 @@ connect($fd, sockaddr_in($$DEST[1], inet_aton($$DEST[0]))) or die $!;
msg({command => 'ping'})->{result} eq 'pong' or die;
my (@calls, %branches);
my (@calls, %calls);
my %NOENC;
sub send_receive {
@ -267,6 +269,21 @@ sub aes_f8_iv_rtcp {
return $iv;
}
sub append_mki {
my ($ctx_dir, $pack_r) = @_;
$$ctx_dir{rtp_mki_len} or return;
my $mki = pack('N', $$ctx_dir{rtp_mki});
while (length($mki) < $$ctx_dir{rtp_mki_len}) {
$mki = "\x00" . $mki;
}
if (length($mki) > $$ctx_dir{rtp_mki_len}) {
$mki = substr($mki, -$$ctx_dir{rtp_mki_len});
}
$$pack_r .= $mki;
}
sub rtcp_encrypt {
my ($r, $ctx, $dir) = @_;
@ -286,6 +303,8 @@ sub rtcp_encrypt {
my $hmac = hmac_sha1($pkt, $$ctx{$dir}{rtcp_session_auth_key});
append_mki($$ctx{$dir}, \$pkt);
#$pkt .= pack("N", 1); # mki
$pkt .= substr($hmac, 0, 10);
@ -319,6 +338,8 @@ sub rtp_encrypt {
my $hmac = hmac_sha1($pkt . pack("N", $$ctx{$dir}{rtp_roc}), $$ctx{$dir}{rtp_session_auth_key});
# print("HMAC for packet " . unpack("H*", $pkt) . " ROC $roc is " . unpack("H*", $hmac) . "\n");
append_mki($$ctx{$dir}, \$pkt);
#$pkt .= pack("N", 1); # mki
$pkt .= substr($hmac, 0, $$ctx{$dir}{crypto_suite}{auth_tag});
@ -359,6 +380,15 @@ sub savp_sdp {
if (!$$ctx{out}{crypto_suite}) {
$$ctx{out}{crypto_suite} = $$ctx_o{in}{crypto_suite} ? $$ctx_o{in}{crypto_suite}
: $crypto_suites[rand(@crypto_suites)];
$$ctx{out}{rtp_mki_len} = 0;
if (rand() > .5) {
$$ctx{out}{rtp_mki_len} = int(rand(120)) + 1;
$$ctx{out}{rtp_mki} = int(rand(2**30)) | 1;
if ($$ctx{out}{rtp_mki_len} < 32) {
$$ctx{out}{rtp_mki} &= (0xffffffff >> (32 - ($$ctx{out}{rtp_mki_len})));
}
}
}
if (!$$ctx{out}{rtp_master_key}) {
@ -371,7 +401,14 @@ sub savp_sdp {
$NOENC{rtp_master_key} = $$ctx{out}{rtp_master_key};
$NOENC{rtp_master_salt} = $$ctx{out}{rtp_master_salt};
}
return "a=crypto:0 $$ctx{out}{crypto_suite}{str} inline:" . encode_base64($$ctx{out}{rtp_master_key} . $$ctx{out}{rtp_master_salt}, '') . "\n";
my $ret = "a=crypto:0 $$ctx{out}{crypto_suite}{str} inline:" . encode_base64($$ctx{out}{rtp_master_key} . $$ctx{out}{rtp_master_salt}, '');
if ($$ctx{out}{rtp_mki_len}) {
$ret .= "|$$ctx{out}{rtp_mki}:$$ctx{out}{rtp_mki_len}";
}
$ret .= "\n";
return $ret;
}
sub rtcp_sr {
@ -462,18 +499,22 @@ sub rtp_savp {
sub savp_crypto {
my ($sdp, $ctx, $ctx_o) = @_;
my $cs = $$ctx_o{out}{crypto_suite}{str};
my $re = $cs ? qr/\Q$cs\E/ : qr/\w+/;
my @a = $sdp =~ /[\r\n]a=crypto:\d+ ($re) inline:([\w\/+]{40})(?:\|(?:2\^(\d+)|(\d+)))?(?:\|(\d+):(\d+))?[\r\n]/si;
my @a = $sdp =~ /[\r\n]a=crypto:\d+ (\w+) inline:([\w\/+]{40})(?:\|(?:2\^(\d+)|(\d+)))?(?:\|(\d+):(\d+))?[\r\n]/sig;
@a or die;
$$ctx{in}{crypto_suite} = $crypto_suites{$a[0]} or die;
my $ks = decode_base64($a[1]);
length($ks) == 30 or die;
($$ctx{in}{rtp_master_key}, $$ctx{in}{rtp_master_salt}) = unpack('a16a14', $ks);
$$ctx{in}{rtp_mki} = $a[4];
$$ctx{in}{rtp_mki_len} = $a[5];
undef($$ctx{in}{rtp_session_key});
undef($$ctx{in}{rtcp_session_key});
my $i = 0;
while (@a >= 6) {
$$ctx[$i]{in}{crypto_suite} = $crypto_suites{$a[0]} or die;
my $ks = decode_base64($a[1]);
length($ks) == 30 or die;
($$ctx[$i]{in}{rtp_master_key}, $$ctx[$i]{in}{rtp_master_salt}) = unpack('a16a14', $ks);
$$ctx[$i]{in}{rtp_mki} = $a[4];
$$ctx[$i]{in}{rtp_mki_len} = $a[5];
undef($$ctx[$i]{in}{rtp_session_key});
undef($$ctx[$i]{in}{rtcp_session_key});
$i++;
@a = @a[6 .. $#a];
}
}
sub hexdump {
@ -490,46 +531,60 @@ sub do_rtp {
my ($rtcp) = @_;
for my $c (@calls) {
$c or next;
my ($fds,$outputs,$protos,$cfds,$trans,$tctxs)
= @$c{qw(fds outputs protos rtcp_fds transports trans_contexts)};
for my $j (0 .. $#{$$fds[0]}) {
for my $i ([0,1],[1,0]) {
my ($a, $b) = @$i;
my $pr = $$protos[$a];
my $tcx = $$tctxs[$a];
my $tcx_o = $$tctxs[$b];
my $addr = inet_pton($$pr{family}, $$outputs[$b][$j][1]);
my ($payload, $expect) = $$trans[$a]{rtp_func}($$trans[$b], $tcx, $tcx_o);
my $dst = $$pr{sockaddr}($$outputs[$b][$j][0], $addr);
my $repl = send_receive($$fds[$a][$j], $$fds[$b][$j], $payload, $dst);
for my $i ([0,1],[1,0]) {
my ($a, $b) = @$i;
my $A = $$c{sides}[$a];
my $B = $$c{sides}[$b];
my $rtp_fds = $$A{rtp_fds};
my $rtcp_fds = $$A{rtcp_fds};
my $rtp_fds_o = $$B{rtp_fds};
my $rtcp_fds_o = $$B{rtcp_fds};
my $pr = $$A{proto};;
my $trans = $$A{transport};
my $trans_o = $$B{transport};
my $tcx = $$A{trans_contexts};
my $tcx_o = $$B{trans_contexts};
my $outputs = $$A{outputs};
for my $j (0 .. ($$A{streams_active} - 1)) {
my ($bj_a, $bj_b) = ($j, $j);
$$A{bundle}
and $bj_a = 0;
$$B{bundle}
and $bj_b = 0;
my $addr = inet_pton($$pr{family}, $$outputs[$j][1]);
my ($payload, $expect) = $$trans{rtp_func}($trans_o, $$tcx[$j], $$tcx_o[$j]);
my $dst = $$pr{sockaddr}($$outputs[$j][0], $addr);
my $repl = send_receive($$rtp_fds[$bj_a], $$rtp_fds_o[$bj_b], $payload, $dst);
$RTP_COUNT++;
if ($repl eq '') {
warn("no rtp reply received, ports $$outputs[$b][$j][0] and $$outputs[$a][$j][0]");
warn("no rtp reply received, port $$outputs[$j][0]");
$KEEPGOING or undef($c);
}
$NOENC and $repl = $expect;
!$repl && $KEEPGOING and next;
$repl eq $expect or die hexdump($repl, $expect) . " $$trans[$a]{name} > $$trans[$b]{name}, ports $$outputs[$b][$j][0] and $$outputs[$a][$j][0]";
$repl eq $expect or die hexdump($repl, $expect) . " $$trans{name} > $$trans_o{name}, $$c{callid}, RTP port $$outputs[$j][0]";
$rtcp or next;
($payload, $expect) = $$trans[$a]{rtcp_func}($$trans[$b], $tcx, $tcx_o);
my $dstport = $$outputs[$b][$j][0] + 1;
my $sendfd = $$cfds[$a][$j];
my $expfd = $$cfds[$b][$j];
if ($RTCPMUX) {
if (!$a) {
$dstport--;
$sendfd = $$fds[$a][$j];
}
else {
$expfd = $$fds[$b][$j];
}
($payload, $expect) = $$trans{rtcp_func}($trans_o, $$tcx[$j], $$tcx_o[$j]);
my $dstport = $$outputs[$j][0] + 1;
my $sendfd = $$rtcp_fds[$bj_a];
my $expfd = $$rtcp_fds_o[$bj_b];
if ($$A{rtcpmux}) {
$dstport--;
$sendfd = $$rtp_fds[$bj_a];
}
if ($$B{rtcpmux}) {
$expfd = $$rtp_fds_o[$bj_b];
}
$dst = $$pr{sockaddr}($dstport, $addr);
$repl = send_receive($sendfd, $expfd, $payload, $dst);
$NOENC and $repl = $expect;
!$repl && $KEEPGOING and next;
$repl eq $expect or die hexdump($repl, $expect) . " $$trans[$a]{name} > $$trans[$b]{name}";
$repl eq $expect or die hexdump($repl, $expect) . " $$trans{name} > $$trans_o{name}, $$c{callid}, RTCP";
}
}
}
@ -590,82 +645,102 @@ my %transports = map {$$_{name} => $_} @transports;
sub callid {
my $i = rand_str(50);
$BRANCHES or return [$i];
rand() < .5 and return [$i];
if (rand() < .5) {
my @k = keys(%branches);
@k and $i = $k[rand(@k)];
}
my $b = rand_str(20);
push(@{$branches{$i}}, $b);
return [$i, $b];
return $i;
}
my $NUM_STREAMS = 0;
sub update_lookup {
my ($c, $i) = @_;
my $j = $i ^ 1;
my $c_v = $$c{callid_viabranch} || ($$c{callid_viabranch} = callid());
my ($callid, $viabranch) = @$c_v;
my $protos = $$c{protos} || ($$c{protos} = []);
my $trans = $$c{transports} || ($$c{transports} = []);
my $tctxs = $$c{trans_contexts} || ($$c{trans_contexts} = []);
my $fds_a = $$c{fds} || ($$c{fds} = []);
my $cfds_a = $$c{rtcp_fds} || ($$c{rtcp_fds} = []);
for my $x (0,1) {
$$protos[$x] and next;
$$protos[$x] = $protos_avail[rand(@protos_avail)];
undef($$fds_a[$x]);
sub port_setup {
my ($r, $j) = @_;
my $pr = $$r{proto};
my $rtp_fds = $$r{rtp_fds};
my $rtcp_fds = $$r{rtcp_fds};
my $ports = $$r{ports};
my $ips = $$r{ips};
my $tcx = $$r{trans_contexts};
$$tcx[$j] or $$tcx[$j] = {};
while (1) {
socket(my $rtp, $$pr{family}, SOCK_DGRAM, 0) or die $!;
socket(my $rtcp, $$pr{family}, SOCK_DGRAM, 0) or die $!;
my $port = rand(0x7000) << 1 + 1024;
bind($rtp, $$pr{sockaddr}($port,
inet_pton($$pr{family}, $$pr{address}))) or next;
bind($rtcp, $$pr{sockaddr}($port + 1,
inet_pton($$pr{family}, $$pr{address}))) or next;
$$rtp_fds[$j] = $rtp;
$$rtcp_fds[$j] = $rtcp;
my $addr = getsockname($rtp);
my $ip;
($$ports[$j], $ip) = $$pr{sockaddr}($addr);
$$ips[$j] = inet_ntop($$pr{family}, $ip);
last;
}
for my $x (0,1) {
$$trans[$x] and next;
$$trans[$x] = ($PROTOS && $$PROTOS[$x] && $transports{$$PROTOS[$x]})
? $transports{$$PROTOS[$x]}
}
sub side_setup {
my ($i) = @_;
my $r = {};
my $pr = $$r{proto} = $protos_avail[rand(@protos_avail)];
$$r{transport} = ($PROTOS && $$PROTOS[$i] && $transports{$$PROTOS[$i]})
? $transports{$$PROTOS[$i]}
: $transports[rand(@transports)];
}
my ($pr, $pr_o) = @$protos[$i, $j];
my ($tr, $tr_o) = @$trans[$i, $j];
my $tcx = $$tctxs[$i] || ($$tctxs[$i] = {});
my $tcx_o = $$tctxs[$j] || ($$tctxs[$j] = {});
my @commands = qw(offer answer);
my $ports_a = $$c{ports} || ($$c{ports} = []);
my $ports_t = $$ports_a[$i] || ($$ports_a[$i] = []);
my $ips_a = $$c{ips} || ($$c{ips} = []);
my $ips_t = $$ips_a[$i] || ($$ips_a[$i] = []);
my $fds_t = $$fds_a[$i] || ($$fds_a[$i] = []);
my $fds_o = $$fds_a[$j];
my $cfds_t = $$cfds_a[$i] || ($$cfds_a[$i] = []);
my $cfds_o = $$cfds_a[$j];
my $num_streams = int(rand($STREAMS));
($fds_o && @$fds_o) and $num_streams = $#$fds_o;
for my $j (0 .. $num_streams) {
if (!$$fds_t[$j]) {
$NUM_STREAMS++;
undef($$tcx_o{in});
while (1) {
undef($$fds_t[$j]);
undef($$cfds_t[$j]);
socket($$fds_t[$j], $$pr{family}, SOCK_DGRAM, 0) or die $!;
socket($$cfds_t[$j], $$pr{family}, SOCK_DGRAM, 0) or die $!;
my $port = rand(0x7000) << 1 + 1024;
bind($$fds_t[$j], $$pr{sockaddr}($port,
inet_pton($$pr{family}, $$pr{address}))) or next;
bind($$cfds_t[$j], $$pr{sockaddr}($port + 1,
inet_pton($$pr{family}, $$pr{address}))) or next;
last;
}
my $addr = getsockname($$fds_t[$j]);
my $ip;
($$ports_t[$j], $ip) = $$pr{sockaddr}($addr);
$$ips_t[$j] = inet_ntop($$pr{family}, $ip);
}
$$r{trans_contexts} = [];
$$r{outputs} = [];
$$r{num_streams} = int(rand($STREAMS));
$$r{streams_seen} = 0;
$$r{streams_active} = 0;
$$r{rtp_fds} = [];
$$r{rtcp_fds} = [];
$$r{ports} = [];
$$r{ips} = [];
for my $j (0 .. $$r{num_streams}) {
port_setup($r, $j);
}
my $tags = $$c{tags} || ($$c{tags} = []);
$$tags[$i] or $$tags[$i] = rand_str(15);
$$r{tag} = rand_str(15);
$RTCPMUX and $$r{want_rtcpmux} = rand() >= .3;
$BUNDLE and $$r{want_bundle} = rand() >= .3;
$$r{want_bundle} and $$r{want_rtcpmux} = 1;
return $r;
}
sub call_setup {
my ($c) = @_;
$$c{setup} = 1;
$$c{callid} = callid();
$$c{sides}[0] = side_setup(0);
$$c{sides}[1] = side_setup(1);
}
sub offer_answer {
my ($c, $a, $b, $op) = @_;
$$c{setup} or call_setup($c);
my $callid = $$c{callid} || ($$c{callid} = callid());
my $A = $$c{sides}[$a];
my $B = $$c{sides}[$b];
my $pr = $$A{proto};
my $pr_o = $$B{proto};
my $ips_t = $$A{ips};
my $ports_t = $$A{ports};
my $tr = $$A{transport};
my $tr_o = $$B{transport};
my $tcx = $$A{trans_contexts};
my $tcx_o = $$B{trans_contexts};
my $sdp = <<"!";
v=0
@ -674,67 +749,148 @@ s=session
c=IN $$pr{family_str} $$ips_t[0]
t=0 0
!
for my $p (@$ports_t) {
my $ul = $$A{num_streams};
$op eq 'answer' && $$A{streams_seen} < $$A{num_streams}
and $ul = $$A{streams_seen};
$$A{want_bundle} && $op eq 'offer' and
$$A{bundle} = 1,
$sdp .= "a=group:BUNDLE " . join(' ', (0 .. $ul)) . "\n";
for my $i (0 .. $ul) {
my $bi = $i;
$$A{bundle}
and $bi = 0;
my $p = $$ports_t[$bi];
my $cp = $p + 1;
$$A{bundle} && $$A{want_rtcpmux} && $op eq 'offer'
and $cp = $p;
$sdp .= <<"!";
m=audio $p $$tr{name} 8
a=rtpmap:8 PCMA/8000
!
if ($RTCPMUX && !$i) {
if ($$A{want_rtcpmux} && $op eq 'offer') {
$sdp .= "a=rtcp-mux\n";
rand() >= .5 and $sdp .= "a=rtcp:$p\n";
$sdp .= "a=rtcp:$cp\n";
$$A{rtcpmux} = 1;
}
else {
$sdp .= "a=rtcp:$cp\n";
rand() >= .5 and $sdp .= "a=rtcp:$cp\n";
}
$$tr{sdp_media_params} and $sdp .= $$tr{sdp_media_params}($tcx, $tcx_o);
$$tr{sdp_media_params} and $sdp .= $$tr{sdp_media_params}($$tcx[$i], $$tcx_o[$i]);
$$A{bundle} and
$sdp .= "a=mid:$i\n";
}
for my $x (($ul + 1) .. $$A{streams_seen}) {
$sdp .= "m=audio 0 $$tr{name} 0\n";
}
$i or print("transport is $$tr{name} -> $$tr_o{name}\n");
my $dict = {sdp => $sdp, command => $commands[$i], 'call-id' => $callid,
'from-tag' => $$tags[0],
flags => [ qw( trust-address ) ],
replace => [ qw( origin session-connection ) ],
direction => [ $$pr{direction}, $$pr_o{direction} ],
'received-from' => [ qw(IP4 127.0.0.1) ],
'transport-protocol' => $$tr_o{name},
$op eq 'offer' and print("transport is $$tr{name} -> $$tr_o{name}\n");
#print(Dumper($op, $A, $B, $sdp) . "\n\n\n\n");
#print("sdp $op in:\n$sdp\n\n");
my $dict = {sdp => $sdp, command => $op, 'call-id' => $$c{callid},
flags => [ 'trust address' ],
replace => [ 'origin', 'session connection' ],
#direction => [ $$pr{direction}, $$pr_o{direction} ],
'received from' => [ qw(IP4 127.0.0.1) ],
'rtcp-mux' => ['demux'],
};
$viabranch and $dict->{'via-branch'} = $viabranch;
$i == 1 and $dict->{'to-tag'} = $$tags[1];
#$viabranch and $dict->{'via-branch'} = $viabranch;
if ($op eq 'offer') {
$dict->{'from-tag'} = $$A{tag};
rand() > .5 and $$dict{'to-tag'} = $$B{tag};
}
elsif ($op eq 'answer') {
$dict->{'from-tag'} = $$B{tag},
$dict->{'to-tag'} = $$A{tag};
}
if (!$LAZY
|| ($op eq 'offer' && !$$c{established})
|| (rand() > .5))
{
$$dict{'address family'} = $$pr_o{family_str};
$$dict{'transport protocol'} = $$tr_o{name};
}
#print(Dumper($dict) . "\n\n");
my $o = msg($dict);
$$o{result} eq 'ok' or die;
#print("sdp $op out:\n$$o{sdp}\n\n\n\n");
my ($rp_af, $rp_add) = $$o{sdp} =~ /c=IN IP([46]) (\S+)/s or die;
$RTCPMUX && $i and ($$o{sdp} =~ /a=rtcp-mux/s or die);
$$B{rtcpmux} and ($$o{sdp} =~ /a=rtcp-mux/s or die);
my @rp_ports = $$o{sdp} =~ /m=audio (\d+) \Q$$tr_o{name}\E /gs or die;
$$B{streams_seen} = $#rp_ports;
$rp_af ne $$pr_o{reply} and die "incorrect address family reply code";
my $rpl_a = $$c{outputs} || ($$c{outputs} = []);
my $rpl_t = $$rpl_a[$i] = [];
for my $rpl (@rp_ports) {
$rpl == 0 and die "mediaproxy ran out of ports";
$NUM_STREAMS -= $$B{streams_active};
$$B{streams_active} = 0;
my $old_outputs = $$B{outputs};
my $rpl_t = $$B{outputs} = [];
for my $i (0 .. $#rp_ports) {
my $rpl = $rp_ports[$i];
if ($rpl == 0) {
$op eq 'offer' and $$B{streams_seen}--;
if ($$A{rtp_fds}[$i]) {
undef($$A{rtp_fds}[$i]);
}
next;
}
$$B{ports}[$i] or next;
$$B{streams_active}++;
$NUM_STREAMS++;
push(@$rpl_t, [$rpl,$rp_add]);
my $oa = shift(@$old_outputs);
if (defined($oa) && $$oa[0] != $rpl) {
print("port change: $$oa[0] -> $rpl\n");
#print(Dumper($i, $c) . "\n");
undef($$tcx_o[$i]{out}{rtcp_index});
undef($$tcx_o[$i]{out}{rtp_roc});
}
}
$$tr_o{sdp_parse_func} and $$tr_o{sdp_parse_func}($$o{sdp}, $tcx_o, $tcx);
#print(Dumper($op, $A, $B) . "\n\n\n\n");
$op eq 'answer' and $$c{established} = 1;
}
sub offer {
my ($c, $a, $b) = @_;
return offer_answer($c, $a, $b, 'offer');
}
sub answer {
my ($c, $a, $b) = @_;
return offer_answer($c, $a, $b, 'answer');
}
for my $iter (1 .. $NUM) {
($iter % 10 == 0) and print("$iter calls established\n"), do_rtp();
my $c = {};
update_lookup($c, 0);
update_lookup($c, 1);
offer($c, 0, 1);
answer($c, 1, 0);
push(@calls, $c);
$calls{$$c{callid}} = $c;
}
print("all calls established\n");
#print(Dumper(\@calls) . "\n");
my $end = time() + $RUNTIME;
my $rtptime = Time::HiRes::gettimeofday();
my $rtcptime = $rtptime;
my $countstart = $rtptime;
my $countstop = $countstart + $STATS_INTERVAL;
my $last_reinv = 0;
my $last_reinv = $rtptime;
while (time() < $end) {
my $now = Time::HiRes::gettimeofday();
$now <= $rtptime and Time::HiRes::sleep($rtptime - $now);
@ -761,23 +917,27 @@ while (time() < $end) {
@calls = sort {rand() < .5} grep(defined, @calls);
if ($REINVITES && $now >= $last_reinv + 5) {
if ($REINVITES && $now >= $last_reinv + 15) {
$last_reinv = $now;
my $c = $calls[rand(@calls)];
print("simulating re-invite on $$c{callid_viabranch}[0]");
print("simulating re-invite on $$c{callid}\n");
for my $i (0,1) {
if (rand() < .5) {
print(", side $sides[$i]: new port");
undef($$c{fds}[$i]);
$NUM_STREAMS--;
}
else {
print(", side $sides[$i]: same port");
my $s = $$c{sides}[$i];
for my $j (0 .. $$s{num_streams}) {
if (rand() < .5) {
print("\tside $sides[$i] stream #$j: new port\n");
port_setup($s, $j);
#print("\n" . Dumper($i, $c) . "\n");
undef($$s{trans_contexts}[$j]{in}{rtcp_index});
undef($$s{trans_contexts}[$j]{in}{rtp_roc});
}
else {
print("\tside $sides[$i] stream #$j: same port\n");
}
}
}
print("\n");
update_lookup($c, 0);
update_lookup($c, 1);
offer($c, 0, 1);
answer($c, 1, 0);
}
}
@ -785,12 +945,12 @@ if (!$NODEL) {
print("deleting\n");
for my $c (@calls) {
$c or next;
my ($tags, $c_v) = @$c{qw(tags callid_viabranch)};
my ($callid, $viabranch) = @$c_v;
my $dict = { command => 'delete', 'call-id' => $callid, 'from-tag' => $$tags[0],
'to-tag' => $$tags[1],
my $callid = $$c{callid};
my $fromtag = $$c{sides}[0]{tag};
my $totag = $$c{sides}[1]{tag};
my $dict = { command => 'delete', 'call-id' => $callid, 'from-tag' => $fromtag,
'to-tag' => $totag,
};
$BRANCHES && rand() < .7 and $$dict{'via-branch'} = $viabranch;
msg($dict);
}
}

@ -26,6 +26,11 @@ GetOptions(
'sdp=s' => \$options{'sdp'},
'sdp-file=s' => \$options{'sdp-file'},
'ICE=s' => \$options{'ICE'},
'rtcp-mux-offer' => \$options{'rtcp-mux-offer'},
'rtcp-mux-demux' => \$options{'rtcp-mux-demux'},
'rtcp-mux-accept' => \$options{'rtcp-mux-accept'},
'rtcp-mux-reject' => \$options{'rtcp-mux-reject'},
'address-family=s' => \$options{'address family'},
'force' => \$options{'force'},
'v|verbose' => \$options{'verbose'},
) or die;
@ -34,7 +39,7 @@ my $cmd = shift(@ARGV) or die;
my %packet = (command => $cmd);
for my $x (split(',', 'from-tag,to-tag,call-id,transport protocol,media address,ICE')) {
for my $x (split(',', 'from-tag,to-tag,call-id,transport protocol,media address,ICE,address family')) {
defined($options{$x}) and $packet{$x} = $options{$x};
}
for my $x (split(',', 'trust address,symmetric,asymmetric,force')) {
@ -43,6 +48,9 @@ for my $x (split(',', 'trust address,symmetric,asymmetric,force')) {
for my $x (split(',', 'origin,session connection')) {
defined($options{'replace-' . $x}) and push(@{$packet{replace}}, $x);
}
for my $x (split(',', 'offer,demux,accept,reject')) {
defined($options{'rtcp-mux-' . $x}) and push(@{$packet{'rtcp-mux'}}, $x);
}
if (defined($options{sdp})) {
$packet{sdp} = $options{sdp};

Loading…
Cancel
Save