michael@0: /* michael@0: Copyright (c) 2007, Adobe Systems, Incorporated michael@0: All rights reserved. michael@0: michael@0: Redistribution and use in source and binary forms, with or without michael@0: modification, are permitted provided that the following conditions are michael@0: met: michael@0: michael@0: * Redistributions of source code must retain the above copyright michael@0: notice, this list of conditions and the following disclaimer. michael@0: michael@0: * Redistributions in binary form must reproduce the above copyright michael@0: notice, this list of conditions and the following disclaimer in the michael@0: documentation and/or other materials provided with the distribution. michael@0: michael@0: * Neither the name of Adobe Systems, Network Resonance nor the names of its michael@0: contributors may be used to endorse or promote products derived from michael@0: this software without specific prior written permission. michael@0: michael@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: */ michael@0: michael@0: michael@0: michael@0: static char *RCSSTRING __UNUSED__="$Id: ice_parser.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; michael@0: michael@0: #include michael@0: #include michael@0: #ifdef WIN32 michael@0: #include michael@0: #else michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #endif michael@0: #include michael@0: #include michael@0: #include michael@0: #include "nr_api.h" michael@0: #include "ice_ctx.h" michael@0: #include "ice_candidate.h" michael@0: #include "ice_reg.h" michael@0: michael@0: static void michael@0: skip_whitespace(char **str) michael@0: { michael@0: char *c = *str; michael@0: while (*c == ' ') michael@0: ++c; michael@0: michael@0: *str = c; michael@0: } michael@0: michael@0: static void michael@0: fast_forward(char **str, int skip) michael@0: { michael@0: char *c = *str; michael@0: while (*c != '\0' && skip-- > 0) michael@0: ++c; michael@0: michael@0: *str = c; michael@0: } michael@0: michael@0: static void michael@0: skip_to_past_space(char **str) michael@0: { michael@0: char *c = *str; michael@0: while (*c != ' ' && *c != '\0') michael@0: ++c; michael@0: michael@0: *str = c; michael@0: michael@0: skip_whitespace(str); michael@0: } michael@0: michael@0: static int michael@0: grab_token(char **str, char **out) michael@0: { michael@0: int _status; michael@0: char *c = *str; michael@0: int len; michael@0: char *tmp; michael@0: michael@0: while (*c != ' ' && *c != '\0') michael@0: ++c; michael@0: michael@0: len = c - *str; michael@0: michael@0: tmp = RMALLOC(len + 1); michael@0: if (!tmp) michael@0: ABORT(R_NO_MEMORY); michael@0: michael@0: memcpy(tmp, *str, len); michael@0: tmp[len] = '\0'; michael@0: michael@0: *str = c; michael@0: *out = tmp; michael@0: michael@0: _status = 0; michael@0: abort: michael@0: return _status; michael@0: } michael@0: michael@0: int michael@0: nr_ice_peer_candidate_from_attribute(nr_ice_ctx *ctx,char *orig,nr_ice_media_stream *stream,nr_ice_candidate **candp) michael@0: { michael@0: int r,_status; michael@0: char* str = orig; michael@0: nr_ice_candidate *cand; michael@0: char *connection_address=0; michael@0: unsigned int port; michael@0: in_addr_t addr; michael@0: int i; michael@0: unsigned int component_id; michael@0: char *rel_addr=0; michael@0: michael@0: if(!(cand=RCALLOC(sizeof(nr_ice_candidate)))) michael@0: ABORT(R_NO_MEMORY); michael@0: michael@0: if(!(cand->label=r_strdup(orig))) michael@0: ABORT(R_NO_MEMORY); michael@0: michael@0: cand->ctx=ctx; michael@0: cand->isock=0; michael@0: cand->state=NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED; michael@0: cand->stream=stream; michael@0: skip_whitespace(&str); michael@0: michael@0: /* Candidate attr */ michael@0: if (strncasecmp(str, "candidate:", 10)) michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: fast_forward(&str, 10); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: skip_whitespace(&str); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: /* Foundation */ michael@0: if ((r=grab_token(&str, &cand->foundation))) michael@0: ABORT(r); michael@0: michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: skip_whitespace(&str); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: /* component */ michael@0: if (sscanf(str, "%u", &component_id) != 1) michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: if (component_id < 1 || component_id > 256) michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: cand->component_id = (UCHAR)component_id; michael@0: michael@0: skip_to_past_space(&str); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: /* Protocol */ michael@0: if (strncasecmp(str, "UDP", 3)) michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: fast_forward(&str, 3); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: skip_whitespace(&str); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: /* priority */ michael@0: if (sscanf(str, "%u", &cand->priority) != 1) michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: if (cand->priority < 1) michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: skip_to_past_space(&str); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: /* Peer address/port */ michael@0: if ((r=grab_token(&str, &connection_address))) michael@0: ABORT(r); michael@0: michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: addr = inet_addr(connection_address); michael@0: if (addr == INADDR_NONE) michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: skip_whitespace(&str); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: if (sscanf(str, "%u", &port) != 1) michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: if (port < 1 || port > 0x0FFFF) michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: /* Assume v4 for now */ michael@0: if(r=nr_ip4_port_to_transport_addr(ntohl(addr),port,IPPROTO_UDP,&cand->addr)) michael@0: ABORT(r); michael@0: michael@0: skip_to_past_space(&str); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: /* Type */ michael@0: if (strncasecmp("typ", str, 3)) michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: fast_forward(&str, 3); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: skip_whitespace(&str); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: assert(nr_ice_candidate_type_names[0] == 0); michael@0: #if __STDC_VERSION__ >= 201112L michael@0: _Static_assert(nr_ice_candidate_type_names[0] == 0,"Candidate name array is misformatted"); michael@0: #endif michael@0: michael@0: for (i = 1; nr_ice_candidate_type_names[i]; ++i) { michael@0: if(!strncasecmp(nr_ice_candidate_type_names[i], str, strlen(nr_ice_candidate_type_names[i]))) { michael@0: cand->type=i; michael@0: break; michael@0: } michael@0: } michael@0: if (nr_ice_candidate_type_names[i] == 0) michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: fast_forward(&str, strlen(nr_ice_candidate_type_names[i])); michael@0: michael@0: /* Look for the other side's raddr, rport */ michael@0: /* raddr, rport */ michael@0: switch (cand->type) { michael@0: case HOST: michael@0: break; michael@0: case SERVER_REFLEXIVE: michael@0: case PEER_REFLEXIVE: michael@0: case RELAYED: michael@0: michael@0: skip_whitespace(&str); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: if (strncasecmp("raddr", str, 5)) michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: fast_forward(&str, 5); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: skip_whitespace(&str); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: if ((r=grab_token(&str, &rel_addr))) michael@0: ABORT(r); michael@0: michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: addr = inet_addr(rel_addr); michael@0: if (addr == INADDR_NONE) michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: skip_whitespace(&str); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: if (strncasecmp("rport", str, 5)) michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: fast_forward(&str, 5); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: skip_whitespace(&str); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: if (sscanf(str, "%u", &port) != 1) michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: if (port < 1 || port > 0x0FFFF) michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: /* Assume v4 for now */ michael@0: if(r=nr_ip4_port_to_transport_addr(ntohl(addr),port,IPPROTO_UDP,&cand->base)) michael@0: ABORT(r); michael@0: michael@0: skip_to_past_space(&str); michael@0: /* it's expected to be at EOD at this point */ michael@0: michael@0: break; michael@0: default: michael@0: ABORT(R_INTERNAL); michael@0: break; michael@0: } michael@0: michael@0: skip_whitespace(&str); michael@0: michael@0: /* Ignore extensions per RFC 5245 S 15.1 */ michael@0: #if 0 michael@0: /* This used to be an assert, but we don't want to exit on invalid michael@0: remote data */ michael@0: if (strlen(str) != 0) { michael@0: ABORT(R_BAD_DATA); michael@0: } michael@0: #endif michael@0: michael@0: nr_ice_candidate_compute_codeword(cand); michael@0: michael@0: *candp=cand; michael@0: michael@0: _status=0; michael@0: abort: michael@0: if (_status){ michael@0: r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Error parsing attribute: %s",ctx->label,orig); michael@0: nr_ice_candidate_destroy(&cand); michael@0: } michael@0: michael@0: RFREE(connection_address); michael@0: RFREE(rel_addr); michael@0: return(_status); michael@0: } michael@0: michael@0: michael@0: int michael@0: nr_ice_peer_ctx_parse_media_stream_attribute(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *attr) michael@0: { michael@0: int r,_status; michael@0: char *orig = 0; michael@0: char *str; michael@0: michael@0: orig = str = attr; michael@0: michael@0: if (!strncasecmp(str, "ice-ufrag:", 10)) { michael@0: fast_forward(&str, 10); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: skip_whitespace(&str); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: if ((r=grab_token(&str, &stream->ufrag))) michael@0: ABORT(r); michael@0: } michael@0: else if (!strncasecmp(str, "ice-pwd:", 8)) { michael@0: fast_forward(&str, 8); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: skip_whitespace(&str); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: if ((r=grab_token(&str, &stream->pwd))) michael@0: ABORT(r); michael@0: } michael@0: else { michael@0: ABORT(R_BAD_DATA); michael@0: } michael@0: michael@0: skip_whitespace(&str); michael@0: michael@0: /* RFC 5245 grammar doesn't have an extension point for ice-pwd or michael@0: ice-ufrag: if there's anything left on the line, we treat it as bad. */ michael@0: if (str[0] != '\0') { michael@0: ABORT(R_BAD_DATA); michael@0: } michael@0: michael@0: _status=0; michael@0: abort: michael@0: if (_status) { michael@0: if (orig) michael@0: r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): Error parsing attribute: %s",pctx->label,orig); michael@0: } michael@0: michael@0: return(_status); michael@0: } michael@0: michael@0: int michael@0: nr_ice_peer_ctx_parse_global_attributes(nr_ice_peer_ctx *pctx, char **attrs, int attr_ct) michael@0: { michael@0: int r,_status; michael@0: int i; michael@0: char *orig = 0; michael@0: char *str; michael@0: char *component_id = 0; michael@0: char *connection_address = 0; michael@0: unsigned int port; michael@0: in_addr_t addr; michael@0: char *ice_option_tag = 0; michael@0: michael@0: for(i=0;i 0x0FFFF) michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: skip_to_past_space(&str); michael@0: michael@0: #if 0 michael@0: /* TODO: !nn! just drop on the floor for now, later put somewhere */ michael@0: /* Assume v4 for now */ michael@0: if(r=nr_ip4_port_to_transport_addr(ntohl(addr),port,IPPROTO_UDP,&candidate->base)) michael@0: ABORT(r); michael@0: michael@0: TAILQ_INSERT_TAIL(head, elm, field); michael@0: #endif michael@0: michael@0: component_id = 0; /* prevent free */ michael@0: RFREE(connection_address); michael@0: connection_address = 0; /* prevent free */ michael@0: } michael@0: } michael@0: else if (!strncasecmp(str, "ice-lite", 8)) { michael@0: pctx->peer_lite = 1; michael@0: michael@0: fast_forward(&str, 8); michael@0: } michael@0: else if (!strncasecmp(str, "ice-mismatch", 12)) { michael@0: pctx->peer_ice_mismatch = 1; michael@0: michael@0: fast_forward(&str, 12); michael@0: } michael@0: else if (!strncasecmp(str, "ice-ufrag:", 10)) { michael@0: fast_forward(&str, 10); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: skip_whitespace(&str); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: RFREE(pctx->peer_ufrag); michael@0: pctx->peer_ufrag = 0; michael@0: if ((r=grab_token(&str, &pctx->peer_ufrag))) michael@0: ABORT(r); michael@0: } michael@0: else if (!strncasecmp(str, "ice-pwd:", 8)) { michael@0: fast_forward(&str, 8); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: skip_whitespace(&str); michael@0: if (*str == '\0') michael@0: ABORT(R_BAD_DATA); michael@0: michael@0: RFREE(pctx->peer_pwd); michael@0: pctx->peer_pwd = 0; michael@0: if ((r=grab_token(&str, &pctx->peer_pwd))) michael@0: ABORT(r); michael@0: } michael@0: else if (!strncasecmp(str, "ice-options:", 12)) { michael@0: fast_forward(&str, 12); michael@0: skip_whitespace(&str); michael@0: michael@0: while (*str != '\0') { michael@0: if ((r=grab_token(&str, &ice_option_tag))) michael@0: ABORT(r); michael@0: michael@0: skip_whitespace(&str); michael@0: michael@0: //TODO: for now, just throw away; later put somewhere michael@0: RFREE(ice_option_tag); michael@0: michael@0: ice_option_tag = 0; /* prevent free */ michael@0: } michael@0: } michael@0: else { michael@0: ABORT(R_BAD_DATA); michael@0: } michael@0: michael@0: skip_whitespace(&str); michael@0: michael@0: /* RFC 5245 grammar doesn't have an extension point for any of the michael@0: preceding attributes: if there's anything left on the line, we michael@0: treat it as bad data. */ michael@0: if (str[0] != '\0') { michael@0: ABORT(R_BAD_DATA); michael@0: } michael@0: } michael@0: michael@0: _status=0; michael@0: abort: michael@0: if (_status) { michael@0: if (orig) michael@0: r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): Error parsing attribute: %s",pctx->label,orig); michael@0: } michael@0: michael@0: RFREE(connection_address); michael@0: RFREE(component_id); michael@0: RFREE(ice_option_tag); michael@0: return(_status); michael@0: } michael@0: