int ice_cand_attr_encode(struct re_printf *pf, const struct ice_cand_attr *cand) { int err = 0; if (!cand) return 0; err |= re_hprintf(pf, "%s %u %s %u %j %u typ %s", cand->foundation, cand->compid, net_proto2name(cand->proto), cand->prio, &cand->addr, sa_port(&cand->addr), ice_cand_type2name(cand->type)); if (sa_isset(&cand->rel_addr, SA_ADDR)) err |= re_hprintf(pf, " raddr %j", &cand->rel_addr); if (sa_isset(&cand->rel_addr, SA_PORT)) err |= re_hprintf(pf, " rport %u", sa_port(&cand->rel_addr)); if (cand->proto == IPPROTO_TCP) { err |= re_hprintf(pf, " tcptype %s", ice_tcptype_name(cand->tcptype)); } return err; }
static bool allocation_status(struct le *le, void *arg) { const uint32_t bsize = hash_bsize(turnd.ht_alloc); struct allocation *al = le->data; struct mbuf *mb = arg; (void)mbuf_printf(mb, "- %04u %s/%J/%J - %J \"%s\" %us (drop %llu/%llu)\n", sa_hash(&al->cli_addr, SA_ALL) & (bsize - 1), net_proto2name(al->proto), &al->cli_addr, &al->srv_addr, &al->rel_addr, al->username, (uint32_t)tmr_get_expire(&al->tmr) / 1000, al->dropc_tx, al->dropc_rx); perm_status(al->perms, mb); chan_status(al->chans, mb); return false; }
static void stun_mapped_handler(int err, const struct sa *map, void *arg) { struct candidate *cand = arg; struct ice_lcand *lcand=0, *base = cand->base; uint32_t prio; if (err) { re_printf("STUN Request failed: (%m)\n", err); goto out; } re_printf("adding SRFLX candidate %s.%J\n", net_proto2name(base->attr.proto), map); prio = calc_prio(ICE_CAND_TYPE_SRFLX, base->attr.proto, base->attr.tcptype, sa_af(&base->attr.addr), base->attr.proto); err = trice_lcand_add(&lcand, base->icem, base->attr.compid, base->attr.proto, prio, map, &base->attr.addr, ICE_CAND_TYPE_SRFLX, &base->attr.addr, base->attr.tcptype, NULL, 0); if (err) { re_fprintf(stderr, "failed to add SRFLX candidate (%m)\n", err); goto out; } err = control_send_message(cand->ag->cli, "a=candidate:%H\r\n", ice_cand_attr_encode, &lcand->attr); if (err) goto out; out: candidate_done(cand); }
static void turnc_handler(int err, uint16_t scode, const char *reason, const struct sa *relay_addr, const struct sa *mapped_addr, const struct stun_msg *msg, void *arg) { struct candidate *cand = arg; struct ice_lcand *lcand_relay=0, *lcand_srflx=0, *base = cand->base; uint32_t prio; if (err || scode) { re_printf("TURN client error: %u %s (%m)\n", scode, reason, err); goto out; } /* check if the relayed address is of the same Address Family * as the base candidate */ if (sa_af(relay_addr) != sa_af(&base->attr.addr)) { re_printf("could not use RELAY address (AF mismatch)\n"); goto out; } if (stun_msg_method(msg) == STUN_METHOD_ALLOCATE) { re_printf("TURN allocation okay (turn-proto=%s)\n", net_proto2name(cand->turn_proto)); cand->turn_ok = true; } /* RELAY */ re_printf("adding RELAY candidate %s.%J\n", net_proto2name(base->attr.proto), relay_addr); prio = calc_prio(ICE_CAND_TYPE_RELAY, base->attr.proto, base->attr.tcptype, sa_af(&base->attr.addr), cand->turn_proto); err = trice_lcand_add(&lcand_relay, base->icem, base->attr.compid, base->attr.proto, prio, relay_addr, relay_addr, ICE_CAND_TYPE_RELAY, mapped_addr, base->attr.tcptype, base->us, LAYER_ICE); if (err) { re_fprintf(stderr, "failed to add RELAY candidate (%m)\n", err); goto out; } if (cand->turn_proto == IPPROTO_TCP) { /* NOTE: this is needed to snap up outgoing UDP-packets */ err = udp_register_helper(&cand->uh_turntcp, lcand_relay->us, LAYER_TURN, turntcp_send_handler, NULL, cand); if (err) { re_printf("helper error\n"); goto out; } } /* SRFLX */ if (cand->turn_proto == base->attr.proto) { re_printf("adding SRFLX candidate %s.%J\n", net_proto2name(base->attr.proto), mapped_addr); prio = calc_prio(ICE_CAND_TYPE_SRFLX, base->attr.proto, base->attr.tcptype, sa_af(&base->attr.addr), base->attr.proto); err = trice_lcand_add(&lcand_srflx, base->icem, base->attr.compid, base->attr.proto, prio, mapped_addr, &base->attr.addr, ICE_CAND_TYPE_SRFLX, &base->attr.addr, base->attr.tcptype, NULL, LAYER_ICE); if (err) { re_fprintf(stderr, "failed to add SRFLX" " candidate (%m)\n", err); goto out; } } err = control_send_message(cand->ag->cli, "a=candidate:%H\r\n" "a=candidate:%H\r\n" , ice_cand_attr_encode, &lcand_relay->attr, ice_cand_attr_encode, lcand_srflx ? &lcand_srflx->attr : 0); if (err) goto out; candidate_add_permissions(cand); out: candidate_done(cand); }
static void process_msg(struct turnserver *turn, int proto, void *sock, const struct sa *src, struct mbuf *mb) { struct stun_msg *msg = NULL; struct sa laddr; int err = 0; if (stun_msg_decode(&msg, mb, NULL)) { uint16_t numb, len; struct channel *chan; if (!turn->us_relay) return; ++turn->n_raw; numb = ntohs(mbuf_read_u16(mb)); len = ntohs(mbuf_read_u16(mb)); if (mbuf_get_left(mb) < len) { DEBUG_WARNING("short length: %zu < %u\n", mbuf_get_left(mb), len); } chan = find_channel_numb(turn, numb); if (!chan) { DEBUG_WARNING("channel not found: numb=%u\n", numb); return; } /* relay data from channel to peer */ (void)udp_send(turn->us_relay, &chan->peer, mb); return; } #if 0 re_printf("process: %s:%p:%J %s\n", net_proto2name(proto), sock, src, stun_method_name(stun_msg_method(msg))); #endif switch (stun_msg_method(msg)) { case STUN_METHOD_ALLOCATE: /* Max 1 allocation for now */ ++turn->n_allocate; if (turn->us_relay) { err = EALREADY; goto out; } turn->cli = *src; err = sa_set_str(&laddr, "127.0.0.1", 0); if (err) goto out; err = udp_listen(&turn->us_relay, &laddr, relay_udp_recv, turn); if (err) goto out; err = udp_local_get(turn->us_relay, &turn->relay); if (err) goto out; udp_rxbuf_presz_set(turn->us_relay, 4); err = stun_reply(proto, sock, src, 0, msg, NULL, 0, false, 2, STUN_ATTR_XOR_MAPPED_ADDR, src, STUN_ATTR_XOR_RELAY_ADDR, &turn->relay); break; case STUN_METHOD_CREATEPERM: { struct stun_attr *peer; ++turn->n_createperm; peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); TEST_ASSERT(peer != NULL); add_permission(turn, &peer->v.xor_peer_addr); /* todo: install permissions and check them */ err = stun_reply(proto, sock, src, 0, msg, NULL, 0, false, 0); } break; case STUN_METHOD_CHANBIND: { struct stun_attr *chnr, *peer; ++turn->n_chanbind; TEST_ASSERT(turn->us_relay != NULL); chnr = stun_msg_attr(msg, STUN_ATTR_CHANNEL_NUMBER); peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); if (!chnr || !peer) { DEBUG_WARNING("CHANBIND: missing chnr/peer attrib\n"); } TEST_ASSERT(turn->chanc < ARRAY_SIZE(turn->chanv)); turn->chanv[turn->chanc].nr = chnr->v.channel_number; turn->chanv[turn->chanc].peer = peer->v.xor_peer_addr; ++turn->chanc; err = stun_reply(proto, sock, src, 0, msg, NULL, 0, false, 0); } break; case STUN_METHOD_SEND: { struct stun_attr *peer, *data; ++turn->n_send; TEST_ASSERT(turn->us_relay != NULL); peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); data = stun_msg_attr(msg, STUN_ATTR_DATA); if (!peer || !data) { DEBUG_WARNING("SEND: missing peer/data attrib\n"); goto out; } /* check for valid Permission */ if (!find_permission(turn, &peer->v.xor_peer_addr)) { DEBUG_NOTICE("no permission to peer %j\n", &peer->v.xor_peer_addr); goto out; } err = udp_send(turn->us_relay, &peer->v.xor_peer_addr, &data->v.data); } break; default: DEBUG_WARNING("unknown STUN method: %s\n", stun_method_name(stun_msg_method(msg))); err = EPROTO; break; } if (err) goto out; out: if (err && stun_msg_class(msg) == STUN_CLASS_REQUEST) { (void)stun_ereply(proto, sock, src, 0, msg, 500, "Server Error", NULL, 0, false, 0); } mem_deref(msg); }