/** * 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 nonce_validate(char *nonce, uint32_t now, const struct sa *src) { struct pl pl; uint32_t v; if (strlen(nonce) != NONCE_SIZE) { restund_info("auth: bad nonce length (%u)\n", strlen(nonce)); return false; } pl.p = nonce; pl.l = 8; v = pl_x32(&pl) ^ auth.rand_time; if (v + auth.nonce_expiry < now) { restund_debug("auth: nonce expired\n"); return false; } pl.p += 8; v = pl_x32(&pl) ^ auth.rand_addr; if (v != sa_hash(src, SA_ADDR)) { restund_info("auth: bad nonce src address (%j)\n", src); return false; } return true; }
int sip_keepalive_udp(struct sip_keepalive *ka, struct sip *sip, struct udp_sock *us, const struct sa *paddr, uint32_t interval) { struct sip_udpconn *uc; if (!ka || !sip || !us || !paddr) return EINVAL; uc = udpconn_find(sip, us, paddr); if (!uc) { uc = mem_zalloc(sizeof(*uc), destructor); if (!uc) return ENOMEM; hash_append(sip->ht_udpconn, sa_hash(paddr, SA_ALL), &uc->he, uc); uc->paddr = *paddr; uc->stun = mem_ref(sip->stun); uc->us = mem_ref(us); uc->ka_interval = interval ? interval : UDP_KEEPALIVE_INTVAL; /* learn mapped address immediately */ tmr_start(&uc->tmr_ka, 0, udpconn_keepalive_handler, uc); } list_append(&uc->kal, &ka->le, ka); return 0; }
static struct chan *chan_create(struct chanlist *cl, uint16_t numb, const struct sa *peer, const struct allocation *al) { struct chan *chan; if (!cl || !peer) return NULL; chan = mem_zalloc(sizeof(*chan), destructor); if (!chan) return NULL; hash_append(cl->ht_numb, numb, &chan->he_numb, chan); hash_append(cl->ht_peer, sa_hash(peer, SA_ALL), &chan->he_peer, chan); chan->peer = *peer; chan->numb = numb; chan->al = al; chan->expires = time(NULL) + CHAN_LIFETIME; restund_debug("turn: allocation %p channel 0x%x %J created\n", chan->al, chan->numb, &chan->peer); return chan; }
static const char *mknonce(char *nonce, uint32_t now, const struct sa *src) { (void)re_snprintf(nonce, NONCE_SIZE + 1, "%08x%08x", auth.rand_time ^ now, auth.rand_addr ^ sa_hash(src, SA_ADDR)); return nonce; }
struct tls_conn *tls_udp_conn(const struct tls_sock *ts, const struct sa *peer) { if (!ts) return NULL; return list_ledata(hash_lookup(ts->ht_conn, sa_hash(peer, SA_ALL), hash_cmp_handler, (void *)peer)); }
/** Foundation is a hash of IP address and candidate type */ static int compute_foundation(struct cand *cand) { uint32_t v; v = sa_hash(&cand->addr, SA_ADDR); v ^= cand->type; return re_sdprintf(&cand->foundation, "%08x", v); }
static struct allocation *allocation_find(int proto, const struct sa *src, const struct sa *dst) { struct tuple tup; tup.cli_addr = src; tup.srv_addr = dst; tup.proto = proto; return list_ledata(hash_lookup(turnd.ht_alloc, sa_hash(src, SA_ALL), hash_cmp_handler, &tup)); }
/** Foundation is a hash of IP address and candidate type */ static int compute_foundation(struct ice_lcand *cand, const struct sa *addr, enum ice_cand_type type) { uint32_t v; v = sa_hash(addr, SA_ADDR); v ^= type; if (re_snprintf(cand->attr.foundation, sizeof(cand->attr.foundation), "%08x", v) < 0) return ENOMEM; return 0; }
static const char *mknonce(char *nonce, time_t now, const struct sa *src) { uint8_t key[MD5_SIZE]; uint64_t nv[3]; nv[0] = now; nv[1] = auth.secret; nv[2] = sa_hash(src, SA_ADDR); md5((uint8_t *)nv, sizeof(nv), key); (void)re_snprintf(nonce, NONCE_MAX_SIZE + 1, "%w%llx", key, sizeof(key), nv[0]); return nonce; }
static int send_tcp(struct dns_query *q) { const struct sa *srv; struct tcpconn *tc; int err = 0; if (!q) return EINVAL; while (q->ntx < *q->srvc) { srv = &q->srvv[q->ntx++]; DEBUG_NOTICE("trying tcp server#%u: %J\n", q->ntx-1, srv); tc = list_ledata(hash_lookup(q->dnsc->ht_tcpconn, sa_hash(srv, SA_ALL), tcpconn_cmp_handler, (void *)srv)); if (!tc) { err = tcpconn_alloc(&tc, q->dnsc, srv); if (err) continue; } if (tc->connected) { q->mb.pos = 0; err = tcp_send(tc->conn, &q->mb); if (err) { tcpconn_close(tc, err); continue; } tmr_start(&tc->tmr, tc->dnsc->conf.idle_timeout, tcpconn_timeout_handler, tc); DEBUG_NOTICE("tcp send %J\n", srv); } list_append(&tc->ql, &q->le_tc, q); q->tc = mem_ref(tc); break; } return err; }
static void tcp_connect_handler(const struct sa *paddr, void *arg) { struct sip_transport *transp = arg; struct sip_conn *conn; int err; conn = mem_zalloc(sizeof(*conn), conn_destructor); if (!conn) { err = ENOMEM; goto out; } hash_append(transp->sip->ht_conn, sa_hash(paddr, SA_ALL), &conn->he, conn); conn->paddr = *paddr; conn->sip = transp->sip; err = tcp_accept(&conn->tc, transp->sock, tcp_estab_handler, tcp_recv_handler, tcp_close_handler, conn); if (err) goto out; err = tcp_conn_local_get(conn->tc, &conn->laddr); if (err) goto out; #ifdef USE_TLS if (transp->tls) { err = tls_start_tcp(&conn->sc, transp->tls, conn->tc, 0); if (err) goto out; } #endif tmr_start(&conn->tmr, TCP_ACCEPT_TIMEOUT * 1000, conn_tmr_handler, conn); out: if (err) { tcp_reject(transp->sock); mem_deref(conn); } }
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 bool nonce_validate(char *nonce, time_t now, const struct sa *src) { uint8_t nkey[MD5_SIZE], ckey[MD5_SIZE]; uint64_t nv[3]; struct pl pl; int64_t age; unsigned i; pl.p = nonce; pl.l = str_len(nonce); if (pl.l < NONCE_MIN_SIZE || pl.l > NONCE_MAX_SIZE) { restund_info("auth: bad nonce length (%zu)\n", pl.l); return false; } for (i=0; i<sizeof(nkey); i++) { nkey[i] = ch_hex(*pl.p++) << 4; nkey[i] += ch_hex(*pl.p++); pl.l -= 2; } nv[0] = pl_x64(&pl); nv[1] = auth.secret; nv[2] = sa_hash(src, SA_ADDR); md5((uint8_t *)nv, sizeof(nv), ckey); if (memcmp(nkey, ckey, MD5_SIZE)) { restund_debug("auth: invalid nonce (%j)\n", src); return false; } age = now - nv[0]; if (age < 0 || age > auth.nonce_expiry) { restund_debug("auth: nonce expired, age: %lli secs\n", age); return false; } return true; }
struct chan *chan_peer_find(const struct chanlist *cl, const struct sa *peer) { struct chan *chan; if (!cl || !peer) return NULL; chan = list_ledata(hash_lookup(cl->ht_peer, sa_hash(peer, SA_ALL), hash_peer_cmp_handler, (void *)peer)); if (!chan) return NULL; if (chan->expires < time(NULL)) { restund_debug("turn: allocation %p channel 0x%x %J expired\n", chan->al, chan->numb, &chan->peer); mem_deref(chan); return NULL; } return chan; }
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; }
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 tls_conn *conn_alloc(struct tls_sock *ts, const struct sa *peer) { struct tls_conn *tc; tc = mem_zalloc(sizeof(*tc), conn_destructor); if (!tc) return NULL; tc->ssl = SSL_new(ts->tls->ctx); if (!tc->ssl) goto error; tc->sbio_in = BIO_new(BIO_s_mem()); if (!tc->sbio_in) goto error; tc->sbio_out = BIO_new(&bio_udp_send); if (!tc->sbio_out) { BIO_free(tc->sbio_in); goto error; } tc->sbio_out->ptr = tc; SSL_set_bio(tc->ssl, tc->sbio_in, tc->sbio_out); tmr_init(&tc->tmr); tc->peer = *peer; tc->ts = ts; hash_append(ts->ht_conn, sa_hash(peer, SA_ALL), &tc->he, tc); return tc; error: return mem_deref(tc); }
static int tcpconn_alloc(struct tcpconn **tcpp, struct dnsc *dnsc, const struct sa *srv) { struct tcpconn *tc; int err = ENOMEM; if (!tcpp || !dnsc || !srv) return EINVAL; tc = mem_zalloc(sizeof(struct tcpconn), tcpconn_destructor); if (!tc) goto out; hash_append(dnsc->ht_tcpconn, sa_hash(srv, SA_ALL), &tc->le, tc); tc->srv = *srv; tc->dnsc = dnsc; tc->mb = mbuf_alloc(1500); if (!tc->mb) goto out; err = tcp_connect(&tc->conn, srv, tcp_estab_handler, tcp_recv_handler, tcp_close_handler, tc); if (err) goto out; tmr_start(&tc->tmr, tc->dnsc->conf.conn_timeout, tcpconn_timeout_handler, tc); out: if (err) mem_deref(tc); else *tcpp = tc; return err; }
static int conn_send(struct sip_connqent **qentp, struct sip *sip, bool secure, const struct sa *dst, struct mbuf *mb, sip_transp_h *transph, void *arg) { struct sip_conn *conn, *new_conn = NULL; struct sip_connqent *qent; int err = 0; conn = conn_find(sip, dst, secure); if (conn) { if (!conn->established) goto enqueue; return tcp_send(conn->tc, mb); } new_conn = conn = mem_zalloc(sizeof(*conn), conn_destructor); if (!conn) return ENOMEM; hash_append(sip->ht_conn, sa_hash(dst, SA_ALL), &conn->he, conn); conn->paddr = *dst; conn->sip = sip; err = tcp_connect(&conn->tc, dst, tcp_estab_handler, tcp_recv_handler, tcp_close_handler, conn); if (err) goto out; err = tcp_conn_local_get(conn->tc, &conn->laddr); if (err) goto out; #ifdef USE_TLS if (secure) { const struct sip_transport *transp; transp = transp_find(sip, SIP_TRANSP_TLS, sa_af(dst), dst); if (!transp || !transp->tls) { err = EPROTONOSUPPORT; goto out; } err = tls_start_tcp(&conn->sc, transp->tls, conn->tc, 0); if (err) goto out; } #endif tmr_start(&conn->tmr, TCP_IDLE_TIMEOUT * 1000, conn_tmr_handler, conn); enqueue: qent = mem_zalloc(sizeof(*qent), qent_destructor); if (!qent) { err = ENOMEM; goto out; } list_append(&conn->ql, &qent->le, qent); qent->mb = mem_ref(mb); qent->transph = transph ? transph : internal_transport_handler; qent->arg = arg; if (qentp) { qent->qentp = qentp; *qentp = qent; } out: if (err) mem_deref(new_conn); return err; }
static struct perm *perm_find(const struct turnc *turnc, const struct sa *peer) { return list_ledata(hash_lookup(turnc->perms, sa_hash(peer, SA_ADDR), hash_cmp_handler, (void *)peer)); }