From c75394d3cd2778182032b29676ab27e265163336 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Thu, 24 Jan 2013 16:15:58 -0500 Subject: [PATCH] first take on SDP rewriting --- daemon/bencode.c | 21 +++++- daemon/bencode.h | 20 ++++++ daemon/call.c | 135 +++++++++++++++++++++++++----------- daemon/call.h | 1 + daemon/control_ng.c | 2 +- daemon/log.h | 1 + daemon/sdp.c | 163 +++++++++++++++++++++++++++++++++++++++++++- daemon/sdp.h | 2 + daemon/str.h | 3 +- 9 files changed, 303 insertions(+), 45 deletions(-) diff --git a/daemon/bencode.c b/daemon/bencode.c index 1562c260a..7e4a716cc 100644 --- a/daemon/bencode.c +++ b/daemon/bencode.c @@ -16,6 +16,10 @@ struct __bencode_buffer_piece { struct __bencode_buffer_piece *next; char buf[0]; }; +struct __bencode_free_list { + void *ptr; + struct __bencode_free_list *next; +}; @@ -356,7 +360,7 @@ static bencode_item_t *bencode_decode_dictionary(bencode_buffer_t *buf, const ch return NULL; bencode_dictionary_init(ret); - while (s > end) { + while (s < end) { item = __bencode_decode(buf, s, end); if (!item) return NULL; @@ -393,7 +397,7 @@ static bencode_item_t *bencode_decode_list(bencode_buffer_t *buf, const char *s, return NULL; bencode_list_init(ret); - while (s > end) { + while (s < end) { item = __bencode_decode(buf, s, end); if (!item) return NULL; @@ -552,3 +556,16 @@ bencode_item_t *bencode_dictionary_get_len(bencode_item_t *dict, const char *key return NULL; } + +void bencode_buffer_freelist_add(bencode_buffer_t *buf, void *p) { + struct __bencode_free_list *li; + + if (!p) + return; + li = __bencode_alloc(buf, sizeof(*li)); + if (!li) + return; + li->ptr = p; + li->next = buf->free_list; + buf->free_list = li; +} diff --git a/daemon/bencode.h b/daemon/bencode.h index 35043ba36..76c878d70 100644 --- a/daemon/bencode.h +++ b/daemon/bencode.h @@ -25,6 +25,7 @@ struct bencode_buffer; enum bencode_type; struct bencode_item; struct __bencode_buffer_piece; +struct __bencode_free_list; typedef enum bencode_type bencode_type_t; typedef struct bencode_buffer bencode_buffer_t; @@ -52,6 +53,7 @@ struct bencode_item { struct bencode_buffer { struct __bencode_buffer_piece *pieces; + struct __bencode_free_list *free_list; }; /* Initializes a bencode_buffer_t object. This object is used to group together all memory allocations @@ -73,6 +75,10 @@ bencode_item_t *bencode_dictionary(bencode_buffer_t *buf); * Returns NULL if no memory could be allocated. */ bencode_item_t *bencode_list(bencode_buffer_t *buf); +/* Adds a pointer to the bencode_buffer_t object's internal free list. When the bencode_buffer_t + * object is destroyed, BENCODE_FREE will be called on this pointer. */ +void bencode_buffer_freelist_add(bencode_buffer_t *buf, void *); + /* Adds a new key/value pair to a dictionary. Memory will be allocated from the same bencode_buffer_t * object as the dictionary was allocated from. Returns NULL if no memory could be allocated, otherwise * returns "val". @@ -91,6 +97,11 @@ static inline bencode_item_t *bencode_dictionary_add_string(bencode_item_t *dict /* Ditto, but for a "str" object */ static inline bencode_item_t *bencode_dictionary_add_str(bencode_item_t *dict, const char *key, const str *val); +/* Ditto again, but adds the buffer which contains the string (val->s) to the bencode_buffer_t's + * internal free list. When the bencode_item_t object is destroyed, BENCODE_FREE will be called + * on this buffer. */ +static inline bencode_item_t *bencode_dictionary_add_str_free(bencode_item_t *dict, const char *key, const str *val); + /* Convenience function to add an integer value to a dictionary */ static inline bencode_item_t *bencode_dictionary_add_integer(bencode_item_t *dict, const char *key, long long int val); @@ -264,6 +275,13 @@ static inline bencode_item_t *bencode_dictionary_add_str(bencode_item_t *dict, c return bencode_dictionary_add(dict, key, bencode_str(dict->buffer, val)); } +static inline bencode_item_t *bencode_dictionary_add_str_free(bencode_item_t *dict, const char *key, const str *val) { + if (!val) + return NULL; + bencode_buffer_freelist_add(dict->buffer, val->s); + return bencode_dictionary_add(dict, key, bencode_str(dict->buffer, val)); +} + static inline bencode_item_t *bencode_dictionary_add_integer(bencode_item_t *dict, const char *key, long long int val) { return bencode_dictionary_add(dict, key, bencode_integer(dict->buffer, val)); } @@ -289,6 +307,8 @@ static inline char *bencode_dictionary_get_string(bencode_item_t *dict, const ch static inline char *bencode_dictionary_get_str(bencode_item_t *dict, const char *key, str *str) { str->s = bencode_dictionary_get_string(dict, key, &str->len); + if (!str->s) + str->len = 0; return str->s; } diff --git a/daemon/call.c b/daemon/call.c index a657f9187..5429c2338 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -1450,16 +1450,73 @@ static void call_destroy(struct call *c) { +/* XXX use enum format */ +static int call_stream_address4(GString *o, struct peer *p, int format) { + struct callstream *cs = p->up; + u_int32_t ip4; + struct callmaster *m = cs->call->callmaster; + + if (format == 2) + g_string_append(o, "IP4 "); + + ip4 = p->rtps[0].peer.ip46.s6_addr32[3]; + if (!ip4) + g_string_append(o, "0.0.0.0"); + else if (m->conf.adv_ipv4) + g_string_append_printf(o, IPF, IPP(m->conf.adv_ipv4)); + else + g_string_append_printf(o, IPF, IPP(m->conf.ipv4)); + + return AF_INET; +} + +static int call_stream_address6(GString *o, struct peer *p, int format) { + char ips[64]; + struct callmaster *m = p->up->call->callmaster; + + if (format == 2) + g_string_append(o, "IP4 "); + + if (IN6_IS_ADDR_UNSPECIFIED(&p->rtps[0].peer.ip46)) + g_string_append(o, "::"); + else { + if (!IN6_IS_ADDR_UNSPECIFIED(&m->conf.adv_ipv6)) + inet_ntop(AF_INET6, &m->conf.adv_ipv6, ips, sizeof(ips)); + else + inet_ntop(AF_INET6, &m->conf.ipv6, ips, sizeof(ips)); + g_string_append(o, ips); + } + + return AF_INET6; +} + + +int call_stream_address(GString *o, struct peer *p, int format) { + struct callmaster *m; + struct peer *other; + + m = p->up->call->callmaster; + other = &p->up->peers[p->idx ^ 1]; + + if (other->desired_family == AF_INET) + return call_stream_address4(o, p, format); + if (other->desired_family == 0 && IN6_IS_ADDR_V4MAPPED(&other->rtps[0].peer.ip46)) + return call_stream_address4(o, p, format); + if (IN6_IS_ADDR_UNSPECIFIED(&m->conf.ipv6)) + return call_stream_address4(o, p, format); + + return call_stream_address6(o, p, format); +} + + + static str *streams_print(GQueue *s, unsigned int num, unsigned int off, const char *prefix, int format) { GString *o; int i; GList *l; struct callstream *t; struct streamrelay *x; - char ips[64]; - u_int32_t ip4; - int other_off; - char af; + int af; o = g_string_new_str(); if (prefix) @@ -1469,35 +1526,8 @@ static str *streams_print(GQueue *s, unsigned int num, unsigned int off, const c goto out; t = s->head->data; - other_off = (off == 0) ? 1 : 0; - - if (t->peers[other_off].desired_family == AF_INET - || (t->peers[other_off].desired_family == 0 - && IN6_IS_ADDR_V4MAPPED(&t->peers[other_off].rtps[0].peer.ip46)) - || IN6_IS_ADDR_UNSPECIFIED(&t->call->callmaster->conf.ipv6)) { - ip4 = t->peers[off].rtps[0].peer.ip46.s6_addr32[3]; - if (!ip4) - strcpy(ips, "0.0.0.0"); - else if (t->call->callmaster->conf.adv_ipv4) - sprintf(ips, IPF, IPP(t->call->callmaster->conf.adv_ipv4)); - else - sprintf(ips, IPF, IPP(t->call->callmaster->conf.ipv4)); - - af = '4'; - } - else { - if (IN6_IS_ADDR_UNSPECIFIED(&t->peers[off].rtps[0].peer.ip46)) - strcpy(ips, "::"); - else if (!IN6_IS_ADDR_UNSPECIFIED(&t->call->callmaster->conf.adv_ipv6)) - inet_ntop(AF_INET6, &t->call->callmaster->conf.adv_ipv6, ips, sizeof(ips)); - else - inet_ntop(AF_INET6, &t->call->callmaster->conf.ipv6, ips, sizeof(ips)); - - af = '6'; - } - if (format == 0) - g_string_append(o, ips); + call_stream_address(o, &t->peers[off], format); for (i = 0, l = s->head; i < num && l; i++, l = l->next) { t = l->data; @@ -1505,8 +1535,10 @@ static str *streams_print(GQueue *s, unsigned int num, unsigned int off, const c g_string_append_printf(o, (format == 1) ? "%u " : " %u", x->localport); } - if (format == 1) - g_string_append_printf(o, "%s %c", ips, af); + if (format == 1) { + af = call_stream_address(o, &t->peers[off], format); + g_string_append_printf(o, " %c", (af == AF_INET) ? '4' : '6'); + } out: g_string_append(o, "\n"); @@ -2102,18 +2134,23 @@ struct callstream *callstream_new(struct call *ca, int num) { const char *call_offer(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) { - str sdp, fromtag; + str sdp, fromtag, viabranch, callid, *sdp_new; char *errstr; GQueue parsed = G_QUEUE_INIT; GQueue streams = G_QUEUE_INIT; - int ret; + struct call *call; + int num; - bencode_dictionary_get_str(input, "sdp", &sdp); - if (!sdp.s) + if (!bencode_dictionary_get_str(input, "sdp", &sdp)) return "No SDP body in message"; - bencode_dictionary_get_str(input, "from-tag", &fromtag); - if (!fromtag.s) + 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, "via-branch", &viabranch); + log_info = &viabranch; + + /* XXX get rid of "direction" and use "desired_family" as intended? */ if (sdp_parse(&sdp, &parsed)) return "Failed to parse SDP"; @@ -2122,10 +2159,28 @@ const char *call_offer(bencode_item_t *input, struct callmaster *m, bencode_item if (sdp_streams(&parsed, &streams)) goto out; + call = call_get_or_create(&callid, &viabranch, m); + log_info = &viabranch; + call->calling_agent = "UNKNOWN(ng)"; /* XXX get rid of, or make use of */ + + num = call_streams(call, &streams, &fromtag, 0); + sdp_new = sdp_replace(&sdp, &parsed, call, abs(num), (num >= 0) ? 0 : 1); + + mutex_unlock(&call->lock); + obj_put(call); + + errstr = "Error rewriting SDP"; + if (!sdp_new) + goto out; + + bencode_dictionary_add_str_free(output, "sdp", sdp_new); + bencode_dictionary_add_string(output, "result", "ok"); + errstr = NULL; out: sdp_free(&parsed); streams_free(&streams); + log_info = NULL; return errstr; } diff --git a/daemon/call.h b/daemon/call.h index 03efa9099..5bd2814c7 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -140,6 +140,7 @@ struct call *call_get_or_create(const str *callid, const str *viabranch, struct struct callstream *callstream_new(struct call *ca, int num); void callstream_init(struct callstream *s, int port1, int port2); void kernelize(struct callstream *c); +int call_stream_address(GString *o, struct peer *p, int format); static inline char *call_strdup(struct call *c, const char *s) { char *r; diff --git a/daemon/control_ng.c b/daemon/control_ng.c index 82c1d5dc2..63a3d110d 100644 --- a/daemon/control_ng.c +++ b/daemon/control_ng.c @@ -26,7 +26,7 @@ static void control_ng_incoming(struct obj *obj, str *buf, struct sockaddr_in6 * resp = bencode_dictionary(&bencbuf); cookie = *buf; - cookie.len = data.s - buf->s; + cookie.len -= data.len; *data.s++ = '\0'; data.len--; diff --git a/daemon/log.h b/daemon/log.h index 7767281d2..bbefadf31 100644 --- a/daemon/log.h +++ b/daemon/log.h @@ -6,6 +6,7 @@ #define mylog(x,y...) syslog(x,y) +#define LOG_ERROR LOG_ERR diff --git a/daemon/sdp.c b/daemon/sdp.c index fb6f55c40..33fb73847 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -7,6 +7,7 @@ #include "call.h" #include "log.h" #include "str.h" +#include "call.h" struct network_address { str network_type; @@ -31,8 +32,8 @@ struct sdp_connection { struct sdp_session { struct sdp_origin origin; struct sdp_connection connection; - GQueue media_streams; GQueue attributes; + GQueue media_streams; }; struct sdp_media { @@ -48,6 +49,12 @@ struct sdp_media { GQueue attributes; }; +struct string_chopper { + str *input; + GString *output; + int position; +}; + @@ -321,8 +328,12 @@ int sdp_streams(const GQueue *sessions, GQueue *streams) { else goto error; + /* XXX ports must be consecutive */ + /* XXX check for RTP type */ stream->port = (media->port_num + (i * 2)) & 0xffff; stream->num = ++num; + + g_queue_push_tail(streams, stream); } } } @@ -333,3 +344,153 @@ error: mylog(LOG_WARNING, "Failed to extract streams from SDP: %s", errstr); return -1; } + +static void chopper_init(struct string_chopper *c, str *input) { + c->input = input; + c->output = g_string_new_str(); + c->position = 0; +} + +static int copy_up_to(struct string_chopper *chop, str *where) { + int offset, len; + + offset = where->s - chop->input->s; + assert(offset >= 0); + assert(offset < chop->input->len); + + len = offset - chop->position; + if (len < 0) { + mylog(LOG_WARNING, "Malformed SDP, cannot rewrite"); + return -1; + } + g_string_append_len(chop->output, chop->input->s + chop->position, len); + chop->position += len; + return 0; +} + +static void copy_remainder(struct string_chopper *chop) { + int len; + len = chop->input->len - chop->position; + assert(len >= 0); + g_string_append_len(chop->output, chop->input->s + chop->position, len); + chop->position += len; +} + +static int skip_over(struct string_chopper *chop, str *where) { + int offset, len; + + offset = (where->s - chop->input->s) + where->len; + assert(offset >= 0); + assert(offset < chop->input->len); + + len = offset - chop->position; + if (len < 0) { + mylog(LOG_WARNING, "Malformed SDP, cannot rewrite"); + return -1; + } + chop->position += len; + return 0; +} + +static int replace_port(struct string_chopper *chop, str *port, GList *m, int off) { + struct callstream *cs; + struct streamrelay *sr; + + if (!m) { + mylog(LOG_ERROR, "BUG! Ran out of streams"); + return -1; + } + + cs = m->data; + sr = &cs->peers[off].rtps[0]; + + if (copy_up_to(chop, port)) + return -1; + + g_string_append_printf(chop->output, "%hu", sr->localport); + + if (skip_over(chop, port)) + return -1; + + return 0; +} + +static int replace_network_address(struct string_chopper *chop, struct network_address *address, GList *m, int off) { + struct callstream *cs; + struct peer *peer; + + if (!m) { + mylog(LOG_ERROR, "BUG! Ran out of streams"); + return -1; + } + + cs = m->data; + peer = &cs->peers[off]; + + if (copy_up_to(chop, &address->address_type)) + return -1; + + call_stream_address(chop->output, peer, 2); + + if (skip_over(chop, &address->address)) + return -1; + + return 0; +} + +static str *chopper_done(struct string_chopper *chop) { + str *ret; + ret = g_string_free_str(chop->output); + return ret; +} + +static void chopper_destroy(struct string_chopper *chop) { + g_string_free(chop->output, TRUE); +} + +/* XXX use stream numbers as index */ +/* XXX use port numbers as index */ +/* XXX get rid of num/off parameters? */ +str *sdp_replace(str *body, GQueue *sessions, struct call *call, int num, int off) { + struct sdp_session *session; + struct sdp_media *media; + GList *l, *k, *m; + struct string_chopper chop; + + chopper_init(&chop, body); + m = call->callstreams->head; + + for (l = sessions->head; l; l = l->next) { + session = l->data; + + if (session->origin.parsed) { + if (replace_network_address(&chop, &session->origin.address, m, off)) + goto error; + } + if (session->connection.parsed) { + if (replace_network_address(&chop, &session->connection.address, m, off)) + goto error; + } + + for (k = session->media_streams.head; k; k = k->next) { + media = k->data; + + /* XXX take multiple ports into account */ + if (replace_port(&chop, &media->port, m, off)) + goto error; + + if (media->connection.parsed) { + if (replace_network_address(&chop, &media->connection.address, m, off)) + goto error; + } + } + } + + copy_remainder(&chop); + return chopper_done(&chop); + +error: + mylog(LOG_ERROR, "Error rewriting SDP"); + chopper_destroy(&chop); + return NULL; +} diff --git a/daemon/sdp.h b/daemon/sdp.h index d8370a264..55f435c62 100644 --- a/daemon/sdp.h +++ b/daemon/sdp.h @@ -3,9 +3,11 @@ #include #include "str.h" +#include "call.h" int sdp_parse(str *body, GQueue *sessions); int sdp_streams(const GQueue *sessions, GQueue *streams); void sdp_free(GQueue *sessions); +str *sdp_replace(str *body, GQueue *sessions, struct call *call, int num, int off); #endif diff --git a/daemon/str.h b/daemon/str.h index 5c3523610..2e40bcfb4 100644 --- a/daemon/str.h +++ b/daemon/str.h @@ -76,7 +76,7 @@ static inline char *str_chr(const str *s, int c) { } static inline str *str_chr_str(str *out, const str *s, int c) { out->s = str_chr(s, c); - out->len = out->s ? out->s - s->s : 0; + out->len = out->s ? (s->len - (out->s - s->s)) : 0; return out; } static inline int str_cmp(const str *a, const char *b) { @@ -169,6 +169,7 @@ static inline str *g_string_free_str(GString *gs) { pl = strlen(STR_MALLOC_PADDING); assert(gs->len >= pl); + assert(memcmp(gs->str, STR_MALLOC_PADDING, pl) == 0); ret = (void *) gs->str; ret->s = gs->str + pl; ret->len = gs->len - pl;