diff --git a/daemon/call.c b/daemon/call.c index f052fba15..9049edd70 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -23,6 +23,7 @@ #include "redis.h" #include "xt_MEDIAPROXY.h" #include "bencode.h" +#include "sdp.h" @@ -2119,11 +2120,18 @@ struct callstream *callstream_new(struct call *ca, int num) { const char *call_offer(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) { const char *sdp; int sdp_len; + GQueue parsed = G_QUEUE_INIT; + GQueue streams = G_QUEUE_INIT; sdp = bencode_dictionary_get_string(input, "sdp", &sdp_len); if (!sdp) return "No SDP body in message"; + if (sdp_parse(sdp, sdp_len, &parsed)) + return "Failed to parse SDP"; + + sdp_free(&parsed); + return NULL; } diff --git a/daemon/sdp.c b/daemon/sdp.c index 3fc05400b..df8e00bf5 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -1,4 +1,7 @@ #include +#include +#include +#include #include "sdp.h" #include "call.h" @@ -9,20 +12,23 @@ struct string { int len; }; +struct network_address { + struct string network_type; + struct string address_type; + struct string address; + struct in6_addr parsed; +}; + struct sdp_origin { struct string username; struct string session_id; struct string version; - struct string network_type; - struct string address_type; - struct string address; + struct network_address address; int parsed:1; }; struct sdp_connection { - struct string network_type; - struct string address_type; - struct string address; + struct network_address address; int parsed:1; }; @@ -39,6 +45,9 @@ struct sdp_media { struct string transport; /* ... format list */ + long int port_num; + int port_count; + struct sdp_connection connection; GQueue attributes; }; @@ -46,6 +55,45 @@ struct sdp_media { +/* hack hack */ +static inline int inet_pton_str(int af, struct string *src, void *dst) { + char *s = (void *) src->s; + char p; + int ret; + p = s[src->len]; + s[src->len] = '\0'; + ret = inet_pton(af, src->s, dst); + s[src->len] = p; + return ret; +} + +static int parse_address(struct network_address *address) { + struct in_addr in4; + + if (address->network_type.len != 2) + return -1; + if (memcmp(address->network_type.s, "IN", 2) + && memcmp(address->network_type.s, "in", 2)) + return -1; + if (address->address_type.len != 3) + return -1; + if (!memcmp(address->address_type.s, "IP4", 3) + || !memcmp(address->address_type.s, "ip4", 3)) { + if (inet_pton_str(AF_INET, &address->address, &in4) != 1) + return -1; + in4_to_6(&address->parsed, in4.s_addr); + } + else if (!memcmp(address->address_type.s, "IP6", 3) + || !memcmp(address->address_type.s, "ip6", 3)) { + if (inet_pton_str(AF_INET6, &address->address, &address->parsed) != 1) + return -1; + } + else + return -1; + + return 0; +} + static inline int extract_token(const char **sp, const char *end, struct string *out) { const char *space; @@ -66,6 +114,11 @@ static inline int extract_token(const char **sp, const char *end, struct string } #define EXTRACT_TOKEN(field) if (extract_token(&start, end, &output->field)) return -1 +#define EXTRACT_NETWORK_ADDRESS(field) \ + EXTRACT_TOKEN(field.network_type); \ + EXTRACT_TOKEN(field.address_type); \ + EXTRACT_TOKEN(field.address); \ + if (parse_address(&output->address)) return -1 static int parse_origin(const char *start, const char *end, struct sdp_origin *output) { if (output->parsed) @@ -74,9 +127,7 @@ static int parse_origin(const char *start, const char *end, struct sdp_origin *o EXTRACT_TOKEN(username); EXTRACT_TOKEN(session_id); EXTRACT_TOKEN(version); - EXTRACT_TOKEN(network_type); - EXTRACT_TOKEN(address_type); - EXTRACT_TOKEN(address); + EXTRACT_NETWORK_ADDRESS(address); output->parsed = 1; return 0; @@ -86,31 +137,45 @@ static int parse_connection(const char *start, const char *end, struct sdp_conne if (output->parsed) return -1; - EXTRACT_TOKEN(network_type); - EXTRACT_TOKEN(address_type); - EXTRACT_TOKEN(address); + EXTRACT_NETWORK_ADDRESS(address); output->parsed = 1; return 0; } static int parse_media(const char *start, const char *end, struct sdp_media *output) { + char *ep; + EXTRACT_TOKEN(media_type); EXTRACT_TOKEN(port); EXTRACT_TOKEN(transport); + output->port_num = strtol(output->port.s, &ep, 10); + if (ep == output->port.s) + return -1; + if (output->port_num <= 0 || output->port_num > 0xffff) + return -1; + + if (*ep == '/') { + output->port_count = atoi(ep + 1); + if (output->port_count <= 0) + return -1; + if (output->port_count > 10) /* unsupported */ + return -1; + } + else + output->port_count = 1; + return 0; } -GQueue *sdp_parse(const char *body, int len, GQueue *streams) { +int sdp_parse(const char *body, int len, GQueue *sessions) { const char *b, *end, *value, *line_end, *next_line; struct sdp_session *session = NULL; - GQueue *sessions; struct sdp_media *media = NULL; const char *errstr; struct string *attribute; - sessions = g_queue_new(); b = body; end = body + len; @@ -205,10 +270,31 @@ GQueue *sdp_parse(const char *body, int len, GQueue *streams) { b = next_line; } - return sessions; + return 0; error: - mylog(LOG_WARNING, "Error parsing SDP: %s", errstr); - /* XXX free sessions */ - return NULL; + mylog(LOG_WARNING, "Error parsing SDP at offset %li: %s", (b - body), errstr); + sdp_free(sessions); + return -1; +} + +static void __free_attributes(GQueue *a) { + struct string *str; + while ((str = g_queue_pop_head(a))) { + g_slice_free1(sizeof(*str), str); + } +} + +void sdp_free(GQueue *sessions) { + struct sdp_session *session; + struct sdp_media *media; + + while ((session = g_queue_pop_head(sessions))) { + while ((media = g_queue_pop_head(&session->media_streams))) { + __free_attributes(&media->attributes); + g_slice_free1(sizeof(*media), media); + } + __free_attributes(&session->attributes); + g_slice_free1(sizeof(*session), session); + } } diff --git a/daemon/sdp.h b/daemon/sdp.h index 84a158110..434b61167 100644 --- a/daemon/sdp.h +++ b/daemon/sdp.h @@ -3,6 +3,7 @@ #include -GQueue *sdp_parse(const char *body, int len, GQueue *streams); +int sdp_parse(const char *body, int len, GQueue *sessions); +void sdp_free(GQueue *sessions); #endif diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 000000000..78005f0df --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1 @@ +sdp-parse-test diff --git a/tests/sdp-parse-test.c b/tests/sdp-parse-test.c index 1c1fbae8c..6d6628f6b 100644 --- a/tests/sdp-parse-test.c +++ b/tests/sdp-parse-test.c @@ -6,12 +6,16 @@ #include "../daemon/sdp.h" int main() { - char *sdp = "v=0\r\no=root 25669 25669 IN IP4 192.168.51.133\r\ns=session\r\nc=IN IP4 192.168.51.133\r\nt=0 0\r\nm=audio 30018 RTP/AVP 8 0 101\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:101 telephone-event/8000\r\na=fmtp:101 0-16\r\na=silenceSupp:off - - - -\r\na=ptime:20\r\na=sendrecv\r\na=nortpproxy:yes\r\n"; + char sdp[] = "v=0\r\no=root 25669 25669 IN IP4 192.168.51.133\r\ns=session\r\nc=IN IP4 192.168.51.133\r\nt=0 0\r\nm=audio 30018 RTP/AVP 8 0 101\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:101 telephone-event/8000\r\na=fmtp:101 0-16\r\na=silenceSupp:off - - - -\r\na=ptime:20\r\na=sendrecv\r\na=nortpproxy:yes\r\n"; int len = strlen(sdp); - GQueue *ret; + int i; + GQueue ret = G_QUEUE_INIT; - ret = sdp_parse(sdp, len, NULL); - printf("%i stream(s)\n", g_queue_get_length(ret)); + i = sdp_parse(sdp, len, &ret); + if (i) + return 0; + + printf("%i stream(s)\n", g_queue_get_length(&ret)); return 0; }