/* add TURN permission to all known remotes */ static int candidate_add_permissions(struct candidate *candidate) { struct le *le; struct trice *icem; int err = 0; if (candidate->type != TYPE_TURN || !candidate->turn_ok) return 0; icem = candidate->ag->icem; for (le = list_head(trice_rcandl(icem)); le; le = le->next) { struct ice_rcand *rcand = le->data; struct ice_lcand *base = candidate->base; if (rcand->attr.proto == IPPROTO_UDP && rcand->attr.compid == base->attr.compid && sa_af(&rcand->attr.addr) == sa_af(&base->attr.addr)) { err |= turnc_add_perm(candidate->turnc, &rcand->attr.addr, turnc_perm_handler, rcand); } } return err; }
/** * Forming Candidate Pairs */ static int candpairs_form(struct icem *icem) { struct le *le; int err = 0; if (list_isempty(&icem->lcandl)) return ENOENT; for (le = icem->lcandl.head; le; le = le->next) { struct cand *lcand = le->data; struct le *rle; for (rle = icem->rcandl.head; rle; rle = rle->next) { struct cand *rcand = rle->data; if (lcand->compid != rcand->compid) continue; if (sa_af(&lcand->addr) != sa_af(&rcand->addr)) continue; err |= icem_candpair_alloc(NULL, icem, lcand, rcand); } } return err; }
static int add_candidate(struct agent *ag, const struct sa *addr, int proto, enum ice_tcptype tcptype, const char *ifname) { struct ice_lcand *lcand; uint32_t prio; int err; prio = calc_prio(ICE_CAND_TYPE_HOST, proto, tcptype, sa_af(addr), proto); err = trice_lcand_add(&lcand, ag->icem, COMPID, proto, prio, addr, NULL, ICE_CAND_TYPE_HOST, NULL, tcptype, NULL, LAYER_ICE); if (err) { re_fprintf(stderr, "failed to add local candidate (%m)\n", err); return err; } str_ncpy(lcand->ifname, ifname, sizeof(lcand->ifname)); err = control_send_message(ag->cli, "a=candidate:%H\r\n", ice_cand_attr_encode, &lcand->attr); if (err) return err; /* gather SRFLX candidates */ if (ag->cli->param.stun_server) { if (proto == IPPROTO_UDP || (proto == IPPROTO_TCP && tcptype != ICE_TCP_ACTIVE)) { gather_srflx(ag, lcand, proto); } } /* gather RELAY candidates, NOTE IPv4 only */ if (ag->cli->param.turn_server) { if (proto == IPPROTO_UDP && sa_af(addr)==AF_INET) { gather_relay(ag, lcand, IPPROTO_UDP); gather_relay(ag, lcand, IPPROTO_TCP); } } return err; }
static int stream_sock_alloc(struct stream *s, int af) { struct sa laddr; int tos, err; if (!s) return EINVAL; /* we listen on all interfaces */ sa_init(&laddr, sa_af(net_laddr_af(af))); err = rtp_listen(&s->rtp, IPPROTO_UDP, &laddr, config.avt.rtp_ports.min, config.avt.rtp_ports.max, s->rtcp, rtp_recv, rtcp_handler, s); if (err) return err; tos = config.avt.rtp_tos; (void)udp_setsockopt(rtp_sock(s->rtp), IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); (void)udp_setsockopt(rtcp_sock(s->rtp), IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); udp_rxsz_set(rtp_sock(s->rtp), RTP_RECV_SIZE); return 0; }
static void lstnr_handler(struct sa *bnd_addr, struct udp_sock *us, void *arg) { const char *maddr; struct sa dst; int err; (void)arg; switch (sa_af(bnd_addr)) { case AF_INET: maddr = "224.0.0.1"; break; case AF_INET6: maddr = "ff02::1"; break; default: return; } sa_set_str(&dst, maddr, PCP_PORT_CLI); err = pcp_reply(us, &dst, NULL, PCP_ANNOUNCE, PCP_SUCCESS, 0, repcpd_epoch_time(), NULL); if (err) { warning("announce: failed to send ANNOUNCE from" " %J to %s (%m)\n", bnd_addr, maddr, err); return; } info("announce: sent ANNOUNCE from %J to %s\n", bnd_addr, maddr); }
/** * Get the Link-local address for a specific network interface * * @param ifname Name of the interface * @param af Address family * @param ip Returned link-local address * * @return 0 if success, otherwise errorcode */ int net_if_getlinklocal(const char *ifname, int af, struct sa *ip) { struct sa addr; void *argv[3]; int err; if (!ip) return EINVAL; sa_init(&addr, sa_af(ip)); argv[0] = (void *)ifname; argv[1] = ⁡ argv[2] = &addr; err = net_if_apply(linklocal_handler, argv); if (err) return err; if (!sa_isset(&addr, SA_ADDR)) return ENOENT; *ip = addr; return 0; }
static bool if_getaddr_handler(const char *ifname, const struct sa *sa, void *arg) { struct ifentry *ife = arg; /* Match name of interface? */ if (str_isset(ife->ifname) && 0 != str_casecmp(ife->ifname, ifname)) return false; if (!sa_isset(sa, SA_ADDR)) return false; #if 1 /* skip loopback and link-local IP */ if (sa_is_loopback(sa) || sa_is_linklocal(sa)) return false; #endif /* Match address family */ if (ife->af != sa_af(sa)) return false; /* Match - copy address */ sa_cpy(ife->ip, sa); ife->found = true; return ife->found; }
static int media_alloc(struct mnat_media **mp, struct mnat_sess *sess, int proto, void *sock1, void *sock2, struct sdp_media *sdpm) { struct mnat_media *m; struct sa laddr; struct pcp_map map; unsigned i; int err = 0; if (!mp || !sess || !sdpm || proto != IPPROTO_UDP) return EINVAL; m = mem_zalloc(sizeof(*m), media_destructor); if (!m) return ENOMEM; m->compc = sock2 ? 2 : 1; list_append(&sess->medial, &m->le, m); m->sess = sess; m->sdpm = mem_ref(sdpm); for (i=0; i<m->compc; i++) { struct comp *comp = &m->compv[i]; comp->id = i+1; comp->media = m; err = udp_local_get(i==0 ? sock1 : sock2, &laddr); if (err) goto out; rand_bytes(map.nonce, sizeof(map.nonce)); map.proto = proto; map.int_port = sa_port(&laddr); /* note: using same address-family as the PCP server */ sa_init(&map.ext_addr, sa_af(&pcp_srv)); info("pcp: %s: internal port for %s is %u\n", sdp_media_name(sdpm), i==0 ? "RTP" : "RTCP", map.int_port); err = pcp_request(&comp->pcp, NULL, &pcp_srv, PCP_MAP, LIFETIME, &map, pcp_resp_handler, comp, 0); if (err) goto out; } out: if (err) mem_deref(m); else if (mp) { *mp = m; } return err; }
int allocation_create(struct allocator *allocator, unsigned ix, int proto, const struct sa *srv, const char *username, const char *password, struct tls *tls, bool turn_ind, allocation_h *alloch, void *arg) { struct allocation *alloc; struct sa laddr; int err; if (!allocator || !proto || !srv) return EINVAL; sa_init(&laddr, sa_af(srv)); alloc = mem_zalloc(sizeof(*alloc), destructor); if (!alloc) return ENOMEM; list_append(&allocator->allocl, &alloc->le, alloc); (void)gettimeofday(&alloc->sent, NULL); alloc->atime = -1; alloc->ix = ix; alloc->allocator = allocator; alloc->proto = proto; alloc->secure = tls != NULL; alloc->srv = *srv; alloc->user = username; alloc->pass = password; alloc->turn_ind = turn_ind; alloc->alloch = alloch; alloc->arg = arg; alloc->tls = mem_ref(tls); receiver_init(&alloc->recv, allocator->session_cookie, alloc->ix); err = udp_listen(&alloc->us_tx, &laddr, NULL, NULL); if (err) { re_fprintf(stderr, "allocation: failed to create UDP tx socket" " (%m)\n", err); goto out; } udp_local_get(alloc->us_tx, &alloc->laddr_tx); err = start(alloc); if (err) goto out; out: if (err) mem_deref(alloc); return err; }
static bool interface_handler(const char *ifname, const struct sa *addr, void *arg) { struct agent *ag = arg; int err = 0; /* Skip loopback and link-local addresses */ if (ag->cli->param.skip_local) { if (sa_is_loopback(addr) || sa_is_linklocal(addr)) return false; } if (str_isset(ag->cli->param.ifname) && str_casecmp(ag->cli->param.ifname, ifname)) { return false; } switch (sa_af(addr)) { case AF_INET: if (!ag->cli->param.use_ipv4) return false; break; case AF_INET6: if (!ag->cli->param.use_ipv6) return false; break; } /* NOTE: on some machines an interface is listed twice. */ if (interface_find(ag, addr)) { re_printf("ignoring duplicated interface (%s %j)\n", ifname, addr); return false; } ag->interfacev[ag->interfacec++] = *addr; re_printf("interface: %s %j\n", ifname, addr); if (ag->cli->param.use_udp) err |= add_candidate(ag, addr, IPPROTO_UDP, 0, ifname); if (ag->cli->param.use_tcp) { err |= add_candidate(ag, addr, IPPROTO_TCP, ICE_TCP_SO, ifname); err |= add_candidate(ag, addr, IPPROTO_TCP, ag->client ? ICE_TCP_ACTIVE : ICE_TCP_PASSIVE, ifname); } return err != 0; }
static int udp_send_internal(struct udp_sock *us, const struct sa *dst, struct mbuf *mb, struct le *le) { struct sa hdst; int err = 0, fd; /* check for error in e.g. connected state */ if (us->err) { err = us->err; us->err = 0; /* clear error */ return err; } /* choose a socket */ if (AF_INET6 == sa_af(dst) && -1 != us->fd6) fd = us->fd6; else fd = us->fd; /* call helpers in reverse order */ while (le) { struct udp_helper *uh = le->data; le = le->prev; if (dst != &hdst) { sa_cpy(&hdst, dst); dst = &hdst; } if (uh->sendh(&err, &hdst, mb, uh->arg) || err) return err; } /* Connected socket? */ if (us->conn) { if (0 != connect(fd, &dst->u.sa, dst->len)) { DEBUG_WARNING("send: connect: %s\n", strerror(errno)); us->conn = false; } if (send(fd, BUF_CAST mb->buf + mb->pos, mb->end - mb->pos, 0) < 0) return errno; } else { if (sendto(fd, BUF_CAST mb->buf + mb->pos, mb->end - mb->pos, 0, &dst->u.sa, dst->len) < 0) return errno; } return 0; }
/** * Forming Candidate Pairs */ static int candpairs_form(struct icem *icem) { struct le *le; int err = 0; if (list_isempty(&icem->lcandl)) return ENOENT; if (list_isempty(&icem->rcandl)) { DEBUG_WARNING("%s: no remote candidates\n", icem->name); return ENOENT; } for (le = icem->lcandl.head; le; le = le->next) { struct cand *lcand = le->data; struct le *rle; for (rle = icem->rcandl.head; rle; rle = rle->next) { struct cand *rcand = rle->data; if (lcand->compid != rcand->compid) continue; if (sa_af(&lcand->addr) != sa_af(&rcand->addr)) continue; err = icem_candpair_alloc(NULL, icem, lcand, rcand); if (err) return err; } } return err; }
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; }
int sip_transp_laddr(struct sip *sip, struct sa *laddr, enum sip_transp tp, const struct sa *dst) { const struct sip_transport *transp; if (!sip || !laddr) return EINVAL; transp = transp_find(sip, tp, sa_af(dst), dst); if (!transp) return EPROTONOSUPPORT; *laddr = transp->laddr; return 0; }
int sip_transp_send(struct sip_connqent **qentp, struct sip *sip, void *sock, enum sip_transp tp, const struct sa *dst, struct mbuf *mb, sip_transp_h *transph, void *arg) { const struct sip_transport *transp; struct sip_conn *conn; bool secure = false; int err; if (!sip || !dst || !mb) return EINVAL; switch (tp) { case SIP_TRANSP_UDP: if (!sock) { transp = transp_find(sip, tp, sa_af(dst), dst); if (!transp) return EPROTONOSUPPORT; sock = transp->sock; } err = udp_send(sock, dst, mb); break; case SIP_TRANSP_TLS: secure = true; /*@fallthrough@*/ case SIP_TRANSP_TCP: conn = sock; if (conn && conn->tc) err = tcp_send(conn->tc, mb); else err = conn_send(qentp, sip, secure, dst, mb, transph, arg); break; default: err = EPROTONOSUPPORT; break; } return err; }
static int multicast_update(struct udp_sock *us, const struct sa *group, bool join) { struct ip_mreq mreq; #ifdef HAVE_INET6 struct ipv6_mreq mreq6; #endif int err; if (!us || !group) return EINVAL; switch (sa_af(group)) { case AF_INET: mreq.imr_multiaddr = group->u.in.sin_addr; mreq.imr_interface.s_addr = 0; err = udp_setsockopt(us, IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); break; #ifdef HAVE_INET6 case AF_INET6: mreq6.ipv6mr_multiaddr = group->u.in6.sin6_addr; mreq6.ipv6mr_interface = 0; err = udp_setsockopt(us, IPPROTO_IPV6, join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP, &mreq6, sizeof(mreq6)); break; #endif default: return EAFNOSUPPORT; } return err; }
static bool net_rt_handler(const char *ifname, const struct sa *dst, int dstlen, const struct sa *gw, void *arg) { (void)dstlen; (void)arg; if (sa_af(dst) != AF_INET) return false; if (sa_in(dst) == 0) { natpmp_srv = *gw; sa_set_port(&natpmp_srv, NATPMP_PORT); info("natpmp: found default gateway %j on interface '%s'\n", gw, ifname); return true; } return false; }
static inline size_t stun_indlen(const struct sa *sa) { size_t len = STUN_HEADER_SIZE + STUN_ATTR_HEADER_SIZE * 2; switch (sa_af(sa)) { case AF_INET: len += STUN_ATTR_ADDR4_SIZE; break; #ifdef HAVE_INET6 case AF_INET6: len += STUN_ATTR_ADDR6_SIZE; break; #endif } return len; }
static bool linklocal_handler(const char *ifname, const struct sa *sa, void *arg) { void **argv = arg; int af = *(int *)argv[1]; if (argv[0] && 0 != str_casecmp(argv[0], ifname)) return false; if (af != AF_UNSPEC && af != sa_af(sa)) return false; if (sa_is_linklocal(sa)) { *((struct sa *)argv[2]) = *sa; return true; } return false; }
static int gather_relay(struct agent *ag, struct ice_lcand *base, int turn_proto) { struct candidate *cand = &ag->candv[ag->candc++]; const char *proto_name = ""; int err; if (ag->candc >= ARRAY_SIZE(ag->candv)) return ENOMEM; cand->ag = ag; cand->base = base; cand->type = TYPE_TURN; cand->turn_proto = turn_proto; switch (turn_proto) { case IPPROTO_UDP: proto_name = stun_proto_udp; break; case IPPROTO_TCP: proto_name = stun_proto_tcp; break; } re_printf("resolving TURN-server '%s' for %s..\n", ag->cli->param.turn_server, proto_name); err = stun_server_discover(&cand->stun_dns, cand->ag->cli->dnsc, stun_usage_relay, proto_name, sa_af(&base->attr.addr), ag->cli->param.turn_server, 0, stun_dns_handler, cand); if (err) { re_fprintf(stderr, "stun_server_discover failed (%m)\n", err); return err; } return 0; }
static const struct sip_transport *transp_find(struct sip *sip, enum sip_transp tp, int af, const struct sa *dst) { struct le *le; (void)dst; for (le = sip->transpl.head; le; le = le->next) { const struct sip_transport *transp = le->data; if (transp->tp != tp) continue; if (af != AF_UNSPEC && sa_af(&transp->laddr) != af) continue; return transp; } return NULL; }
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); }
struct ice_lcand *trice_lcand_find2(const struct trice *icem, enum ice_cand_type type, int af) { struct le *le; if (!icem) return NULL; for (le = list_head(&icem->lcandl); le; le = le->next) { struct ice_cand_attr *cand = le->data; if (cand->type != type) continue; if (af != sa_af(&cand->addr)) continue; return (void *)cand; } return NULL; }
int call_accept(struct call *call, struct sipsess_sock *sess_sock, const struct sip_msg *msg) { bool got_offer; int err; if (!call || !msg) return EINVAL; call->outgoing = false; got_offer = (mbuf_get_left(msg->mb) > 0); err = pl_strdup(&call->peer_uri, &msg->from.auri); if (err) return err; if (pl_isset(&msg->from.dname)) { err = pl_strdup(&call->peer_name, &msg->from.dname); if (err) return err; } if (got_offer) { struct sdp_media *m; const struct sa *raddr; err = sdp_decode(call->sdp, msg->mb, true); if (err) return err; call->got_offer = true; /* * Each media description in the SDP answer MUST * use the same network type as the corresponding * media description in the offer. * * See RFC 6157 */ m = stream_sdpmedia(audio_strm(call->audio)); raddr = sdp_media_raddr(m); if (sa_af(raddr) != call->af) { info("call: incompatible address-family" " (local=%s, remote=%s)\n", net_af2name(call->af), net_af2name(sa_af(raddr))); sip_treply(NULL, uag_sip(), msg, 488, "Not Acceptable Here"); call_event_handler(call, CALL_EVENT_CLOSED, "Wrong address family"); return 0; } /* Check if we have any common audio codecs, after * the SDP offer has been parsed */ if (!have_common_audio_codecs(call)) { info("call: no common audio codecs - rejected\n"); sip_treply(NULL, uag_sip(), msg, 488, "Not Acceptable Here"); call_event_handler(call, CALL_EVENT_CLOSED, "No audio codecs"); return 0; } } err = sipsess_accept(&call->sess, sess_sock, msg, 180, "Ringing", ua_cuser(call->ua), "application/sdp", NULL, auth_handler, call->acc, true, sipsess_offer_handler, sipsess_answer_handler, sipsess_estab_handler, sipsess_info_handler, sipsess_refer_handler, sipsess_close_handler, call, "Allow: %s\r\n", uag_allowed_methods()); if (err) { warning("call: sipsess_accept: %m\n", err); return err; } set_state(call, STATE_INCOMING); /* New call */ tmr_start(&call->tmr_inv, LOCAL_TIMEOUT*1000, invite_timeout, call); if (!call->acc->mnat) call_event_handler(call, CALL_EVENT_INCOMING, call->peer_uri); return err; }
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); } }
static int start(struct allocation *alloc) { struct sa laddr; int err = 0; if (!alloc) return EINVAL; sa_init(&laddr, sa_af(&alloc->srv)); switch (alloc->proto) { case IPPROTO_UDP: err = udp_listen(&alloc->us, &laddr, udp_recv, alloc); if (err) { re_fprintf(stderr, "allocation: failed to" " create UDP socket" " (%m)\n", err); goto out; } udp_sockbuf_set(alloc->us, 524288); if (alloc->secure) { /* note: re-using UDP socket for DTLS-traffic */ err = dtls_listen(&alloc->dtls_sock, NULL, alloc->us, 2, DTLS_LAYER, NULL, NULL); if (err) { re_fprintf(stderr, "dtls_listen error: %m\n", err); goto out; } err = dtls_connect(&alloc->tlsc, alloc->tls, alloc->dtls_sock, &alloc->srv, dtls_estab_handler, dtls_recv_handler, dtls_close_handler, alloc); if (err) { re_fprintf(stderr, "dtls_connect error: %m\n", err); goto out; } } else { err = turnc_alloc(&alloc->turnc, NULL, IPPROTO_UDP, alloc->us, TURN_LAYER, &alloc->srv, alloc->user, alloc->pass, TURN_DEFAULT_LIFETIME, turnc_handler, alloc); if (err) { re_fprintf(stderr, "allocation: failed to" " create TURN client" " (%m)\n", err); goto out; } } break; case IPPROTO_TCP: err = tcp_connect(&alloc->tc, &alloc->srv, tcp_estab_handler, tcp_recv_handler, tcp_close_handler, alloc); if (err) break; if (alloc->secure) { err = tls_start_tcp(&alloc->tlsc, alloc->tls, alloc->tc, 0); if (err) break; } break; default: err = EPROTONOSUPPORT; goto out; } out: return err; }
int sdp_encode(struct mbuf **mbp, struct sdp_session *sess, bool offer) { const int ipver = sa_af(&sess->laddr) == AF_INET ? 4 : 6; enum sdp_bandwidth i; struct mbuf *mb; struct le *le; int err; if (!mbp || !sess) return EINVAL; mb = mbuf_alloc(512); if (!mb) return ENOMEM; err = mbuf_printf(mb, "v=%u\r\n", SDP_VERSION); err |= mbuf_printf(mb, "o=- %u %u IN IP%d %j\r\n", sess->id, sess->ver++, ipver, &sess->laddr); err |= mbuf_write_str(mb, "s=-\r\n"); err |= mbuf_printf(mb, "c=IN IP%d %j\r\n", ipver, &sess->laddr); for (i=SDP_BANDWIDTH_MIN; i<SDP_BANDWIDTH_MAX; i++) { if (sess->lbwv[i] < 0) continue; err |= mbuf_printf(mb, "b=%s:%i\r\n", sdp_bandwidth_name(i), sess->lbwv[i]); } err |= mbuf_write_str(mb, "t=0 0\r\n"); for (le = sess->lattrl.head; le; le = le->next) err |= mbuf_printf(mb, "%H", sdp_attr_print, le->data); for (le=sess->lmedial.head; offer && le;) { struct sdp_media *m = le->data; le = le->next; if (m->disabled) continue; list_unlink(&m->le); list_append(&sess->medial, &m->le, m); } for (le=sess->medial.head; le; le=le->next) { struct sdp_media *m = le->data; err |= media_encode(m, mb, offer); } mb->pos = 0; if (err) mem_deref(mb); else *mbp = mb; return err; }
static int media_encode(const struct sdp_media *m, struct mbuf *mb, bool offer) { enum sdp_bandwidth i; int err, supc = 0; bool disabled; struct le *le; uint16_t port; for (le=m->lfmtl.head; le; le=le->next) { const struct sdp_format *fmt = le->data; if (fmt->sup) ++supc; } if (m->disabled || supc == 0 || (!offer && !sa_port(&m->raddr))) { disabled = true; port = 0; } else { disabled = false; port = sa_port(&m->laddr); } err = mbuf_printf(mb, "m=%s %u %s", m->name, port, m->proto); if (disabled) { err |= mbuf_write_str(mb, " 0\r\n"); return err; } for (le=m->lfmtl.head; le; le=le->next) { const struct sdp_format *fmt = le->data; if (!fmt->sup) continue; err |= mbuf_printf(mb, " %s", fmt->id); } err |= mbuf_write_str(mb, "\r\n"); if (sa_isset(&m->laddr, SA_ADDR)) { const int ipver = sa_af(&m->laddr) == AF_INET ? 4 : 6; err |= mbuf_printf(mb, "c=IN IP%d %j\r\n", ipver, &m->laddr); } for (i=SDP_BANDWIDTH_MIN; i<SDP_BANDWIDTH_MAX; i++) { if (m->lbwv[i] < 0) continue; err |= mbuf_printf(mb, "b=%s:%i\r\n", sdp_bandwidth_name(i), m->lbwv[i]); } for (le=m->lfmtl.head; le; le=le->next) { const struct sdp_format *fmt = le->data; if (!fmt->sup || !str_len(fmt->name)) continue; err |= mbuf_printf(mb, "a=rtpmap:%s %s/%u", fmt->id, fmt->name, fmt->srate); if (fmt->ch > 1) err |= mbuf_printf(mb, "/%u", fmt->ch); err |= mbuf_printf(mb, "\r\n"); if (str_len(fmt->params)) err |= mbuf_printf(mb, "a=fmtp:%s %s\r\n", fmt->id, fmt->params); } if (sa_isset(&m->laddr_rtcp, SA_ALL)) err |= mbuf_printf(mb, "a=rtcp:%u IN IP%d %j\r\n", sa_port(&m->laddr_rtcp), (AF_INET == sa_af(&m->laddr_rtcp)) ? 4 : 6, &m->laddr_rtcp); else if (sa_isset(&m->laddr_rtcp, SA_PORT)) err |= mbuf_printf(mb, "a=rtcp:%u\r\n", sa_port(&m->laddr_rtcp)); err |= mbuf_printf(mb, "a=%s\r\n", sdp_dir_name(offer ? m->ldir : m->ldir & m->rdir)); for (le = m->lattrl.head; le; le = le->next) err |= mbuf_printf(mb, "%H", sdp_attr_print, le->data); return err; }
/** * Print a formatted string * * @param fmt Formatted string * @param ap Variable argument * @param vph Print handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode * * Extensions: * * <pre> * %b (char *, size_t) Buffer string with pointer and length * %r (struct pl) Pointer-length object * %w (uint8_t *, size_t) Binary buffer to hexadecimal format * %j (struct sa *) Socket address - address part only * %J (struct sa *) Socket address and port - like 1.2.3.4:1234 * %H (re_printf_h *, void *) Print handler with argument * %v (char *fmt, va_list *) Variable argument list * %m (int) Describe an error code * </pre> * * Reserved for the future: * * %k * %y * */ int re_vhprintf(const char *fmt, va_list ap, re_vprintf_h *vph, void *arg) { uint8_t base, *bptr; char pch, ch, num[NUM_SIZE], addr[64], msg[256]; enum length_modifier lenmod = LENMOD_NONE; struct re_printf pf; bool fm = false, plr = false; const struct pl *pl; size_t pad = 0, fpad = -1, len, i; const char *str, *p = fmt, *p0 = fmt; const struct sa *sa; re_printf_h *ph; void *ph_arg; va_list *apl; int err = 0; void *ptr; uint64_t n; int64_t sn; bool uc = false; double dbl; if (!fmt || !vph) return EINVAL; pf.vph = vph; pf.arg = arg; for (;*p && !err; p++) { if (!fm) { if (*p != '%') continue; pch = ' '; plr = false; pad = 0; fpad = -1; lenmod = LENMOD_NONE; uc = false; if (p > p0) err |= vph(p0, p - p0, arg); fm = true; continue; } fm = false; base = 10; switch (*p) { case '-': plr = true; fm = true; break; case '.': fpad = pad; pad = 0; fm = true; break; case '%': ch = '%'; err |= vph(&ch, 1, arg); break; case 'b': str = va_arg(ap, const char *); len = va_arg(ap, size_t); err |= write_padded(str, str ? len : 0, pad, ' ', plr, NULL, vph, arg); break; case 'c': ch = va_arg(ap, int); err |= write_padded(&ch, 1, pad, ' ', plr, NULL, vph, arg); break; case 'd': case 'i': switch (lenmod) { case LENMOD_SIZE: sn = va_arg(ap, ssize_t); break; default: case LENMOD_LONG_LONG: sn = va_arg(ap, signed long long); break; case LENMOD_LONG: sn = va_arg(ap, signed long); break; case LENMOD_NONE: sn = va_arg(ap, signed); break; } len = local_itoa(num, (sn < 0) ? -sn : sn, base, false); err |= write_padded(num, len, pad, plr ? ' ' : pch, plr, (sn < 0) ? prfx_neg : NULL, vph, arg); break; case 'f': case 'F': dbl = va_arg(ap, double); if (fpad == (size_t)-1) { fpad = pad; pad = 0; } if (isinf(dbl)) { err |= write_padded("inf", 3, fpad, ' ', plr, NULL, vph, arg); } else if (isnan(dbl)) { err |= write_padded("nan", 3, fpad, ' ', plr, NULL, vph, arg); } else { len = local_ftoa(num, dbl, pad ? min(pad, DEC_SIZE) : 6); err |= write_padded(num, len, fpad, plr ? ' ' : pch, plr, (dbl<0) ? prfx_neg : NULL, vph, arg); } break; case 'H': ph = va_arg(ap, re_printf_h *); ph_arg = va_arg(ap, void *); if (ph) err |= ph(&pf, ph_arg); break; case 'l': ++lenmod; fm = true; break; case 'm': str = str_error(va_arg(ap, int), msg, sizeof(msg)); err |= write_padded(str, str_len(str), pad, ' ', plr, NULL, vph, arg); break; case 'p': ptr = va_arg(ap, void *); if (ptr) { len = local_itoa(num, (unsigned long int)ptr, 16, false); err |= write_padded(num, len, pad, plr ? ' ' : pch, plr, prfx_hex, vph, arg); } else { err |= write_padded(str_nil, sizeof(str_nil) - 1, pad, ' ', plr, NULL, vph, arg); } break; case 'r': pl = va_arg(ap, const struct pl *); err |= write_padded(pl ? pl->p : NULL, (pl && pl->p) ? pl->l : 0, pad, ' ', plr, NULL, vph, arg); break; case 's': str = va_arg(ap, const char *); err |= write_padded(str, str_len(str), pad, ' ', plr, NULL, vph, arg); break; case 'X': uc = true; /*@fallthrough@*/ case 'x': base = 16; /*@fallthrough@*/ case 'u': switch (lenmod) { case LENMOD_SIZE: n = va_arg(ap, size_t); break; default: case LENMOD_LONG_LONG: n = va_arg(ap, unsigned long long); break; case LENMOD_LONG: n = va_arg(ap, unsigned long); break; case LENMOD_NONE: n = va_arg(ap, unsigned); break; } len = local_itoa(num, n, base, uc); err |= write_padded(num, len, pad, plr ? ' ' : pch, plr, NULL, vph, arg); break; case 'v': str = va_arg(ap, char *); apl = va_arg(ap, va_list *); if (!str || !apl) break; err |= re_vhprintf(str, *apl, vph, arg); break; case 'W': uc = true; /*@fallthrough@*/ case 'w': bptr = va_arg(ap, uint8_t *); len = va_arg(ap, size_t); len = bptr ? len : 0; pch = plr ? ' ' : pch; while (!plr && pad-- > (len * 2)) err |= vph(&pch, 1, arg); for (i=0; i<len; i++) { const uint8_t v = *bptr++; uint32_t l = local_itoa(num, v, 16, uc); err |= write_padded(num, l, 2, '0', false, NULL, vph, arg); } while (plr && pad-- > (len * 2)) err |= vph(&pch, 1, arg); break; case 'z': lenmod = LENMOD_SIZE; fm = true; break; case 'j': sa = va_arg(ap, struct sa *); if (!sa) break; if (sa_ntop(sa, addr, sizeof(addr))) { err |= write_padded("?", 1, pad, ' ', plr, NULL, vph, arg); break; } err |= write_padded(addr, strlen(addr), pad, ' ', plr, NULL, vph, arg); break; case 'J': sa = va_arg(ap, struct sa *); if (!sa) break; if (sa_ntop(sa, addr, sizeof(addr))) { err |= write_padded("?", 1, pad, ' ', plr, NULL, vph, arg); break; } #ifdef HAVE_INET6 if (AF_INET6 == sa_af(sa)) { ch = '['; err |= vph(&ch, 1, arg); } #endif err |= write_padded(addr, strlen(addr), pad, ' ', plr, NULL, vph, arg); #ifdef HAVE_INET6 if (AF_INET6 == sa_af(sa)) { ch = ']'; err |= vph(&ch, 1, arg); } #endif ch = ':'; err |= vph(&ch, 1, arg); len = local_itoa(num, sa_port(sa), 10, false); err |= write_padded(num, len, pad, plr ? ' ' : pch, plr, NULL, vph, arg); break; default: if (('0' <= *p) && (*p <= '9')) { if (!pad && ('0' == *p)) { pch = '0'; } else { pad *= 10; pad += *p - '0'; } fm = true; break; } ch = '?'; err |= vph(&ch, 1, arg); break; } if (!fm) p0 = p + 1; } if (!fm && p > p0) err |= vph(p0, p - p0, arg); return err; }
/** * Create and listen on a UDP Socket * * @param usp Pointer to returned UDP Socket * @param local Local network address * @param rh Receive handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int udp_listen(struct udp_sock **usp, const struct sa *local, udp_recv_h *rh, void *arg) { struct addrinfo hints, *res = NULL, *r; struct udp_sock *us = NULL; char addr[NET_ADDRSTRLEN]; char serv[6] = "0"; int af, error, err = 0; if (!usp) return EINVAL; us = mem_zalloc(sizeof(*us), udp_destructor); if (!us) return ENOMEM; list_init(&us->helpers); us->fd = -1; us->fd6 = -1; if (local) { af = sa_af(local); err = sa_ntop(local, addr, sizeof(addr)); (void)re_snprintf(serv, sizeof(serv), "%u", sa_port(local)); if (err) goto out; } else { #ifdef HAVE_INET6 af = AF_UNSPEC; #else af = AF_INET; #endif } memset(&hints, 0, sizeof(hints)); /* set-up hints structure */ hints.ai_family = af; hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; error = getaddrinfo(local ? addr : NULL, serv, &hints, &res); if (error) { #ifdef WIN32 DEBUG_WARNING("listen: getaddrinfo: wsaerr=%d\n", WSAGetLastError()); #endif DEBUG_WARNING("listen: getaddrinfo: %s:%s (%s)\n", addr, serv, gai_strerror(error)); err = EADDRNOTAVAIL; goto out; } for (r = res; r; r = r->ai_next) { int fd = -1; if (us->fd > 0) continue; DEBUG_INFO("listen: for: af=%d addr=%j\n", r->ai_family, r->ai_addr); fd = SOK_CAST socket(r->ai_family, SOCK_DGRAM, IPPROTO_UDP); if (fd < 0) { err = errno; continue; } err = net_sockopt_blocking_set(fd, false); if (err) { DEBUG_WARNING("udp listen: nonblock set: %s\n", strerror(err)); (void)close(fd); continue; } if (bind(fd, r->ai_addr, SIZ_CAST r->ai_addrlen) < 0) { err = errno; DEBUG_INFO("listen: bind(): %s (%J)\n", strerror(err), local); (void)close(fd); continue; } /* Can we do both IPv4 and IPv6 on same socket? */ if (AF_INET6 == r->ai_family) { struct sa sa; int on = 1; /* assume v6only */ #if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY) socklen_t on_len = sizeof(on); if (0 != getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on, &on_len)) { on = 1; } #endif /* Extra check for unspec addr - MAC OS X/Solaris */ if (0==sa_set_sa(&sa, r->ai_addr) && sa_is_any(&sa)) { on = 1; } DEBUG_INFO("socket %d: IPV6_V6ONLY is %d\n", fd, on); if (on) { us->fd6 = fd; continue; } } /* OK */ us->fd = fd; break; } freeaddrinfo(res); /* We must have at least one socket */ if (-1 == us->fd && -1 == us->fd6) { if (0 == err) err = EADDRNOTAVAIL; goto out; } err = udp_thread_attach(us); if (err) goto out; us->rh = rh ? rh : dummy_udp_recv_handler; us->arg = arg; us->rxsz = UDP_RXSZ_DEFAULT; out: if (err) mem_deref(us); else *usp = us; return err; }