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_socket.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; michael@0: michael@0: #include michael@0: #include michael@0: #include "nr_api.h" michael@0: #include "ice_ctx.h" michael@0: #include "stun.h" michael@0: michael@0: static void nr_ice_socket_readable_cb(NR_SOCKET s, int how, void *cb_arg) michael@0: { michael@0: int r; michael@0: nr_ice_stun_ctx *sc1,*sc2; michael@0: nr_ice_socket *sock=cb_arg; michael@0: UCHAR buf[8192]; michael@0: char string[256]; michael@0: nr_transport_addr addr; michael@0: int len; michael@0: size_t len_s; michael@0: int is_stun; michael@0: int is_req; michael@0: int is_ind; michael@0: int processed_indication=0; michael@0: michael@0: nr_socket *stun_srv_sock=sock->sock; michael@0: michael@0: r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Socket ready to read",sock->ctx->label); michael@0: michael@0: /* Re-arm first! */ michael@0: NR_ASYNC_WAIT(s,how,nr_ice_socket_readable_cb,cb_arg); michael@0: michael@0: if(r=nr_socket_recvfrom(sock->sock,buf,sizeof(buf),&len_s,0,&addr)){ michael@0: if (r != R_WOULDBLOCK && (sock->type != NR_ICE_SOCKET_TYPE_DGRAM)) { michael@0: /* Report this error upward. Bug 946423 */ michael@0: r_log(LOG_ICE,LOG_ERR,"ICE(%s): Error on reliable socket. Abandoning.",sock->ctx->label); michael@0: NR_ASYNC_CANCEL(s, NR_ASYNC_WAIT_READ); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: /* Deal with the fact that sizeof(int) and sizeof(size_t) may not michael@0: be the same */ michael@0: if (len_s > (size_t)INT_MAX) michael@0: return; michael@0: michael@0: len = (int)len_s; michael@0: michael@0: #ifdef USE_TURN michael@0: re_process: michael@0: #endif /* USE_TURN */ michael@0: r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Read %d bytes %sfrom %s",sock->ctx->label,len,(processed_indication ? "relayed " : ""),addr.as_string); michael@0: michael@0: /* First question: is this STUN or not? */ michael@0: is_stun=nr_is_stun_message(buf,len); michael@0: michael@0: if(is_stun){ michael@0: is_req=nr_is_stun_request_message(buf,len); michael@0: is_ind=is_req?0:nr_is_stun_indication_message(buf,len); michael@0: michael@0: snprintf(string, sizeof(string)-1, "ICE(%s): Message is STUN (%s)",sock->ctx->label, michael@0: is_req ? "request" : (is_ind ? "indication" : "other")); michael@0: r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)buf, len); michael@0: michael@0: michael@0: /* We need to offer it to all of our stun contexts michael@0: to see who bites */ michael@0: sc1=TAILQ_FIRST(&sock->stun_ctxs); michael@0: while(sc1){ michael@0: sc2=TAILQ_NEXT(sc1,entry); michael@0: michael@0: r=-1; michael@0: switch(sc1->type){ michael@0: /* This has been deleted, prune... */ michael@0: case NR_ICE_STUN_NONE: michael@0: TAILQ_REMOVE(&sock->stun_ctxs,sc1,entry); michael@0: RFREE(sc1); michael@0: break; michael@0: michael@0: case NR_ICE_STUN_CLIENT: michael@0: if(!(is_req||is_ind)){ michael@0: r=nr_stun_client_process_response(sc1->u.client,buf,len,&addr); michael@0: } michael@0: break; michael@0: michael@0: case NR_ICE_STUN_SERVER: michael@0: if(is_req){ michael@0: r=nr_stun_server_process_request(sc1->u.server,stun_srv_sock,(char *)buf,len,&addr,NR_STUN_AUTH_RULE_SHORT_TERM); michael@0: } michael@0: break; michael@0: #ifdef USE_TURN michael@0: case NR_ICE_TURN_CLIENT: michael@0: /* data indications are ok, so don't ignore those */ michael@0: /* Check that this is from the right TURN server address. Else michael@0: skip */ michael@0: if (nr_transport_addr_cmp( michael@0: &sc1->u.turn_client.turn_client->turn_server_addr, michael@0: &addr, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) michael@0: break; michael@0: michael@0: if(!is_req){ michael@0: if(!is_ind) michael@0: r=nr_turn_client_process_response(sc1->u.turn_client.turn_client,buf,len,&addr); michael@0: else{ michael@0: nr_transport_addr n_addr; michael@0: size_t n_len; michael@0: michael@0: if (processed_indication) { michael@0: /* Don't allow recursively wrapped indications */ michael@0: r_log(LOG_ICE, LOG_WARNING, michael@0: "ICE(%s): discarding recursively wrapped indication", michael@0: sock->ctx->label); michael@0: break; michael@0: } michael@0: /* This is a bit of a hack. If it's a data indication, strip michael@0: off the TURN framing and re-enter. This works because michael@0: all STUN processing is on the same physical socket. michael@0: We don't care about other kinds of indication */ michael@0: r=nr_turn_client_parse_data_indication( michael@0: sc1->u.turn_client.turn_client, &addr, michael@0: buf, len, buf, &n_len, len, &n_addr); michael@0: if(!r){ michael@0: r_log(LOG_ICE,LOG_DEBUG,"Unwrapped a data indication."); michael@0: len=n_len; michael@0: nr_transport_addr_copy(&addr,&n_addr); michael@0: stun_srv_sock=sc1->u.turn_client.turn_sock; michael@0: processed_indication=1; michael@0: goto re_process; michael@0: } michael@0: } michael@0: } michael@0: break; michael@0: #endif /* USE_TURN */ michael@0: michael@0: default: michael@0: assert(0); /* Can't happen */ michael@0: return; michael@0: } michael@0: if(!r) { michael@0: break; michael@0: } michael@0: michael@0: sc1=sc2; michael@0: } michael@0: if(!sc1){ michael@0: if (nr_ice_ctx_is_known_id(sock->ctx,((nr_stun_message_header*)buf)->id.octet)) michael@0: r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Message is a retransmit",sock->ctx->label); michael@0: else michael@0: r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): Message does not correspond to any registered stun ctx",sock->ctx->label); michael@0: } michael@0: } michael@0: else{ michael@0: r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Message is not STUN",sock->ctx->label); michael@0: michael@0: nr_ice_ctx_deliver_packet(sock->ctx, sock->component, &addr, buf, len); michael@0: } michael@0: michael@0: return; michael@0: } michael@0: michael@0: int nr_ice_socket_create(nr_ice_ctx *ctx,nr_ice_component *comp, nr_socket *nsock, nr_ice_socket **sockp) michael@0: { michael@0: nr_ice_socket *sock=0; michael@0: NR_SOCKET fd; michael@0: nr_transport_addr addr; michael@0: int r,_status; michael@0: michael@0: if(!(sock=RCALLOC(sizeof(nr_ice_socket)))) michael@0: ABORT(R_NO_MEMORY); michael@0: michael@0: sock->sock=nsock; michael@0: sock->ctx=ctx; michael@0: sock->component=comp; michael@0: michael@0: if(r=nr_socket_getaddr(nsock, &addr)) michael@0: ABORT(r); michael@0: michael@0: if (addr.protocol == IPPROTO_UDP) { michael@0: sock->type = NR_ICE_SOCKET_TYPE_DGRAM; michael@0: } michael@0: else { michael@0: assert(addr.protocol == IPPROTO_TCP); michael@0: sock->type = NR_ICE_SOCKET_TYPE_STREAM; michael@0: } michael@0: michael@0: TAILQ_INIT(&sock->candidates); michael@0: TAILQ_INIT(&sock->stun_ctxs); michael@0: michael@0: if(r=nr_socket_getfd(nsock,&fd)) michael@0: ABORT(r); michael@0: michael@0: NR_ASYNC_WAIT(fd,NR_ASYNC_WAIT_READ,nr_ice_socket_readable_cb,sock); michael@0: michael@0: *sockp=sock; michael@0: michael@0: _status=0; michael@0: abort: michael@0: if(_status) RFREE(sock); michael@0: return(_status); michael@0: } michael@0: michael@0: michael@0: int nr_ice_socket_destroy(nr_ice_socket **isockp) michael@0: { michael@0: nr_ice_stun_ctx *s1,*s2; michael@0: nr_ice_socket *isock; michael@0: michael@0: if(!isockp || !*isockp) michael@0: return(0); michael@0: michael@0: isock=*isockp; michael@0: *isockp=0; michael@0: michael@0: /* Close the socket */ michael@0: nr_ice_socket_close(isock); michael@0: michael@0: /* The STUN server */ michael@0: nr_stun_server_ctx_destroy(&isock->stun_server); michael@0: michael@0: /* Now clean up the STUN ctxs */ michael@0: TAILQ_FOREACH_SAFE(s1, &isock->stun_ctxs, entry, s2){ michael@0: TAILQ_REMOVE(&isock->stun_ctxs, s1, entry); michael@0: RFREE(s1); michael@0: } michael@0: michael@0: RFREE(isock); michael@0: michael@0: return(0); michael@0: } michael@0: michael@0: int nr_ice_socket_close(nr_ice_socket *isock) michael@0: { michael@0: #ifdef NR_SOCKET_IS_VOID_PTR michael@0: NR_SOCKET fd=NULL; michael@0: NR_SOCKET no_socket = NULL; michael@0: #else michael@0: NR_SOCKET fd=-1; michael@0: NR_SOCKET no_socket = -1; michael@0: #endif michael@0: michael@0: if (!isock||!isock->sock) michael@0: return(0); michael@0: michael@0: nr_socket_getfd(isock->sock,&fd); michael@0: assert(isock->sock!=0); michael@0: if(fd != no_socket){ michael@0: NR_ASYNC_CANCEL(fd,NR_ASYNC_WAIT_READ); michael@0: NR_ASYNC_CANCEL(fd,NR_ASYNC_WAIT_WRITE); michael@0: nr_socket_destroy(&isock->sock); michael@0: } michael@0: michael@0: return(0); michael@0: } michael@0: michael@0: int nr_ice_socket_register_stun_client(nr_ice_socket *sock, nr_stun_client_ctx *srv,void **handle) michael@0: { michael@0: nr_ice_stun_ctx *sc=0; michael@0: int _status; michael@0: michael@0: if(!(sc=RCALLOC(sizeof(nr_ice_stun_ctx)))) michael@0: ABORT(R_NO_MEMORY); michael@0: michael@0: sc->type=NR_ICE_STUN_CLIENT; michael@0: sc->u.client=srv; michael@0: michael@0: TAILQ_INSERT_TAIL(&sock->stun_ctxs,sc,entry); michael@0: michael@0: *handle=sc; michael@0: michael@0: _status=0; michael@0: abort: michael@0: return(_status); michael@0: } michael@0: michael@0: int nr_ice_socket_register_stun_server(nr_ice_socket *sock, nr_stun_server_ctx *srv,void **handle) michael@0: { michael@0: nr_ice_stun_ctx *sc=0; michael@0: int _status; michael@0: michael@0: if(!(sc=RCALLOC(sizeof(nr_ice_stun_ctx)))) michael@0: ABORT(R_NO_MEMORY); michael@0: michael@0: sc->type=NR_ICE_STUN_SERVER; michael@0: sc->u.server=srv; michael@0: michael@0: TAILQ_INSERT_TAIL(&sock->stun_ctxs,sc,entry); michael@0: michael@0: *handle=sc; michael@0: michael@0: _status=0; michael@0: abort: michael@0: return(_status); michael@0: } michael@0: michael@0: int nr_ice_socket_register_turn_client(nr_ice_socket *sock, nr_turn_client_ctx *srv, michael@0: nr_socket *turn_socket, void **handle) michael@0: { michael@0: nr_ice_stun_ctx *sc=0; michael@0: int _status; michael@0: michael@0: if(!(sc=RCALLOC(sizeof(nr_ice_stun_ctx)))) michael@0: ABORT(R_NO_MEMORY); michael@0: michael@0: sc->type=NR_ICE_TURN_CLIENT; michael@0: sc->u.turn_client.turn_client=srv; michael@0: sc->u.turn_client.turn_sock=turn_socket; michael@0: michael@0: TAILQ_INSERT_TAIL(&sock->stun_ctxs,sc,entry); michael@0: michael@0: *handle=sc; michael@0: michael@0: _status=0; michael@0: abort: michael@0: return(_status); michael@0: } michael@0: michael@0: /* Just mark it deregistered. Don't delete it now because it's not safe michael@0: in the CB, which is where this is likely to be called */ michael@0: int nr_ice_socket_deregister(nr_ice_socket *sock, void *handle) michael@0: { michael@0: nr_ice_stun_ctx *sc=handle; michael@0: michael@0: if(!sc) michael@0: return(0); michael@0: michael@0: sc->type=NR_ICE_STUN_NONE; michael@0: michael@0: return(0); michael@0: } michael@0: