/** * Compare local and remote candidates of two candidate pairs * * @param cp1 First Candidate pair * @param cp2 Second Candidate pair * * @return true if match */ bool icem_candpair_cmp(const struct candpair *cp1, const struct candpair *cp2) { if (!sa_cmp(&cp1->lcand->addr, &cp2->lcand->addr, SA_ALL)) return false; return sa_cmp(&cp1->rcand->addr, &cp2->rcand->addr, SA_ALL); }
static void tcp_estab_handler(void *arg) { struct ice_tcpconn *conn = arg; struct trice *icem = conn->icem; struct le *le; int err; conn->estab = true; trice_printf(icem, "TCP established (local=%J <---> peer=%J)\n", &conn->laddr, &conn->paddr); err = shim_insert(&conn->shim, conn->tc, conn->layer, shim_frame_handler, conn); if (err) goto out; if (!icem->checklist) goto out; /* check all pending CONNCHECKs for TCP */ le = icem->checklist->conncheckl.head; while (le) { struct ice_conncheck *cc = le->data; struct ice_candpair *pair = cc->pair; le = le->next; if (pair->state == ICE_CANDPAIR_INPROGRESS && pair->lcand->attr.compid == conn->compid && pair->lcand->attr.proto == IPPROTO_TCP && sa_cmp(&pair->lcand->attr.addr, &conn->laddr, SA_ADDR) && sa_cmp(&pair->rcand->attr.addr, &conn->paddr, SA_ALL)) { trice_printf(icem, " estab: sending pending check" " from %j to %J\n", &pair->lcand->attr.addr, &pair->rcand->attr.addr); /* todo: */ pair->conn = mem_ref(conn); err = trice_conncheck_stun_request(icem->checklist, cc, pair, conn->tc, cc->use_cand); if (err) { DEBUG_WARNING("stun_request error (%m)\n", err); } } } out: if (err) { DEBUG_WARNING("estab: errors (%m)\n", err); } }
static void *unique_handler(struct le *le1, struct le *le2) { struct candpair *cp1 = le1->data, *cp2 = le2->data; if (!sa_cmp(cand_srflx_addr(cp1->lcand), cand_srflx_addr(cp2->lcand), SA_ALL) || !sa_cmp(&cp1->rcand->addr, &cp2->rcand->addr, SA_ALL)) return NULL; return cp1->pprio < cp2->pprio ? cp1 : cp2; }
/* Incoming data from TURN-server */ static void data_handler(struct allocation *alloc, const struct sa *src, struct mbuf *mb) { int err; if (!alloc->ok) { re_fprintf(stderr, "allocation not ready" " -- ignore %zu bytes from %J\n", mbuf_get_left(mb), src); return; } if (!sa_cmp(src, &alloc->peer, SA_ALL)) { re_printf("updating peer address: %J --> %J\n", &alloc->peer, src); alloc->peer = *src; if (!alloc->turn_ind) turnc_add_chan(alloc->turnc, src, NULL, NULL); tmr_start(&alloc->tmr_ping, PING_INTERVAL, tmr_ping_handler, alloc); } err = receiver_recv(&alloc->recv, src, mb); if (err) { re_fprintf(stderr, "corrupt packet coming from %J (%m)\n", src, err); } }
static void udp_recv_client(const struct sa *src, struct mbuf *mb, void *arg) { struct udp_test *ut = arg; switch (ut->tindex++) { case 0: if (!mbuf_compare(mb, data0)) { ut->err = EBADMSG; break; } if (!sa_cmp(src, &ut->srv, SA_ALL)) { ut->err = EPROTO; break; } break; default: ut->err = ERANGE; break; } if (ut->tindex >= 1) re_cancel(); }
static bool hash_cmp_handler(struct le *le, void *arg) { const struct allocation *al = le->data; const struct tuple *tup = arg; if (!sa_cmp(&al->cli_addr, tup->cli_addr, SA_ALL)) return false; if (!sa_cmp(&al->srv_addr, tup->srv_addr, SA_ALL)) return false; if (al->proto != tup->proto) return false; return true; }
static bool tcpconn_cmp_handler(struct le *le, void *arg) { const struct tcpconn *tc = le->data; /* avoid trying this connection if dead */ if (!tc->conn) return false; return sa_cmp(&tc->srv, arg, SA_ALL); }
static void *unique_handler(struct le *le1, struct le *le2) { struct ice_cand *c1 = le1->data, *c2 = le2->data; if (c1->base != c2->base || !sa_cmp(&c1->addr, &c2->addr, SA_ALL)) return NULL; /* remove candidate with lower priority */ return c1->prio < c2->prio ? c1 : c2; }
static bool interface_find(const struct agent *ag, const struct sa *addr) { unsigned i; for (i=0; i<ag->interfacec; i++) { if (sa_cmp(addr, &ag->interfacev[i], SA_ADDR)) return true; } return false; }
static void turnc_handler(int err, uint16_t scode, const char *reason, const struct sa *relay, const struct sa *mapped, const struct stun_msg *msg, void *arg) { struct icem_comp *comp = arg; struct icem *icem = comp->icem; struct cand *lcand; (void)msg; --icem->nstun; /* TURN failed, so we destroy the client */ if (err || scode) { comp->turnc = mem_deref(comp->turnc); } if (err) { DEBUG_WARNING("{%s.%u} TURN Client error: %m\n", icem->name, comp->id, err); goto out; } if (scode) { DEBUG_WARNING("{%s.%u} TURN Client error: %u %s\n", icem->name, comp->id, scode, reason); err = send_binding_request(icem, comp); if (err) goto out; return; } lcand = icem_cand_find(&icem->lcandl, comp->id, NULL); if (!lcand) goto out; if (!sa_cmp(relay, &lcand->base->addr, SA_ALL)) { err = icem_lcand_add(icem, lcand->base, CAND_TYPE_RELAY, relay); } if (mapped) { err |= icem_lcand_add(icem, lcand->base, CAND_TYPE_SRFLX, mapped); } else { err |= send_binding_request(icem, comp); } out: call_gather_handler(err, icem, scode, reason); }
static bool udp_helper_recv(struct sa *src, struct mbuf *mb, void *arg) { struct udp_test *ut = arg; if (!sa_cmp(src, &ut->srv, SA_ALL)) ut->err = EPROTO; mb->end -= 4; if (!mbuf_compare(mb, data0)) ut->err = EBADMSG; return false; }
/* NOTE: laddr matching is SA_ADDR only */ struct ice_tcpconn *trice_conn_find(struct list *connl, unsigned compid, const struct sa *laddr, const struct sa *peer) { struct le *le; for (le = list_head(connl); le; le = le->next) { struct ice_tcpconn *conn = le->data; if (compid != conn->compid) continue; /* NOTE: only for established */ if (!conn->estab) continue; if (sa_cmp(laddr, &conn->laddr, SA_ADDR) && sa_cmp(peer, &conn->paddr, SA_ALL)) return conn; } return NULL; }
static bool recv_handler(struct sa *src, struct mbuf *mb, void *arg) { struct dtls_flow *flow = arg; uint8_t b; int r, n; if (mbuf_get_left(mb) < 1) return false; /* ignore non-DTLS packets */ b = mb->buf[mb->pos]; if (b < 20 || b > 63) return false; if (!sa_cmp(src, &flow->peer, SA_ALL)) return false; /* feed SSL data to the BIO */ r = BIO_write(flow->sbio_in, mbuf_buf(mb), (int)mbuf_get_left(mb)); if (r <= 0) return true; n = SSL_read(flow->ssl, mbuf_buf(mb), (int)mbuf_get_space(mb)); if (n <= 0) { ERR_clear_error(); } if (!flow->up && SSL_state(flow->ssl) == SSL_ST_OK) { struct key client_key, server_key; char profile[256]; int err; flow->up = true; err = get_srtp_key_info(flow, profile, sizeof(profile), &client_key, &server_key); if (err) { warning("dtls: SRTP key info: %m\n", err); return true; } flow->estabh(0, flow, profile, &client_key, &server_key, flow->arg); } return true; }
static bool if_getname_handler(const char *ifname, const struct sa *sa, void *arg) { struct ifentry *ife = arg; if (ife->af != sa_af(sa)) return false; if (0 == sa_cmp(sa, ife->ip, SA_ADDR)) { str_ncpy(ife->ifname, ifname, ife->sz); ife->found = true; return true; } return false; }
static struct channel *find_channel_peer(struct turnserver *tt, const struct sa *peer) { size_t i; if (!tt) return NULL; for (i=0; i<tt->chanc; i++) { if (sa_cmp(&tt->chanv[i].peer, peer, SA_ALL)) return &tt->chanv[i]; } return NULL; }
static struct sa *find_permission(struct turnserver *tt, const struct sa *peer) { size_t i; if (!tt) return NULL; for (i=0; i<tt->permc; i++) { if (sa_cmp(&tt->permv[i], peer, SA_ADDR)) return &tt->permv[i]; } return NULL; }
TEST(media, ice_cand_decode) { struct ice_cand_attr cand; struct sa addr; int err; err = ice_cand_attr_decode(&cand, "42 1 udp 2113937151 10.0.0.63 2004 typ host"); ASSERT_EQ(0, err); sa_set_str(&addr, "10.0.0.63", 2004); ASSERT_STREQ("42", cand.foundation); ASSERT_EQ(1, cand.compid); ASSERT_EQ(IPPROTO_UDP, cand.proto); ASSERT_EQ(2113937151, cand.prio); ASSERT_TRUE(sa_cmp(&addr, &cand.addr, SA_ALL)); ASSERT_EQ(ICE_CAND_TYPE_HOST, cand.type); }
struct cand *icem_cand_find(const struct list *lst, uint8_t compid, const struct sa *addr) { struct le *le; for (le = list_head(lst); le; le = le->next) { struct cand *cand = le->data; if (compid && cand->compid != compid) continue; if (addr && !sa_cmp(&cand->addr, addr, SA_ALL)) continue; return cand; } return NULL; }
/* todo: re-connect if estab and active (with a timer) */ static void tcp_close_handler(int err, void *arg) { struct ice_tcpconn *conn = arg; struct trice *icem = conn->icem; struct le *le; trice_printf(conn->icem, "TCP-connection [%J -> %J] closed (%m)\n", &conn->laddr, &conn->paddr, err); err = err ? err : ECONNRESET; /* note: helper must be closed before tc */ conn->shim = mem_deref(conn->shim); conn->tc = mem_deref(conn->tc); /* todo: iterate through conncheckl and cancel all checks * that are using this conn */ le = conn->icem->checkl.head; while (le) { struct ice_candpair *pair = le->data; le = le->next; if (pair->lcand->attr.compid == conn->compid && pair->lcand->attr.proto == IPPROTO_TCP && sa_cmp(&pair->rcand->attr.addr, &conn->paddr, SA_ALL)) { trice_candpair_failed(pair, err, 0); if (icem->checklist) { icem->checklist->failh(err, 0, pair, icem->checklist->arg); } } } mem_deref(conn); }
struct udp_sock *restund_udp_socket(struct sa *sa, const struct sa *orig, bool ch_ip, bool ch_port) { struct le *le = list_head(&lstnrl); while (le) { struct udp_lstnr *ul = le->data; le = le->next; if (ch_ip && sa_cmp(orig, &ul->bnd_addr, SA_PORT)) continue; if (ch_port && (sa_port(orig) == sa_port(&ul->bnd_addr))) continue; sa_cpy(sa, &ul->bnd_addr); return ul->us; } return NULL; }
static void stun_response_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct sip_udpconn *uc = arg; struct stun_attr *attr; (void)reason; if (err || scode) { err = err ? err : EPROTO; goto out; } attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!attr) { attr = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR); if (!attr) { err = EPROTO; goto out; } } if (!sa_isset(&uc->maddr, SA_ALL)) { uc->maddr = attr->v.sa; } else if (!sa_cmp(&uc->maddr, &attr->v.sa, SA_ALL)) { err = ENOTCONN; goto out; } out: if (err) { udpconn_close(uc, err); mem_deref(uc); } else { tmr_start(&uc->tmr_ka, sip_keepalive_wait(uc->ka_interval), udpconn_keepalive_handler, uc); } }
static struct sip_udpconn *udpconn_find(struct sip *sip, struct udp_sock *us, const struct sa *paddr) { struct le *le; le = list_head(hash_list(sip->ht_udpconn, sa_hash(paddr, SA_ALL))); for (; le; le = le->next) { struct sip_udpconn *uc = le->data; if (!sa_cmp(&uc->paddr, paddr, SA_ALL)) continue; if (uc->us != us) continue; return uc; } return NULL; }
static struct sip_conn *conn_find(struct sip *sip, const struct sa *paddr, bool secure) { struct le *le; le = list_head(hash_list(sip->ht_conn, sa_hash(paddr, SA_ALL))); for (; le; le = le->next) { struct sip_conn *conn = le->data; if (!secure != (conn->sc == NULL)) continue; if (!sa_cmp(&conn->paddr, paddr, SA_ALL)) continue; return conn; } return NULL; }
struct ice_lcand *trice_lcand_find(struct trice *icem, enum ice_cand_type type, unsigned compid, int proto, const struct sa *addr) { struct list *lst; struct le *le; if (!icem) return NULL; if (!proto) { DEBUG_WARNING("find_candidate: invalid args\n"); return NULL; } lst = &icem->lcandl; for (le = list_head(lst); le; le = le->next) { struct ice_cand_attr *cand = le->data; if (type != (enum ice_cand_type)-1 && type != cand->type) continue; if (compid && cand->compid != compid) continue; if (cand->proto != proto) continue; if (addr && !sa_cmp(&cand->addr, addr, SA_ALL)) continue; return (void *)cand; } return NULL; }
/** * Check if network address is part of SIP transports * * @param sip SIP stack instance * @param tp SIP transport * @param laddr Local network address to check * * @return True if part of SIP transports, otherwise false */ bool sip_transp_isladdr(const struct sip *sip, enum sip_transp tp, const struct sa *laddr) { struct le *le; if (!sip || !laddr) return false; for (le=sip->transpl.head; le; le=le->next) { const struct sip_transport *transp = le->data; if (tp != SIP_TRANSP_NONE && transp->tp != tp) continue; if (!sa_cmp(&transp->laddr, laddr, SA_ALL)) continue; return true; } return false; }
static bool udp_helper_send(int *err, struct sa *dst, struct mbuf *mb, void *arg) { struct udp_test *ut = arg; const size_t pos = mb->pos; if (!sa_cmp(dst, &ut->srv, SA_ALL)) { *err = EPROTO; return false; } if (!mbuf_compare(mb, data0)) { *err = EBADMSG; return false; } /* Append a fake protocol trailer */ mb->pos = mb->end; *err = mbuf_write_str(mb, "XXXX"); mb->pos = pos; return false; }
static bool hash_peer_cmp_handler(struct le *le, void *arg) { const struct chan *chan = le->data; return sa_cmp(&chan->peer, arg, SA_ALL); }
static bool udp_recv_handler(struct sa *src, struct mbuf *mb, void *arg) { struct stun_attr *peer, *data; struct stun_unknown_attr ua; struct turnc *turnc = arg; struct stun_msg *msg; bool hdld = true; if (!sa_cmp(&turnc->srv, src, SA_ALL) && !sa_cmp(&turnc->psrv, src, SA_ALL)) return false; if (stun_msg_decode(&msg, mb, &ua)) { struct chan_hdr hdr; struct chan *chan; if (turnc_chan_hdr_decode(&hdr, mb)) return true; if (mbuf_get_left(mb) < hdr.len) return true; chan = turnc_chan_find_numb(turnc, hdr.nr); if (!chan) return true; *src = *turnc_chan_peer(chan); return false; } switch (stun_msg_class(msg)) { case STUN_CLASS_INDICATION: if (ua.typec > 0) break; if (stun_msg_method(msg) != STUN_METHOD_DATA) break; peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); data = stun_msg_attr(msg, STUN_ATTR_DATA); if (!peer || !data) break; *src = peer->v.xor_peer_addr; mb->pos = data->v.data.pos; mb->end = data->v.data.end; hdld = false; break; case STUN_CLASS_ERROR_RESP: case STUN_CLASS_SUCCESS_RESP: (void)stun_ctrans_recv(turnc->stun, msg, &ua); break; default: break; } mem_deref(msg); return hdld; }
static bool hash_cmp_handler(struct le *le, void *arg) { const struct perm *perm = le->data; return sa_cmp(&perm->peer, arg, SA_ADDR); }
static void stun_response_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct nat_mapping *nm = arg; struct stun_attr *map, *other; if (err) { DEBUG_WARNING("stun_response_handler: (%m)\n", err); nm->mh(err, NAT_TYPE_UNKNOWN, nm->arg); return; } switch (scode) { case 0: other = stun_msg_attr(msg, STUN_ATTR_OTHER_ADDR); map = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!map) map = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR); if (!map || !other) { DEBUG_WARNING("missing attributes: %s %s\n", map ? "" : "MAPPED-ADDR", other ? "" : "OTHER-ADDR"); nm->mh(EPROTO, NAT_TYPE_UNKNOWN, nm->arg); return; } nm->map[nm->test_phase-1] = map->v.sa; break; default: DEBUG_WARNING("Binding Error Resp: %u %s\n", scode, reason); nm->mh(EPROTO, NAT_TYPE_UNKNOWN, nm->arg); return; } switch (nm->test_phase) { case 1: /* Test I completed */ if (sa_cmp(&nm->laddr, &nm->map[0], SA_ALL)) { nm->mh(0, NAT_TYPE_ENDP_INDEP, nm->arg); return; } /* Start Test II - the client sends a Binding Request to the alternate *address* */ ++nm->test_phase; sa_set_port(&other->v.other_addr, sa_port(&nm->srv)); sa_cpy(&nm->srv, &other->v.other_addr); err = mapping_send(nm); if (err) { DEBUG_WARNING("stunc_request_send: (%m)\n", err); nm->mh(err, NAT_TYPE_UNKNOWN, nm->arg); } break; case 2: /* Test II completed */ if (sa_cmp(&nm->map[0], &nm->map[1], SA_ALL)) { nm->mh(0, NAT_TYPE_ENDP_INDEP, nm->arg); return; } /* Start Test III - the client sends a Binding Request to the alternate address and port */ ++nm->test_phase; sa_set_port(&nm->srv, sa_port(&other->v.other_addr)); err = mapping_send(nm); if (err) { DEBUG_WARNING("stunc_request_send: (%m)\n", err); nm->mh(err, NAT_TYPE_UNKNOWN, nm->arg); } break; case 3: /* Test III completed */ if (sa_cmp(&nm->map[1], &nm->map[2], SA_ALL)) { nm->mh(0, NAT_TYPE_ADDR_DEP, nm->arg); } else { nm->mh(0, NAT_TYPE_ADDR_PORT_DEP, nm->arg); } ++nm->test_phase; break; default: DEBUG_WARNING("invalid test phase %d\n", nm->test_phase); nm->mh(EINVAL, NAT_TYPE_UNKNOWN, nm->arg); break; } }