/** * Add TURN Permission for a peer * * @param turnc TURN Client * @param peer Peer IP-address * @param ph Permission handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int turnc_add_perm(struct turnc *turnc, const struct sa *peer, turnc_perm_h *ph, void *arg) { struct perm *perm; int err; if (!turnc || !peer) return EINVAL; if (perm_find(turnc, peer)) return 0; perm = mem_zalloc(sizeof(*perm), destructor); if (!perm) return ENOMEM; hash_append(turnc->perms, sa_hash(peer, SA_ADDR), &perm->he, perm); tmr_init(&perm->tmr); perm->peer = *peer; perm->turnc = turnc; perm->ph = ph; perm->arg = arg; err = createperm_request(perm, true); if (err) mem_deref(perm); return err; }
static bool raw_handler(int proto, const struct sa *src, const struct sa *dst, struct mbuf *mb) { struct allocation *al; uint16_t numb, len; struct perm *perm; struct chan *chan; int err; al = allocation_find(proto, src, dst); if (!al) return false; if (mbuf_get_left(mb) < 4) return false; numb = ntohs(mbuf_read_u16(mb)); len = ntohs(mbuf_read_u16(mb)); if (mbuf_get_left(mb) < len) return false; // strip any optional padding if (len != mbuf_get_left(mb)) mbuf_set_end(mb, mb->pos + len); chan = chan_numb_find(al->chans, numb); if (!chan) return false; perm = perm_find(al->perms, chan_peer(chan)); if (!perm) { ++al->dropc_tx; return false; } err = udp_send(al->rel_us, chan_peer(chan), mb); if (err) turnd.errc_tx++; else { const size_t bytes = mbuf_get_left(mb); perm_tx_stat(perm, bytes); turnd.bytec_tx += bytes; } return true; }
static bool indication_handler(struct restund_msgctx *ctx, int proto, void *sock, const struct sa *src, const struct sa *dst, const struct stun_msg *msg) { struct stun_attr *data, *peer; struct allocation *al; struct perm *perm; int err; (void)sock; (void)ctx; if (stun_msg_method(msg) != STUN_METHOD_SEND) return false; if (ctx->ua.typec > 0) return true; al = allocation_find(proto, src, dst); if (!al) return true; peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); data = stun_msg_attr(msg, STUN_ATTR_DATA); if (!peer || !data) return true; perm = perm_find(al->perms, &peer->v.xor_peer_addr); if (!perm) { ++al->dropc_tx; return true; } err = udp_send(al->rel_us, &peer->v.xor_peer_addr, &data->v.data); if (err) turnd.errc_tx++; else { const size_t bytes = mbuf_get_left(&data->v.data); perm_tx_stat(perm, bytes); turnd.bytec_tx += bytes; } return true; }
void chanbind_request(struct allocation *al, struct restund_msgctx *ctx, int proto, void *sock, const struct sa *src, const struct stun_msg *msg) { struct chan *chan = NULL, *ch_numb = NULL, *ch_peer; struct perm *perm = NULL, *permx = NULL; struct stun_attr *chnr, *peer; int err = ENOMEM, rerr; chnr = stun_msg_attr(msg, STUN_ATTR_CHANNEL_NUMBER); peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); if (!chnr || !chan_numb_valid(chnr->v.channel_number) || !peer) { restund_info("turn: bad chanbind attributes\n"); rerr = stun_ereply(proto, sock, src, 0, msg, 400, "Bad Attributes", ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); goto out; } if (sa_af(&peer->v.xor_peer_addr) != sa_af(&al->rel_addr)) { restund_info("turn: chanbind peer address family mismatch\n"); rerr = stun_ereply(proto, sock, src, 0, msg, 443, "Peer Address Family Mismatch", ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); goto out; } ch_numb = chan_numb_find(al->chans, chnr->v.channel_number); ch_peer = chan_peer_find(al->chans, &peer->v.xor_peer_addr); if (ch_numb != ch_peer) { restund_info("turn: channel %p/peer %p already bound\n", ch_numb, ch_peer); rerr = stun_ereply(proto, sock, src, 0, msg, 400, "Channel/Peer Already Bound", ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); goto out; } if (!ch_numb) { chan = chan_create(al->chans, chnr->v.channel_number, &peer->v.xor_peer_addr, al); if (!chan) { restund_info("turn: unable to create channel\n"); rerr = stun_ereply(proto, sock, src, 0, msg, 500, "Server Error", ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE,restund_software); goto out; } } permx = perm_find(al->perms, &peer->v.xor_peer_addr); if (!permx) { perm = perm_create(al->perms, &peer->v.xor_peer_addr, al); if (!perm) { restund_info("turn: unable to create permission\n"); rerr = stun_ereply(proto, sock, src, 0, msg, 500, "Server Error", ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE,restund_software); goto out; } } err = rerr = stun_reply(proto, sock, src, 0, msg, ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); out: if (rerr) restund_warning("turn: chanbind reply: %m\n", rerr); if (err) { mem_deref(chan); mem_deref(perm); } else { chan_refresh(ch_numb); perm_refresh(permx); } }