diff --git a/daemon/bencode.h b/daemon/bencode.h index 91f7ed95e..871a805a7 100644 --- a/daemon/bencode.h +++ b/daemon/bencode.h @@ -122,6 +122,10 @@ bencode_item_t *bencode_string_len(bencode_buffer_t *buf, const char *s, int len * to bencode_string_len(). */ static inline bencode_item_t *bencode_string(bencode_buffer_t *buf, const char *s); +/* Convenience function to compare a string object to a regular C string. Returns 2 if object + * isn't a string object, otherwise returns according to strcmp(). */ +static inline int bencode_strcmp(bencode_item_t *a, const char *b); + /* Creates a new byte-string object from a "str" object. The string does not have to be null- * terminated. */ static inline bencode_item_t *bencode_str(bencode_buffer_t *buf, const str *s); @@ -360,5 +364,16 @@ static inline str *bencode_collapse_str(bencode_item_t *root, str *out) { out->s = bencode_collapse(root, &out->len); return out; } +static inline int bencode_strcmp(bencode_item_t *a, const char *b) { + int len; + if (a->type != BENCODE_STRING) + return 2; + len = strlen(b); + if (a->iov[1].iov_len < len) + return -1; + if (a->iov[1].iov_len > len) + return 1; + return memcmp(a->iov[1].iov_base, b, len); +} #endif diff --git a/daemon/call.c b/daemon/call.c index 24b74292b..c03a0b406 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -45,10 +45,6 @@ static __thread const str *log_info; /* also serves as array index for callstream->peers[] */ -enum opmode { - OP_OFFER = 0, - OP_ANSWER = 1, -}; struct iterator_helper { GSList *del; struct streamrelay *ports[0x10000]; @@ -1174,7 +1170,7 @@ static void callstream_free(void *ptr) { } /* called with call->lock held */ -static int call_streams(struct call *c, GQueue *s, const str *tag, enum opmode opmode) { +static int call_streams(struct call *c, GQueue *s, const str *tag, enum call_opmode opmode) { GQueue *q; GList *i, *l; struct stream_input *t; @@ -1220,7 +1216,7 @@ static int call_streams(struct call *c, GQueue *s, const str *tag, enum opmode o found: /* cs_o remains locked if set */ - if (!opmode) { /* request */ + if (opmode == OP_OFFER) { DBG("creating new callstream"); cs = callstream_new(c, t->stream.num); @@ -1507,18 +1503,18 @@ int call_stream_address(GString *o, struct peer *p, enum stream_address_format f -static str *streams_print(GQueue *s, int rawnum, enum opmode opmode, const char *prefix, enum stream_address_format format) { +static str *streams_print(GQueue *s, int num, enum call_opmode opmode, const char *prefix, enum stream_address_format format) { GString *o; - int i, num, off; + int i, off; GList *l; struct callstream *t; struct streamrelay *x; int af; - num = abs(rawnum); off = opmode; /* 0 or 1 */ if (num < 0) off ^= 1; /* 1 or 0 */ + num = abs(num); o = g_string_new_str(); if (prefix) @@ -1635,7 +1631,7 @@ static struct call *call_get(const str *callid, const str *viabranch, struct cal } /* returns call with lock held, or possibly NULL iff opmode == OP_ANSWER */ -static struct call *call_get_opmode(const str *callid, const str *viabranch, struct callmaster *m, enum opmode opmode) { +static struct call *call_get_opmode(const str *callid, const str *viabranch, struct callmaster *m, enum call_opmode opmode) { if (opmode == OP_OFFER) return call_get_or_create(callid, viabranch, m); return call_get(callid, viabranch, m); @@ -1686,7 +1682,7 @@ fail: return -1; } -static str *call_update_lookup_udp(char **out, struct callmaster *m, enum opmode opmode, int tagidx) { +static str *call_update_lookup_udp(char **out, struct callmaster *m, enum call_opmode opmode, int tagidx) { struct call *c; GQueue q = G_QUEUE_INIT; struct stream_input st; @@ -1738,7 +1734,7 @@ str *call_lookup_udp(char **out, struct callmaster *m) { return call_update_lookup_udp(out, m, OP_ANSWER, RE_UDP_UL_TOTAG); } -static str *call_request_lookup(char **out, struct callmaster *m, enum opmode opmode, const char *tagstr) { +static str *call_request_lookup(char **out, struct callmaster *m, enum call_opmode opmode, const char *tagstr) { struct call *c; GQueue s = G_QUEUE_INIT; int num; @@ -2091,6 +2087,53 @@ struct callstream *callstream_new(struct call *ca, int num) { } +static void call_ng_process_flags(struct sdp_ng_flags *out, GQueue *streams, bencode_item_t *input) { + bencode_item_t *list, *it; + struct stream_input *si; + int diridx; + enum stream_direction dirs[2]; + GList *gl; + + ZERO(*out); + ZERO(dirs); + + if ((list = bencode_dictionary_get_expect(input, "flags", BENCODE_LIST))) { + for (it = list->child; it; it = it->sibling) { + if (!bencode_strcmp(it, "trust-address")) /* XXX not implemented */ + out->trust_address = 1; + else if (!bencode_strcmp(it, "symmetric")) + out->symmetric = 1; + else if (!bencode_strcmp(it, "asymmetric")) + out->asymmetric = 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; + } + } + + 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")) + dirs[diridx++] = DIR_INTERNAL; + else if (!bencode_strcmp(it, "external")) + dirs[diridx++] = DIR_INTERNAL; + } + + for (gl = streams->head; gl; gl = gl->next) { + si = gl->data; + si->direction[0] = dirs[0]; + si->direction[1] = dirs[1]; + } + } +} + const char *call_offer(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) { str sdp, fromtag, viabranch, callid, *sdp_new; char *errstr; @@ -2098,6 +2141,7 @@ const char *call_offer(bencode_item_t *input, struct callmaster *m, bencode_item GQueue streams = G_QUEUE_INIT; struct call *call; int num; + struct sdp_ng_flags flags; if (!bencode_dictionary_get_str(input, "sdp", &sdp)) return "No SDP body in message"; @@ -2108,8 +2152,6 @@ const char *call_offer(bencode_item_t *input, struct callmaster *m, bencode_item 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"; @@ -2117,11 +2159,13 @@ const char *call_offer(bencode_item_t *input, struct callmaster *m, bencode_item if (sdp_streams(&parsed, &streams)) goto out; + call_ng_process_flags(&flags, &streams, input); + call = call_get_or_create(&callid, &viabranch, m); log_info = &viabranch; num = call_streams(call, &streams, &fromtag, OP_OFFER); - sdp_new = sdp_replace(&sdp, &parsed, call, abs(num), (num >= 0) ? 0 : 1); + sdp_new = sdp_replace(&sdp, &parsed, call, num, OP_OFFER, &flags); mutex_unlock(&call->lock); obj_put(call); diff --git a/daemon/call.h b/daemon/call.h index b0ca4827c..c881b2eda 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -35,6 +35,15 @@ enum stream_address_format { SAF_UDP, SAF_NG, }; +enum stream_direction { + DIR_UNKNOWN = 0, + DIR_INTERNAL, + DIR_EXTERNAL, +}; +enum call_opmode { + OP_OFFER = 0, + OP_ANSWER = 1, +}; struct stats { u_int64_t packets; @@ -49,11 +58,7 @@ struct stream { }; struct stream_input { struct stream stream; - enum { - DIR_UNKNOWN = 0, - DIR_INTERNAL, - DIR_EXTERNAL, - } direction[2]; + enum stream_direction direction[2]; }; struct streamrelay { int fd; diff --git a/daemon/sdp.c b/daemon/sdp.c index c27ea6142..5fb17d2d6 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -452,11 +452,17 @@ static void chopper_destroy(struct string_chopper *chop) { /* XXX use port numbers as index */ /* XXX get rid of num/off parameters? */ /* XXX use iovec based rewriting */ -str *sdp_replace(str *body, GQueue *sessions, struct call *call, int num, int off) { +str *sdp_replace(str *body, GQueue *sessions, struct call *call, int num, enum call_opmode opmode, struct sdp_ng_flags *flags) { struct sdp_session *session; struct sdp_media *media; GList *l, *k, *m; struct string_chopper chop; + int off; + + off = opmode; + if (num < 0) + off ^= 1; + num = abs(num); chopper_init(&chop, body); m = call->callstreams->head; @@ -464,7 +470,7 @@ str *sdp_replace(str *body, GQueue *sessions, struct call *call, int num, int of for (l = sessions->head; l; l = l->next) { session = l->data; - if (session->origin.parsed) { + if (session->origin.parsed && flags->replace_origin) { if (replace_network_address(&chop, &session->origin.address, m, off)) goto error; } @@ -480,7 +486,7 @@ str *sdp_replace(str *body, GQueue *sessions, struct call *call, int num, int of if (replace_port(&chop, &media->port, m, off)) goto error; - if (media->connection.parsed) { + if (media->connection.parsed && flags->replace_sess_conn) { if (replace_network_address(&chop, &media->connection.address, m, off)) goto error; } diff --git a/daemon/sdp.h b/daemon/sdp.h index 55f435c62..6aa98a2ca 100644 --- a/daemon/sdp.h +++ b/daemon/sdp.h @@ -5,9 +5,19 @@ #include "str.h" #include "call.h" + +struct sdp_ng_flags { + int desired_family[2]; + int asymmetric:1, + symmetric:1, + trust_address:1, + replace_origin:1, + replace_sess_conn:1; +}; + 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); +str *sdp_replace(str *body, GQueue *sessions, struct call *call, int num, enum call_opmode, struct sdp_ng_flags *); #endif diff --git a/daemon/str.h b/daemon/str.h index c3cee3932..7cf5613b1 100644 --- a/daemon/str.h +++ b/daemon/str.h @@ -35,6 +35,8 @@ static inline char *str_chr(const str *s, int c); static inline str *str_chr_str(str *out, const str *s, int c); /* compares a str to a regular string */ static inline int str_cmp(const str *a, const char *b); +/* compares a str to a non-null-terminated string */ +static inline int str_cmp_len(const str *a, const char *b, int len); /* compares two str objects */ static inline int str_cmp_str(const str *a, const str *b); /* compares two str objects, allows either to be NULL */ @@ -80,8 +82,7 @@ static inline str *str_chr_str(str *out, const str *s, int c) { out->len = out->s ? (s->len - (out->s - s->s)) : 0; return out; } -static inline int str_cmp(const str *a, const char *b) { - int l = strlen(b); +static inline int str_cmp_len(const str *a, const char *b, int l) { if (a->len < l) return -1; if (a->len > l) @@ -90,6 +91,9 @@ static inline int str_cmp(const str *a, const char *b) { return 0; return memcmp(a->s, b, l); } +static inline int str_cmp(const str *a, const char *b) { + return str_cmp_len(a, b, strlen(b)); +} static inline int str_cmp_str(const str *a, const str *b) { if (a->len < b->len) return -1;