/** * Accept an incoming SIP Session connection * * @param sessp Pointer to allocated SIP Session * @param sock SIP Session socket * @param msg Incoming SIP message * @param scode Response status code * @param reason Response reason phrase * @param cuser Contact username * @param ctype Session content-type * @param desc Content description (e.g. SDP) * @param authh SIP Authentication handler * @param aarg Authentication handler argument * @param aref True to mem_ref() aarg * @param offerh Session offer handler * @param answerh Session answer handler * @param estabh Session established handler * @param infoh Session info handler * @param referh Session refer handler * @param closeh Session close handler * @param arg Handler argument * @param fmt Formatted strings with extra SIP Headers * * @return 0 if success, otherwise errorcode */ int sipsess_accept(struct sipsess **sessp, struct sipsess_sock *sock, const struct sip_msg *msg, uint16_t scode, const char *reason, const char *cuser, const char *ctype, struct mbuf *desc, sip_auth_h *authh, void *aarg, bool aref, sipsess_offer_h *offerh, sipsess_answer_h *answerh, sipsess_estab_h *estabh, sipsess_info_h *infoh, sipsess_refer_h *referh, sipsess_close_h *closeh, void *arg, const char *fmt, ...) { struct sipsess *sess; va_list ap; int err; if (!sessp || !sock || !msg || scode < 101 || scode > 299 || !cuser || !ctype) return EINVAL; err = sipsess_alloc(&sess, sock, cuser, ctype, NULL, authh, aarg, aref, offerh, answerh, NULL, estabh, infoh, referh, closeh, arg); if (err) return err; err = sip_dialog_accept(&sess->dlg, msg); if (err) goto out; hash_append(sock->ht_sess, hash_joaat_str(sip_dialog_callid(sess->dlg)), &sess->he, sess); sess->msg = mem_ref((void *)msg); err = sip_strans_alloc(&sess->st, sess->sip, msg, cancel_handler, sess); if (err) goto out; va_start(ap, fmt); if (scode >= 200) err = sipsess_reply_2xx(sess, msg, scode, reason, desc, fmt, &ap); else err = sip_treplyf(&sess->st, NULL, sess->sip, msg, true, scode, reason, "Contact: <sip:%s@%J%s>\r\n" "%v" "%s%s%s" "Content-Length: %zu\r\n" "\r\n" "%b", sess->cuser, &msg->dst, sip_transp_param(msg->tp), fmt, &ap, desc ? "Content-Type: " : "", desc ? sess->ctype : "", desc ? "\r\n" : "", desc ? mbuf_get_left(desc) : (size_t)0, desc ? mbuf_buf(desc) : NULL, desc ? mbuf_get_left(desc) : (size_t)0); va_end(ap); if (err) goto out; out: if (err) mem_deref(sess); else *sessp = sess; return err; }
static int media_alloc(struct menc_media **stp, struct menc_sess *sess, struct rtp_sock *rtp, struct udp_sock *rtpsock, struct udp_sock *rtcpsock, const struct sa *raddr_rtp, const struct sa *raddr_rtcp, struct sdp_media *sdpm) { struct menc_st *st; const char *rattr = NULL; int layer = 10; /* above zero */ int err = 0; bool mux = (rtpsock == rtcpsock); (void)sess; (void)rtp; (void)raddr_rtp; (void)raddr_rtcp; if (!stp || !sdpm || !sess) return EINVAL; st = (struct menc_st *)*stp; if (!st) { st = mem_zalloc(sizeof(*st), destructor); if (!st) return ENOMEM; st->sess = sess; st->sdpm = mem_ref(sdpm); if (0 == str_cmp(sdp_media_proto(sdpm), "RTP/AVP")) { err = sdp_media_set_alt_protos(st->sdpm, 4, "RTP/AVP", "RTP/AVPF", "RTP/SAVP", "RTP/SAVPF"); if (err) goto out; } if (rtpsock) { st->rtpsock = mem_ref(rtpsock); err |= udp_register_helper(&st->uh_rtp, rtpsock, layer, send_handler, recv_handler, st); } if (rtcpsock && !mux) { st->rtcpsock = mem_ref(rtcpsock); err |= udp_register_helper(&st->uh_rtcp, rtcpsock, layer, send_handler, recv_handler, st); } if (err) goto out; /* set our preferred crypto-suite */ err |= str_dup(&st->crypto_suite, preferred_suite); if (err) goto out; rand_bytes(st->key_tx, sizeof(st->key_tx)); } /* SDP handling */ if (sdp_media_rport(sdpm)) st->got_sdp = true; if (sdp_media_rattr(st->sdpm, "crypto")) { rattr = sdp_media_rattr_apply(st->sdpm, "crypto", sdp_attr_handler, st); if (!rattr) { warning("srtp: no valid a=crypto attribute from" " remote peer\n"); } } if (!rattr) err = sdp_enc(st, sdpm, 1, st->crypto_suite); out: if (err) mem_deref(st); else *stp = (struct menc_media *)st; return err; }
/* * you can call this at any time * * @param addr HOST: SA_ADDR portion is used * non-HOST: SA_ADDR + SA_PORT portion is used * * @param base_addr Optional * @param rel_addr Optional * * @param layer mandatory for HOST and RELAY candidates */ int trice_lcand_add(struct ice_lcand **lcandp, struct trice *icem, unsigned compid, int proto, uint32_t prio, const struct sa *addr, const struct sa *base_addr, enum ice_cand_type type, const struct sa *rel_addr, enum ice_tcptype tcptype, void *sock, int layer) { struct ice_lcand *lcand; int err = 0; if (!icem || !compid || !proto || !addr) return EINVAL; if (!sa_isset(addr, SA_ADDR)) { DEBUG_WARNING("lcand_add: SA_ADDR is not set\n"); return EINVAL; } if (type != ICE_CAND_TYPE_HOST) { if (!sa_isset(addr, SA_PORT)) { DEBUG_WARNING("lcand_add: %s: SA_PORT" " must be set (%J)\n", ice_cand_type2name(type), addr); return EINVAL; } } /* lookup candidate, replace if PRIO is higher */ /* TODO: dont look up TCP-ACTIVE types for now (port is zero) */ if (proto == IPPROTO_UDP) { lcand = trice_lcand_find(icem, -1, compid, proto, addr); if (lcand) { trice_printf(icem, "add_local[%s.%J] --" " candidate already exists" " (%H)\n", ice_cand_type2name(type), addr, trice_cand_print, lcand); if (prio > lcand->attr.prio) lcand = mem_deref(lcand); else { goto out; } } } err = trice_add_lcandidate(&lcand, icem, &icem->lcandl, compid, NULL, proto, prio, addr, base_addr, type, rel_addr, tcptype); if (err) return err; if (type == ICE_CAND_TYPE_HOST) { switch (proto) { case IPPROTO_UDP: if (sock) { struct sa laddr; lcand->us = mem_ref(sock); err = udp_local_get(lcand->us, &laddr); if (err) goto out; lcand->attr.addr = *addr; sa_set_port(&lcand->attr.addr, sa_port(&laddr)); } else { err = udp_listen(&lcand->us, addr, dummy_udp_recv, lcand); if (err) goto out; err = udp_local_get(lcand->us, &lcand->attr.addr); if (err) goto out; } err = udp_register_helper(&lcand->uh, lcand->us, layer, udp_helper_send_handler, udp_helper_recv_handler, lcand); if (err) goto out; break; case IPPROTO_TCP: /* TCP-transport has 3 variants: active, passive, so */ if (lcand->attr.tcptype == ICE_TCP_ACTIVE) { /* the port MUST be set to 9 (i.e., Discard) */ /*sa_set_port(&lcand->attr.addr, 9); */ } else if (lcand->attr.tcptype == ICE_TCP_PASSIVE || lcand->attr.tcptype == ICE_TCP_SO) { err = tcp_listen(&lcand->ts, addr, tcp_conn_handler, lcand); if (err) goto out; err = tcp_local_get(lcand->ts, &lcand->attr.addr); if (err) goto out; } else { err = EPROTONOSUPPORT; goto out; } break; default: err = EPROTONOSUPPORT; goto out; } } else if (type == ICE_CAND_TYPE_RELAY) { switch (proto) { case IPPROTO_UDP: if (sock) { lcand->us = mem_ref(sock); } else { err = udp_listen(&lcand->us, NULL, dummy_udp_recv, lcand); if (err) goto out; } err = udp_register_helper(&lcand->uh, lcand->us, layer, udp_helper_send_handler, udp_helper_recv_handler, lcand); if (err) goto out; break; default: err = EPROTONOSUPPORT; goto out; } } else if (type == ICE_CAND_TYPE_SRFLX || type == ICE_CAND_TYPE_PRFLX) { /* Special case for SRFLX UDP candidates, if he has * its own UDP-socket that can be used. */ if (proto == IPPROTO_UDP && sock) { lcand->us = mem_ref(sock); err = udp_register_helper(&lcand->uh, lcand->us, layer, udp_helper_send_handler, udp_helper_recv_handler, lcand); if (err) goto out; } } lcand->layer = layer; /* pair this local-candidate with all existing remote-candidates */ err = trice_candpair_with_local(icem, lcand); if (err) goto out; /* new pair -- refresh the checklist timer */ trice_checklist_refresh(icem); out: if (err) mem_deref(lcand); else if (lcandp) *lcandp = lcand; return err; }
static void recv_handler(struct mbuf *mb, void *arg) { struct rst *rst = arg; size_t n; if (!rst->head_recv) { struct pl hdr, name, metaint, eoh; if (rst->mb) { size_t pos; int err; pos = rst->mb->pos; rst->mb->pos = rst->mb->end; err = mbuf_write_mem(rst->mb, mbuf_buf(mb), mbuf_get_left(mb)); if (err) { re_printf("rst: buffer write error: %m\n", err); rst->tc = mem_deref(rst->tc); tmr_start(&rst->tmr, RETRY_WAIT, reconnect, rst); return; } rst->mb->pos = pos; } else { rst->mb = mem_ref(mb); } if (re_regex((const char *)mbuf_buf(rst->mb), mbuf_get_left(rst->mb), "[^\r\n]1\r\n\r\n", &eoh)) return; rst->head_recv = true; hdr.p = (const char *)mbuf_buf(rst->mb); hdr.l = eoh.p + 5 - hdr.p; if (!re_regex(hdr.p, hdr.l, "icy-name:[ \t]*[^\r\n]+\r\n", NULL, &name)) (void)pl_strdup(&rst->name, &name); if (!re_regex(hdr.p, hdr.l, "icy-metaint:[ \t]*[0-9]+\r\n", NULL, &metaint)) rst->metaint = pl_u32(&metaint); if (rst->metaint == 0) { re_printf("rst: icy meta interval not available\n"); rst->tc = mem_deref(rst->tc); tmr_start(&rst->tmr, RETRY_WAIT, reconnect, rst); return; } rst_video_update(rst->vidsrc_st, rst->name, NULL); rst->mb->pos += hdr.l; re_printf("rst: name='%s' metaint=%zu\n", rst->name, rst->metaint); if (rst->mb->pos >= rst->mb->end) return; mb = rst->mb; } while (mb->pos < mb->end) { if (rst->metasz > 0) { n = min(mbuf_get_left(mb), rst->metasz - rst->bytec); if (rst->meta) mbuf_read_mem(mb, (uint8_t *)&rst->meta[rst->bytec], n); else mb->pos += n; rst->bytec += n; #if 0 re_printf("rst: metadata %zu bytes\n", n); #endif if (rst->bytec >= rst->metasz) { #if 0 re_printf("rst: metadata: [%s]\n", rst->meta); #endif rst->metasz = 0; rst->bytec = 0; rst_video_update(rst->vidsrc_st, rst->name, rst->meta); } } else if (rst->bytec < rst->metaint) { n = min(mbuf_get_left(mb), rst->metaint - rst->bytec); rst_audio_feed(rst->ausrc_st, mbuf_buf(mb), n); rst->bytec += n; mb->pos += n; #if 0 re_printf("rst: mp3data %zu bytes\n", n); #endif } else { rst->metasz = mbuf_read_u8(mb) * 16; rst->bytec = 0; rst->meta = mem_deref(rst->meta); rst->meta = mem_zalloc(rst->metasz + 1, NULL); #if 0 re_printf("rst: metalength %zu bytes\n", rst->metasz); #endif } } }
static void tcp_recv_handler(struct mbuf *mbx, void *arg) { struct candidate *tl = arg; int err = 0; if (tl->mb) { size_t pos; pos = tl->mb->pos; tl->mb->pos = tl->mb->end; err = mbuf_write_mem(tl->mb, mbuf_buf(mbx), mbuf_get_left(mbx)); if (err) goto out; tl->mb->pos = pos; } else { tl->mb = mem_ref(mbx); } for (;;) { size_t len, pos, end; uint16_t typ; if (mbuf_get_left(tl->mb) < 4) break; /* STUN Header */ typ = ntohs(mbuf_read_u16(tl->mb)); len = ntohs(mbuf_read_u16(tl->mb)); if (typ < 0x4000) len += STUN_HEADER_SIZE; else if (typ < 0x8000) len += 4; else { err = EBADMSG; goto out; } tl->mb->pos -= 4; if (mbuf_get_left(tl->mb) < len) break; pos = tl->mb->pos; end = tl->mb->end; tl->mb->end = pos + len; handle_packet(tl, tl->mb); /* 4 byte alignment */ while (len & 0x03) ++len; tl->mb->pos = pos + len; tl->mb->end = end; if (tl->mb->pos >= tl->mb->end) { tl->mb = mem_deref(tl->mb); break; } } out: if (err) { re_printf("tcp recv error (%m)\n", err); } }
/** * Put one frame into the jitter buffer * * @param jb Jitter buffer * @param hdr RTP Header * @param mem Memory pointer - will be referenced * * @return 0 if success, otherwise errorcode */ int jbuf_put(struct jbuf *jb, const struct rtp_header *hdr, void *mem) { struct frame *f; struct le *le, *tail; uint16_t seq; int err = 0; if (!jb || !hdr) return EINVAL; seq = hdr->seq; STAT_INC(n_put); if (jb->running) { /* Packet arrived too late to be put into buffer */ if (seq_less((seq + jb->n), jb->seq_put)) { STAT_INC(n_late); DEBUG_INFO("packet too late: seq=%u (seq_put=%u)\n", seq, jb->seq_put); return ETIMEDOUT; } } frame_alloc(jb, &f); tail = jb->framel.tail; /* If buffer is empty -> append to tail Frame is later than tail -> append to tail */ if (!tail || seq_less(((struct frame *)tail->data)->hdr.seq, seq)) { list_append(&jb->framel, &f->le, f); goto out; } /* Out-of-sequence, find right position */ for (le = tail; le; le = le->prev) { const uint16_t seq_le = ((struct frame *)le->data)->hdr.seq; if (seq_less(seq_le, seq)) { /* most likely */ DEBUG_INFO("put: out-of-sequence" " - inserting after seq=%u (seq=%u)\n", seq_le, seq); list_insert_after(&jb->framel, le, &f->le, f); break; } else if (seq == seq_le) { /* less likely */ /* Detect duplicates */ DEBUG_INFO("duplicate: seq=%u\n", seq); STAT_INC(n_dups); list_insert_after(&jb->framel, le, &f->le, f); frame_deref(jb, f); return EALREADY; } /* sequence number less than current seq, continue */ } /* no earlier timestamps found, put in head */ if (!le) { DEBUG_INFO("put: out-of-sequence" " - put in head (seq=%u)\n", seq); list_prepend(&jb->framel, &f->le, f); } STAT_INC(n_oos); out: /* Update last timestamp */ jb->running = true; jb->seq_put = seq; /* Success */ f->hdr = *hdr; f->mem = mem_ref(mem); return err; }
/** * Initialize a SIP Dialog from an incoming SIP Message * * @param dlg SIP Dialog to initialize * @param msg SIP Message * * @return 0 if success, otherwise errorcode */ int sip_dialog_create(struct sip_dialog *dlg, const struct sip_msg *msg) { char *uri = NULL, *rtag = NULL; const struct sip_hdr *contact; struct route_enc renc; struct sip_addr addr; struct pl pl; int err; if (!dlg || dlg->rtag || !dlg->cpos || !msg) return EINVAL; contact = sip_msg_hdr(msg, SIP_HDR_CONTACT); if (!contact) return EBADMSG; if (sip_addr_decode(&addr, &contact->val)) return EBADMSG; renc.mb = mbuf_alloc(512); if (!renc.mb) return ENOMEM; err = pl_strdup(&uri, &addr.auri); if (err) goto out; err = pl_strdup(&rtag, msg->req ? &msg->from.tag : &msg->to.tag); if (err) goto out; renc.end = 0; err |= sip_msg_hdr_apply(msg, msg->req, SIP_HDR_RECORD_ROUTE, record_route_handler, &renc) ? ENOMEM : 0; err |= mbuf_printf(renc.mb, "To: %r\r\n", msg->req ? &msg->from.val : &msg->to.val); dlg->mb->pos = dlg->cpos; err |= mbuf_write_mem(renc.mb, mbuf_buf(dlg->mb), mbuf_get_left(dlg->mb)); dlg->mb->pos = 0; if (err) goto out; renc.mb->pos = 0; if (renc.end) { pl.p = (const char *)mbuf_buf(renc.mb) + ROUTE_OFFSET; pl.l = renc.end - ROUTE_OFFSET; err = sip_addr_decode(&addr, &pl); if (err) goto out; dlg->route = addr.uri; } else { struct uri tmp; pl_set_str(&pl, uri); err = uri_decode(&tmp, &pl); if (err) goto out; dlg->route = tmp; } mem_deref(dlg->mb); mem_deref(dlg->uri); dlg->mb = mem_ref(renc.mb); dlg->rtag = mem_ref(rtag); dlg->uri = mem_ref(uri); dlg->rseq = msg->req ? msg->cseq.num : 0; dlg->cpos = 0; out: mem_deref(renc.mb); mem_deref(rtag); mem_deref(uri); return err; }
int sdp_format_add(struct sdp_format **fmtp, struct sdp_media *m, bool prepend, const char *id, const char *name, uint32_t srate, uint8_t ch, sdp_fmtp_cmp_h *cmph, void *data, bool ref, const char *params, ...) { struct sdp_format *fmt; int err; if (!m) return EINVAL; if (!id && (m->dynpt > RTP_DYNPT_END)) return ERANGE; fmt = mem_zalloc(sizeof(*fmt), destructor); if (!fmt) return ENOMEM; if (prepend) list_prepend(&m->lfmtl, &fmt->le, fmt); else list_append(&m->lfmtl, &fmt->le, fmt); if (id) err = str_dup(&fmt->id, id); else err = re_sdprintf(&fmt->id, "%i", m->dynpt++); if (err) goto out; if (name) { err = str_dup(&fmt->name, name); if (err) goto out; } if (params) { va_list ap; va_start(ap, params); err = re_vsdprintf(&fmt->params, params, ap); va_end(ap); if (err) goto out; } fmt->pt = atoi(fmt->id); fmt->srate = srate; fmt->ch = ch; fmt->cmph = cmph; fmt->data = ref ? mem_ref(data) : data; fmt->ref = ref; fmt->sup = true; out: if (err) mem_deref(fmt); else if (fmtp) *fmtp = fmt; return err; }
int sipevent_accept(struct sipnot **notp, struct sipevent_sock *sock, const struct sip_msg *msg, struct sip_dialog *dlg, const struct sipevent_event *event, uint16_t scode, const char *reason, uint32_t expires_min, uint32_t expires_dfl, uint32_t expires_max, const char *cuser, const char *ctype, sip_auth_h *authh, void *aarg, bool aref, sipnot_close_h *closeh, void *arg, const char *fmt, ...) { struct sipnot *not; uint32_t expires; int err; if (!notp || !sock || !msg || !scode || !reason || !expires_dfl || !expires_max || !cuser || !ctype || expires_dfl < expires_min) return EINVAL; not = mem_zalloc(sizeof(*not), destructor); if (!not) return ENOMEM; if (!pl_strcmp(&msg->met, "REFER")) { err = str_dup(¬->event, "refer"); if (err) goto out; err = re_sdprintf(¬->id, "%u", msg->cseq.num); if (err) goto out; } else { if (!event) { err = EINVAL; goto out; } err = pl_strdup(¬->event, &event->event); if (err) goto out; if (pl_isset(&event->id)) { err = pl_strdup(¬->id, &event->id); if (err) goto out; } } if (dlg) { not->dlg = mem_ref(dlg); } else { err = sip_dialog_accept(¬->dlg, msg); if (err) goto out; } hash_append(sock->ht_not, hash_joaat_str(sip_dialog_callid(not->dlg)), ¬->he, not); err = sip_auth_alloc(¬->auth, authh, aarg, aref); if (err) goto out; err = str_dup(¬->cuser, cuser); if (err) goto out; err = str_dup(¬->ctype, ctype); if (err) goto out; if (fmt) { va_list ap; va_start(ap, fmt); err = re_vsdprintf(¬->hdrs, fmt, ap); va_end(ap); if (err) goto out; } not->expires_min = expires_min; not->expires_dfl = expires_dfl; not->expires_max = expires_max; not->substate = SIPEVENT_PENDING; not->sock = mem_ref(sock); not->sip = mem_ref(sock->sip); not->closeh = closeh ? closeh : internal_close_handler; not->arg = arg; if (pl_isset(&msg->expires)) expires = pl_u32(&msg->expires); else expires = not->expires_dfl; sipnot_refresh(not, expires); err = sipnot_reply(not, msg, scode, reason); if (err) goto out; not->subscribed = true; out: if (err) mem_deref(not); else *notp = not; return err; }
/** * Decode a HTTP message * * @param msgp Pointer to allocated HTTP Message * @param mb Buffer containing HTTP Message * @param req True for request, false for response * * @return 0 if success, otherwise errorcode */ int http_msg_decode(struct http_msg **msgp, struct mbuf *mb, bool req) { struct pl b, s, e, name, scode; const char *p, *cv; struct http_msg *msg; bool comsep, quote; enum http_hdrid id = HTTP_HDR_NONE; uint32_t ws, lf; size_t l; int err; if (!msgp || !mb) return EINVAL; p = (const char *)mbuf_buf(mb); l = mbuf_get_left(mb); if (re_regex(p, l, "[\r\n]*[^\r\n]+[\r]*[\n]1", &b, &s, NULL, &e)) return (l > STARTLINE_MAX) ? EBADMSG : ENODATA; msg = mem_zalloc(sizeof(*msg), destructor); if (!msg) return ENOMEM; msg->mb = mem_ref(mb); if (req) { if (re_regex(s.p, s.l, "[a-z]+ [^? ]+[^ ]* HTTP/[0-9.]+", &msg->met, &msg->path, &msg->prm, &msg->ver) || msg->met.p != s.p) { err = EBADMSG; goto out; } } else { if (re_regex(s.p, s.l, "HTTP/[0-9.]+ [0-9]+ [^]*", &msg->ver, &scode, &msg->reason) || msg->ver.p != s.p + 5) { err = EBADMSG; goto out; } msg->scode = pl_u32(&scode); } l -= e.p + e.l - p; p = e.p + e.l; name.p = cv = NULL; name.l = ws = lf = 0; comsep = false; quote = false; for (; l > 0; p++, l--) { switch (*p) { case ' ': case '\t': lf = 0; /* folding */ ++ws; break; case '\r': ++ws; break; case '\n': ++ws; if (!name.p) { ++p; --l; /* no headers */ err = 0; goto out; } if (!lf++) break; ++p; --l; /* eoh */ /*@fallthrough@*/ default: if (lf || (*p == ',' && comsep && !quote)) { if (!name.l) { err = EBADMSG; goto out; } err = hdr_add(msg, &name, id, cv ? cv : p, cv ? p - cv - ws : 0); if (err) goto out; if (!lf) { /* comma separated */ cv = NULL; break; } if (lf > 1) { /* eoh */ err = 0; goto out; } comsep = false; name.p = NULL; cv = NULL; lf = 0; } if (!name.p) { name.p = p; name.l = 0; ws = 0; } if (!name.l) { if (*p != ':') { ws = 0; break; } name.l = MAX((int)(p - name.p - ws), 0); if (!name.l) { err = EBADMSG; goto out; } id = hdr_hash(&name); comsep = hdr_comma_separated(id); break; } if (!cv) { quote = false; cv = p; } if (*p == '"') quote = !quote; ws = 0; break; } } err = ENODATA; out: if (err) mem_deref(msg); else { *msgp = msg; mb->pos = mb->end - l; } return err; }
/** * Add a SIP transport * * @param sip SIP stack instance * @param tp SIP Transport * @param laddr Local network address * @param ... Optional transport parameters such as TLS context * * @return 0 if success, otherwise errorcode */ int sip_transp_add(struct sip *sip, enum sip_transp tp, const struct sa *laddr, ...) { struct sip_transport *transp; struct tls *tls; va_list ap; int err; if (!sip || !laddr || !sa_isset(laddr, SA_ADDR)) return EINVAL; transp = mem_zalloc(sizeof(*transp), transp_destructor); if (!transp) return ENOMEM; list_append(&sip->transpl, &transp->le, transp); transp->sip = sip; transp->tp = tp; va_start(ap, laddr); switch (tp) { case SIP_TRANSP_UDP: err = udp_listen((struct udp_sock **)&transp->sock, laddr, udp_recv_handler, transp); if (err) break; err = udp_local_get(transp->sock, &transp->laddr); break; case SIP_TRANSP_TLS: tls = va_arg(ap, struct tls *); if (!tls) { err = EINVAL; break; } transp->tls = mem_ref(tls); /*@fallthrough@*/ case SIP_TRANSP_TCP: err = tcp_listen((struct tcp_sock **)&transp->sock, laddr, tcp_connect_handler, transp); if (err) break; err = tcp_sock_local_get(transp->sock, &transp->laddr); break; default: err = EPROTONOSUPPORT; break; } va_end(ap); if (err) mem_deref(transp); 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 void tcp_recv_handler(struct mbuf *mb, void *arg) { struct sip_conn *conn = arg; size_t pos; int err = 0; if (conn->mb) { pos = conn->mb->pos; conn->mb->pos = conn->mb->end; err = mbuf_write_mem(conn->mb, mbuf_buf(mb),mbuf_get_left(mb)); if (err) goto out; conn->mb->pos = pos; if (mbuf_get_left(conn->mb) > TCP_BUFSIZE_MAX) { err = EOVERFLOW; goto out; } } else { conn->mb = mem_ref(mb); } for (;;) { struct sip_msg *msg; uint32_t clen; size_t end; if (mbuf_get_left(conn->mb) < 2) break; if (!memcmp(mbuf_buf(conn->mb), "\r\n", 2)) { tmr_start(&conn->tmr, TCP_IDLE_TIMEOUT * 1000, conn_tmr_handler, conn); conn->mb->pos += 2; if (mbuf_get_left(conn->mb) >= 2 && !memcmp(mbuf_buf(conn->mb), "\r\n", 2)) { struct mbuf mbr; conn->mb->pos += 2; mbr.buf = crlfcrlf; mbr.size = sizeof(crlfcrlf); mbr.pos = 0; mbr.end = 2; err = tcp_send(conn->tc, &mbr); if (err) break; } if (mbuf_get_left(conn->mb)) continue; conn->mb = mem_deref(conn->mb); break; } pos = conn->mb->pos; err = sip_msg_decode(&msg, conn->mb); if (err) { if (err == ENODATA) err = 0; break; } if (!msg->clen.p) { mem_deref(msg); err = EBADMSG; break; } clen = pl_u32(&msg->clen); if (mbuf_get_left(conn->mb) < clen) { conn->mb->pos = pos; mem_deref(msg); break; } tmr_start(&conn->tmr, TCP_IDLE_TIMEOUT * 1000, conn_tmr_handler, conn); end = conn->mb->end; msg->mb->end = msg->mb->pos + clen; msg->sock = mem_ref(conn); msg->src = conn->paddr; msg->dst = conn->laddr; msg->tp = conn->sc ? SIP_TRANSP_TLS : SIP_TRANSP_TCP; sip_recv(conn->sip, msg); mem_deref(msg); if (end <= conn->mb->end) { conn->mb = mem_deref(conn->mb); break; } mb = mbuf_alloc(end - conn->mb->end); if (!mb) { err = ENOMEM; goto out; } (void)mbuf_write_mem(mb, &conn->mb->buf[conn->mb->end], end - conn->mb->end); mb->pos = 0; mem_deref(conn->mb); conn->mb = mb; } out: if (err) { conn_close(conn, err); mem_deref(conn); } }
int audiosess_alloc(struct audiosess_st **stp, audiosess_int_h *inth, void *arg) { struct audiosess_st *st = NULL; struct audiosess *as = NULL; int err = 0; bool created = false; #if TARGET_OS_IPHONE AudioSessionPropertyID id = kAudioSessionProperty_AudioRouteChange; UInt32 category; OSStatus ret; #endif if (!stp) return EINVAL; #if TARGET_OS_IPHONE /* Must be done for all modules */ category = kAudioSessionCategory_PlayAndRecord; ret = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category); if (ret) { re_fprintf(stderr, "Audio Category: %d\n", ret); return EINVAL; } #endif if (gas) goto makesess; as = mem_zalloc(sizeof(*as), destructor); if (!as) return ENOMEM; #if TARGET_OS_IPHONE ret = AudioSessionSetActive(true); if (ret) { re_fprintf(stderr, "AudioSessionSetActive: %d\n", ret); err = ENOSYS; goto out; } ret = AudioSessionAddPropertyListener(id, propListener, as); if (ret) { re_fprintf(stderr, "AudioSessionAddPropertyListener: %d\n", ret); err = EINVAL; goto out; } #endif gas = as; created = true; makesess: st = mem_zalloc(sizeof(*st), sess_destructor); if (!st) { err = ENOMEM; goto out; } st->inth = inth; st->arg = arg; st->as = created ? gas : mem_ref(gas); list_append(&gas->sessl, &st->le, st); out: if (err) { mem_deref(as); mem_deref(st); } else { *stp = st; } return err; }
/** * Fork a SIP Dialog from an incoming SIP Message * * @param dlgp Pointer to allocated SIP Dialog * @param odlg Original SIP Dialog * @param msg SIP Message * * @return 0 if success, otherwise errorcode */ int sip_dialog_fork(struct sip_dialog **dlgp, struct sip_dialog *odlg, const struct sip_msg *msg) { const struct sip_hdr *contact; struct sip_dialog *dlg; struct route_enc renc; struct sip_addr addr; struct pl pl; int err; if (!dlgp || !odlg || !odlg->cpos || !msg) return EINVAL; contact = sip_msg_hdr(msg, SIP_HDR_CONTACT); if (!contact || !msg->callid.p) return EBADMSG; if (sip_addr_decode(&addr, &contact->val)) return EBADMSG; dlg = mem_zalloc(sizeof(*dlg), destructor); if (!dlg) return ENOMEM; dlg->callid = mem_ref(odlg->callid); dlg->ltag = mem_ref(odlg->ltag); dlg->lseq = odlg->lseq; dlg->rseq = msg->req ? msg->cseq.num : 0; err = pl_strdup(&dlg->uri, &addr.auri); if (err) goto out; err = pl_strdup(&dlg->rtag, msg->req ? &msg->from.tag : &msg->to.tag); if (err) goto out; dlg->mb = mbuf_alloc(512); if (!dlg->mb) { err = ENOMEM; goto out; } renc.mb = dlg->mb; renc.end = 0; err |= sip_msg_hdr_apply(msg, msg->req, SIP_HDR_RECORD_ROUTE, record_route_handler, &renc) ? ENOMEM : 0; err |= mbuf_printf(dlg->mb, "To: %r\r\n", msg->req ? &msg->from.val : &msg->to.val); odlg->mb->pos = odlg->cpos; err |= mbuf_write_mem(dlg->mb, mbuf_buf(odlg->mb), mbuf_get_left(odlg->mb)); odlg->mb->pos = 0; if (err) goto out; dlg->mb->pos = 0; if (renc.end) { pl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET; pl.l = renc.end - ROUTE_OFFSET; err = sip_addr_decode(&addr, &pl); dlg->route = addr.uri; } else { pl_set_str(&pl, dlg->uri); err = uri_decode(&dlg->route, &pl); } out: if (err) mem_deref(dlg); else *dlgp = dlg; return err; }
/** * Allocate a SIP Registration client * * @param regp Pointer to allocated SIP Registration client * @param sip SIP Stack instance * @param reg_uri SIP Request URI * @param to_uri SIP To-header URI * @param from_uri SIP From-header URI * @param expires Registration interval in [seconds] * @param cuser Contact username * @param routev Optional route vector * @param routec Number of routes * @param regid Register identification * @param authh Authentication handler * @param aarg Authentication handler argument * @param aref True to ref argument * @param resph Response handler * @param arg Response handler argument * @param params Optional Contact-header parameters * @param fmt Formatted strings with extra SIP Headers * * @return 0 if success, otherwise errorcode */ int sipreg_register(struct sipreg **regp, struct sip *sip, const char *reg_uri, const char *to_uri, const char *from_uri, uint32_t expires, const char *cuser, const char *routev[], uint32_t routec, int regid, sip_auth_h *authh, void *aarg, bool aref, sip_resp_h *resph, void *arg, const char *params, const char *fmt, ...) { struct sipreg *reg; int err; if (!regp || !sip || !reg_uri || !to_uri || !from_uri || !expires || !cuser) return EINVAL; reg = mem_zalloc(sizeof(*reg), destructor); if (!reg) return ENOMEM; err = sip_dialog_alloc(®->dlg, reg_uri, to_uri, NULL, from_uri, routev, routec); if (err) goto out; err = sip_auth_alloc(®->auth, authh, aarg, aref); if (err) goto out; err = str_dup(®->cuser, cuser); if (params) err |= str_dup(®->params, params); if (err) goto out; /* Custom SIP headers */ if (fmt) { va_list ap; reg->hdrs = mbuf_alloc(256); if (!reg->hdrs) { err = ENOMEM; goto out; } va_start(ap, fmt); err = mbuf_vprintf(reg->hdrs, fmt, ap); reg->hdrs->pos = 0; va_end(ap); if (err) goto out; } reg->sip = mem_ref(sip); reg->expires = expires; reg->resph = resph ? resph : dummy_handler; reg->arg = arg; reg->regid = regid; err = request(reg, true); if (err) goto out; out: if (err) mem_deref(reg); else *regp = reg; return err; }
static int alloc(struct menc_media **stp, struct menc_sess *sess, struct rtp_sock *rtp, int proto, void *rtpsock, void *rtcpsock, struct sdp_media *sdpm) { struct menc_st *st; const char *rattr = NULL; int layer = 10; /* above zero */ int err = 0; bool mux = (rtpsock == rtcpsock); (void)sess; (void)rtp; if (!stp || !sdpm) return EINVAL; if (proto != IPPROTO_UDP) return EPROTONOSUPPORT; st = (struct menc_st *)*stp; if (!st) { st = mem_zalloc(sizeof(*st), destructor); if (!st) return ENOMEM; st->sdpm = mem_ref(sdpm); err = sdp_media_set_alt_protos(st->sdpm, 4, "RTP/AVP", "RTP/AVPF", "RTP/SAVP", "RTP/SAVPF"); if (err) goto out; if (rtpsock) { st->rtpsock = mem_ref(rtpsock); err |= udp_register_helper(&st->uh_rtp, rtpsock, layer, send_handler, recv_handler, st); } if (rtcpsock && !mux) { st->rtcpsock = mem_ref(rtcpsock); err |= udp_register_helper(&st->uh_rtcp, rtcpsock, layer, send_handler, recv_handler, st); } if (err) goto out; /* set our preferred crypto-suite */ err |= str_dup(&st->crypto_suite, aes_cm_128_hmac_sha1_80); if (err) goto out; err = setup_srtp(st); if (err) goto out; } /* SDP handling */ if (sdp_media_rattr(st->sdpm, "crypto")) { rattr = sdp_media_rattr_apply(st->sdpm, "crypto", sdp_attr_handler, st); if (!rattr) { DEBUG_WARNING("no valid a=crypto attribute from" " remote peer\n"); } } if (!rattr) err = sdp_enc(st, sdpm, 0, st->crypto_suite); out: if (err) mem_deref(st); else *stp = (struct menc_media *)st; return err; }
int coreaudio_player_alloc(struct auplay_st **stp, struct auplay *ap, struct auplay_prm *prm, const char *device, auplay_write_h *wh, void *arg) { AudioStreamBasicDescription fmt; struct auplay_st *st; uint32_t bytc, i; OSStatus status; int err; (void)device; st = mem_zalloc(sizeof(*st), auplay_destructor); if (!st) return ENOMEM; st->ap = mem_ref(ap); st->wh = wh; st->arg = arg; err = pthread_mutex_init(&st->mutex, NULL); if (err) goto out; err = audio_session_enable(); if (err) goto out; fmt.mSampleRate = (Float64)prm->srate; fmt.mFormatID = audio_fmt(prm->fmt); fmt.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; #ifdef __BIG_ENDIAN__ fmt.mFormatFlags |= kAudioFormatFlagIsBigEndian; #endif fmt.mFramesPerPacket = 1; fmt.mBytesPerFrame = prm->ch * bytesps(prm->fmt); fmt.mBytesPerPacket = prm->ch * bytesps(prm->fmt); fmt.mChannelsPerFrame = prm->ch; fmt.mBitsPerChannel = 8*bytesps(prm->fmt); status = AudioQueueNewOutput(&fmt, play_handler, st, NULL, kCFRunLoopCommonModes, 0, &st->queue); if (status) { re_fprintf(stderr, "AudioQueueNewOutput error: %i\n", status); err = ENODEV; goto out; } bytc = prm->frame_size * bytesps(prm->fmt); for (i=0; i<ARRAY_SIZE(st->buf); i++) { status = AudioQueueAllocateBuffer(st->queue, bytc, &st->buf[i]); if (status) { err = ENOMEM; goto out; } st->buf[i]->mAudioDataByteSize = bytc; memset(st->buf[i]->mAudioData, 0, st->buf[i]->mAudioDataByteSize); (void)AudioQueueEnqueueBuffer(st->queue, st->buf[i], 0, NULL); } status = AudioQueueStart(st->queue, NULL); if (status) { re_fprintf(stderr, "AudioQueueStart error %i\n", status); err = ENODEV; goto out; } out: if (err) mem_deref(st); else *stp = st; return err; }
static void recv_handler(struct mbuf *mb, void *arg) { struct http_msg *msg = NULL; struct http_req *req = arg; size_t pos; int err; if (req->data) { if (req->datah) req->datah(mb, req->arg); return; } if (req->mb) { const size_t len = mbuf_get_left(mb); if ((mbuf_get_left(req->mb) + len) > BUFSIZE_MAX) { err = EOVERFLOW; goto out; } pos = req->mb->pos; req->mb->pos = req->mb->end; err = mbuf_write_mem(req->mb, mbuf_buf(mb), len); if (err) goto out; req->mb->pos = pos; } else { req->mb = mem_ref(mb); } pos = req->mb->pos; err = http_msg_decode(&msg, req->mb, false); if (err) { if (err == ENODATA) { req->mb->pos = pos; return; } goto out; } if (req->datah) { tmr_cancel(&req->tmr); req->data = true; if (req->resph) req->resph(0, msg, req->arg); mem_deref(msg); return; } if (mbuf_get_left(req->mb) < msg->clen) { req->mb->pos = pos; mem_deref(msg); return; } req->mb->end = req->mb->pos + msg->clen; out: req_close(req, err, msg); mem_deref(req); mem_deref(msg); }
static int alloc(struct vidsrc_st **stp, struct vidsrc *vs, struct media_ctx **mctx, struct vidsrc_prm *prm, const struct vidsz *size, const char *fmt, const char *dev, vidsrc_frame_h *frameh, vidsrc_error_h *errorh, void *arg) { #if LIBAVFORMAT_VERSION_INT < ((52<<16) + (110<<8) + 0) AVFormatParameters prms; #endif struct vidsrc_st *st; bool found_stream = false; uint32_t i; int ret, err = 0; (void)mctx; (void)errorh; if (!stp || !size || !frameh) return EINVAL; st = mem_zalloc(sizeof(*st), destructor); if (!st) return ENOMEM; st->vs = mem_ref(vs); st->app_sz = *size; st->frameh = frameh; st->arg = arg; if (prm) { st->fps = prm->fps; } else { st->fps = 25; } /* * avformat_open_input() was added in lavf 53.2.0 according to * ffmpeg/doc/APIchanges */ #if LIBAVFORMAT_VERSION_INT >= ((52<<16) + (110<<8) + 0) (void)fmt; ret = avformat_open_input(&st->ic, dev, NULL, NULL); #else /* Params */ memset(&prms, 0, sizeof(prms)); prms.time_base = (AVRational){1, st->fps}; prms.channels = 1; prms.width = size->w; prms.height = size->h; prms.pix_fmt = PIX_FMT_YUV420P; prms.channel = 0; ret = av_open_input_file(&st->ic, dev, av_find_input_format(fmt), 0, &prms); #endif if (ret < 0) { err = ENOENT; goto out; } #if LIBAVFORMAT_VERSION_INT >= ((53<<16) + (4<<8) + 0) ret = avformat_find_stream_info(st->ic, NULL); #else ret = av_find_stream_info(st->ic); #endif if (ret < 0) { warning("avformat: %s: no stream info\n", dev); err = ENOENT; goto out; } #if 0 dump_format(st->ic, 0, dev, 0); #endif for (i=0; i<st->ic->nb_streams; i++) { const struct AVStream *strm = st->ic->streams[i]; AVCodecContext *ctx = strm->codec; if (ctx->codec_type != AVMEDIA_TYPE_VIDEO) continue; debug("avformat: stream %u: %u x %u " " time_base=%d/%d\n", i, ctx->width, ctx->height, ctx->time_base.num, ctx->time_base.den); st->sz.w = ctx->width; st->sz.h = ctx->height; st->ctx = ctx; st->sindex = strm->index; if (ctx->codec_id != AV_CODEC_ID_NONE) { st->codec = avcodec_find_decoder(ctx->codec_id); if (!st->codec) { err = ENOENT; goto out; } #if LIBAVCODEC_VERSION_INT >= ((53<<16)+(8<<8)+0) ret = avcodec_open2(ctx, st->codec, NULL); #else ret = avcodec_open(ctx, st->codec); #endif if (ret < 0) { err = ENOENT; goto out; } } found_stream = true; break; } if (!found_stream) { err = ENOENT; goto out; } st->run = true; err = pthread_create(&st->thread, NULL, read_thread, st); if (err) { st->run = false; goto out; } out: if (err) mem_deref(st); else *stp = st; return err; }
/** * Send an HTTP request * * @param reqp Pointer to allocated HTTP request object * @param cli HTTP Client * @param met Request method * @param uri Request URI * @param resph Response handler * @param datah Content handler (optional) * @param arg Handler argument * @param fmt Formatted HTTP headers and body (optional) * * @return 0 if success, otherwise errorcode */ int http_request(struct http_req **reqp, struct http_cli *cli, const char *met, const char *uri, http_resp_h *resph, http_data_h *datah, void *arg, const char *fmt, ...) { struct pl scheme, host, port, path; struct http_req *req; uint16_t defport; bool secure; va_list ap; int err; if (!reqp || !cli || !met || !uri) return EINVAL; if (re_regex(uri, strlen(uri), "[a-z]+://[^:/]+[:]*[0-9]*[^]+", &scheme, &host, NULL, &port, &path) || scheme.p != uri) return EINVAL; if (!pl_strcasecmp(&scheme, "http") || !pl_strcasecmp(&scheme, "ws")) { secure = false; defport = 80; } #ifdef USE_TLS else if (!pl_strcasecmp(&scheme, "https") || !pl_strcasecmp(&scheme, "wss")) { secure = true; defport = 443; } #endif else return ENOTSUP; req = mem_zalloc(sizeof(*req), req_destructor); if (!req) return ENOMEM; req->tls = mem_ref(cli->tls); req->secure = secure; req->port = pl_isset(&port) ? pl_u32(&port) : defport; req->resph = resph; req->datah = datah; req->arg = arg; err = pl_strdup(&req->host, &host); if (err) goto out; req->mbreq = mbuf_alloc(1024); if (!req->mbreq) { err = ENOMEM; goto out; } err = mbuf_printf(req->mbreq, "%s %r HTTP/1.1\r\n" "Host: %r\r\n", met, &path, &host); if (fmt) { va_start(ap, fmt); err |= mbuf_vprintf(req->mbreq, fmt, ap); va_end(ap); } else { err |= mbuf_write_str(req->mbreq, "\r\n"); } if (err) goto out; req->mbreq->pos = 0; if (!sa_set_str(&req->srvv[0], req->host, req->port)) { req->srvc = 1; err = req_connect(req); if (err) goto out; } else { err = dnsc_query(&req->dq, cli->dnsc, req->host, DNS_TYPE_A, DNS_CLASS_IN, true, query_handler, req); if (err) goto out; } out: if (err) mem_deref(req); else { req->reqp = reqp; *reqp = req; } return err; }
/** * Align the locate/remote formats of an SDP Media line * * @param m SDP Media line * @param offer True if SDP Offer, False if SDP Answer */ void sdp_media_align_formats(struct sdp_media *m, bool offer) { struct sdp_format *rfmt, *lfmt; struct le *rle, *lle; if (!m || m->disabled || !sa_port(&m->raddr) || m->fmt_ignore) return; for (lle=m->lfmtl.head; lle; lle=lle->next) { lfmt = lle->data; lfmt->rparams = mem_deref(lfmt->rparams); lfmt->sup = false; } for (rle=m->rfmtl.head; rle; rle=rle->next) { rfmt = rle->data; for (lle=m->lfmtl.head; lle; lle=lle->next) { lfmt = lle->data; if (sdp_format_cmp(lfmt, rfmt)) break; } if (!lle) { rfmt->sup = false; continue; } mem_deref(lfmt->rparams); lfmt->rparams = mem_ref(rfmt->params); lfmt->sup = true; rfmt->sup = true; if (rfmt->ref) rfmt->data = mem_deref(rfmt->data); else rfmt->data = NULL; if (lfmt->ref) rfmt->data = mem_ref(lfmt->data); else rfmt->data = lfmt->data; rfmt->ref = lfmt->ref; if (offer) { mem_deref(lfmt->id); lfmt->id = mem_ref(rfmt->id); lfmt->pt = atoi(lfmt->id ? lfmt->id : ""); list_unlink(&lfmt->le); list_append(&m->lfmtl, &lfmt->le, lfmt); } } if (offer) { for (lle=m->lfmtl.tail; lle; ) { lfmt = lle->data; lle = lle->prev; if (!lfmt->sup) { list_unlink(&lfmt->le); list_append(&m->lfmtl, &lfmt->le, lfmt); } } } }
static int alloc_handler(struct ausrc_st **stp, const struct ausrc *as, struct media_ctx **ctx, struct ausrc_prm *prm, const char *dev, ausrc_read_h *rh, ausrc_error_h *errh, void *arg) { struct ausrc_st *st; int err; if (!stp || !as || !prm || !rh) return EINVAL; st = mem_zalloc(sizeof(*st), destructor); if (!st) return ENOMEM; st->as = as; st->rh = rh; st->errh = errh; st->arg = arg; st->mp3 = mpg123_new(NULL, &err); if (!st->mp3) { err = ENODEV; goto out; } err = mpg123_open_feed(st->mp3); if (err != MPG123_OK) { warning("rst: mpg123_open_feed: %s\n", mpg123_strerror(st->mp3)); err = ENODEV; goto out; } /* Set wanted output format */ mpg123_format_none(st->mp3); mpg123_format(st->mp3, prm->srate, prm->ch, aufmt_to_encoding(prm->fmt)); mpg123_volume(st->mp3, 0.3); st->sampc = prm->srate * prm->ch * prm->ptime / 1000; st->sampsz = aufmt_sample_size(prm->fmt); st->ptime = prm->ptime; info("rst: audio ptime=%u sampc=%zu aubuf=[%u:%u]\n", st->ptime, st->sampc, prm->srate * prm->ch * 2, prm->srate * prm->ch * 40); /* 1 - 20 seconds of audio */ err = aubuf_alloc(&st->aubuf, prm->srate * prm->ch * st->sampsz, prm->srate * prm->ch * st->sampsz * 20); if (err) goto out; if (ctx && *ctx && (*ctx)->id && !strcmp((*ctx)->id, "rst")) { st->rst = mem_ref(*ctx); } else { err = rst_alloc(&st->rst, dev); if (err) goto out; if (ctx) *ctx = (struct media_ctx *)st->rst; } rst_set_audio(st->rst, st); st->run = true; err = pthread_create(&st->thread, NULL, play_thread, st); if (err) { st->run = false; goto out; } out: if (err) mem_deref(st); else *stp = st; return err; }
/** * Allocate a new Call state object * * @param callp Pointer to allocated Call state object * @param cfg Global configuration * @param lst List of call objects * @param local_name Local display name (optional) * @param local_uri Local SIP uri * @param acc Account parameters * @param ua User-Agent * @param prm Call parameters * @param msg SIP message for incoming calls * @param xcall Optional call to inherit properties from * @param eh Call event handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int call_alloc(struct call **callp, const struct config *cfg, struct list *lst, const char *local_name, const char *local_uri, struct account *acc, struct ua *ua, const struct call_prm *prm, const struct sip_msg *msg, struct call *xcall, call_event_h *eh, void *arg) { struct call *call; enum vidmode vidmode = prm ? prm->vidmode : VIDMODE_OFF; bool use_video = true, got_offer = false; int label = 0; int err = 0; if (!cfg || !local_uri || !acc || !ua) return EINVAL; call = mem_zalloc(sizeof(*call), call_destructor); if (!call) return ENOMEM; MAGIC_INIT(call); call->config_avt = cfg->avt; tmr_init(&call->tmr_inv); call->acc = mem_ref(acc); call->ua = ua; call->state = STATE_IDLE; call->eh = eh; call->arg = arg; call->af = prm ? prm->af : AF_INET; err = str_dup(&call->local_uri, local_uri); if (local_name) err |= str_dup(&call->local_name, local_name); if (err) goto out; /* Init SDP info */ err = sdp_session_alloc(&call->sdp, net_laddr_af(call->af)); if (err) goto out; err = sdp_session_set_lattr(call->sdp, true, "tool", "baresip " BARESIP_VERSION); if (err) goto out; /* Check for incoming SDP Offer */ if (msg && mbuf_get_left(msg->mb)) got_offer = true; /* Initialise media NAT handling */ if (acc->mnat) { err = acc->mnat->sessh(&call->mnats, net_dnsc(), call->af, acc->stun_host, acc->stun_port, acc->stun_user, acc->stun_pass, call->sdp, !got_offer, mnat_handler, call); if (err) { warning("call: medianat session: %m\n", err); goto out; } } call->mnat_wait = true; /* Media encryption */ if (acc->menc) { if (acc->menc->sessh) { err = acc->menc->sessh(&call->mencs, call->sdp, !got_offer, menc_error_handler, call); if (err) { warning("call: mediaenc session: %m\n", err); goto out; } } } /* Audio stream */ err = audio_alloc(&call->audio, cfg, call, call->sdp, ++label, acc->mnat, call->mnats, acc->menc, call->mencs, acc->ptime, account_aucodecl(call->acc), audio_event_handler, audio_error_handler, call); if (err) goto out; #ifdef USE_VIDEO /* We require at least one video codec, and at least one video source or video display */ use_video = (vidmode != VIDMODE_OFF) && (list_head(account_vidcodecl(call->acc)) != NULL) && (NULL != vidsrc_find(NULL) || NULL != vidisp_find(NULL)); /* Video stream */ if (use_video) { err = video_alloc(&call->video, cfg, call, call->sdp, ++label, acc->mnat, call->mnats, acc->menc, call->mencs, "main", account_vidcodecl(call->acc), video_error_handler, call); if (err) goto out; } if (str_isset(cfg->bfcp.proto)) { err = bfcp_alloc(&call->bfcp, call->sdp, cfg->bfcp.proto, !got_offer, acc->mnat, call->mnats); if (err) goto out; } #else (void)use_video; (void)vidmode; #endif /* inherit certain properties from original call */ if (xcall) { call->not = mem_ref(xcall->not); } list_append(lst, &call->le, call); out: if (err) mem_deref(call); else if (callp) *callp = call; return err; }
static inline int hdr_add(struct sip_msg *msg, const struct pl *name, enum sip_hdrid id, const char *p, ssize_t l, bool atomic, bool line) { struct sip_hdr *hdr; int err = 0; hdr = mem_zalloc(sizeof(*hdr), hdr_destructor); if (!hdr) return ENOMEM; hdr->name = *name; hdr->val.p = p; hdr->val.l = MAX(l, 0); hdr->id = id; switch (id) { case SIP_HDR_VIA: case SIP_HDR_ROUTE: if (!atomic) break; hash_append(msg->hdrht, id, &hdr->he, mem_ref(hdr)); list_append(&msg->hdrl, &hdr->le, mem_ref(hdr)); break; default: if (atomic) hash_append(msg->hdrht, id, &hdr->he, mem_ref(hdr)); if (line) list_append(&msg->hdrl, &hdr->le, mem_ref(hdr)); break; } /* parse common headers */ switch (id) { case SIP_HDR_VIA: if (!atomic || pl_isset(&msg->via.sentby)) break; err = sip_via_decode(&msg->via, &hdr->val); break; case SIP_HDR_TO: err = sip_addr_decode((struct sip_addr *)&msg->to, &hdr->val); if (err) break; (void)msg_param_decode(&msg->to.params, "tag", &msg->to.tag); msg->to.val = hdr->val; break; case SIP_HDR_FROM: err = sip_addr_decode((struct sip_addr *)&msg->from, &hdr->val); if (err) break; (void)msg_param_decode(&msg->from.params, "tag", &msg->from.tag); msg->from.val = hdr->val; break; case SIP_HDR_CALL_ID: msg->callid = hdr->val; break; case SIP_HDR_CSEQ: err = sip_cseq_decode(&msg->cseq, &hdr->val); break; case SIP_HDR_MAX_FORWARDS: msg->maxfwd = hdr->val; break; case SIP_HDR_CONTENT_TYPE: err = msg_ctype_decode(&msg->ctyp, &hdr->val); break; case SIP_HDR_CONTENT_LENGTH: msg->clen = hdr->val; break; case SIP_HDR_EXPIRES: msg->expires = hdr->val; break; default: /* re_printf("%r = %u\n", &hdr->name, id); */ break; } mem_deref(hdr); return err; }
static void tcp_recv_handler(struct mbuf *mb_pkt, void *arg) { struct allocation *alloc = arg; int err = 0; /* re-assembly of fragments */ if (alloc->mb) { size_t pos; pos = alloc->mb->pos; alloc->mb->pos = alloc->mb->end; err = mbuf_write_mem(alloc->mb, mbuf_buf(mb_pkt), mbuf_get_left(mb_pkt)); if (err) goto out; alloc->mb->pos = pos; } else { alloc->mb = mem_ref(mb_pkt); } for (;;) { size_t len, pos, end; struct sa src; uint16_t typ; if (mbuf_get_left(alloc->mb) < 4) break; typ = ntohs(mbuf_read_u16(alloc->mb)); len = ntohs(mbuf_read_u16(alloc->mb)); if (typ < 0x4000) len += STUN_HEADER_SIZE; else if (typ < 0x8000) len += 4; else { err = EBADMSG; goto out; } alloc->mb->pos -= 4; if (mbuf_get_left(alloc->mb) < len) break; pos = alloc->mb->pos; end = alloc->mb->end; alloc->mb->end = pos + len; /* forward packet to TURN client */ err = turnc_recv(alloc->turnc, &src, alloc->mb); if (err) goto out; if (mbuf_get_left(alloc->mb)) { data_handler(alloc, &src, alloc->mb); } /* 4 byte alignment */ while (len & 0x03) ++len; alloc->mb->pos = pos + len; alloc->mb->end = end; if (alloc->mb->pos >= alloc->mb->end) { alloc->mb = mem_deref(alloc->mb); break; } } out: if (err) { alloc->alloch(err, 0, NULL, NULL, NULL, alloc->arg); } }
/** * Decode a SIP message * * @param msgp Pointer to allocated SIP Message * @param mb Buffer containing SIP Message * * @return 0 if success, otherwise errorcode */ int sip_msg_decode(struct sip_msg **msgp, struct mbuf *mb) { struct pl x, y, z, e, name; const char *p, *v, *cv; struct sip_msg *msg; bool comsep, quote; enum sip_hdrid id = SIP_HDR_NONE; uint32_t ws, lf; size_t l; int err; if (!msgp || !mb) return EINVAL; p = (const char *)mbuf_buf(mb); l = mbuf_get_left(mb); if (re_regex(p, l, "[^ \t\r\n]+ [^ \t\r\n]+ [^\r\n]*[\r]*[\n]1", &x, &y, &z, NULL, &e) || x.p != (char *)mbuf_buf(mb)) return (l > STARTLINE_MAX) ? EBADMSG : ENODATA; msg = mem_zalloc(sizeof(*msg), destructor); if (!msg) return ENOMEM; err = hash_alloc(&msg->hdrht, HDR_HASH_SIZE); if (err) goto out; msg->tag = rand_u64(); msg->mb = mem_ref(mb); msg->req = (0 == pl_strcmp(&z, "SIP/2.0")); if (msg->req) { msg->met = x; msg->ruri = y; msg->ver = z; if (uri_decode(&msg->uri, &y)) { err = EBADMSG; goto out; } } else { msg->ver = x; msg->scode = pl_u32(&y); msg->reason = z; if (!msg->scode) { err = EBADMSG; goto out; } } l -= e.p + e.l - p; p = e.p + e.l; name.p = v = cv = NULL; name.l = ws = lf = 0; comsep = false; quote = false; for (; l > 0; p++, l--) { switch (*p) { case ' ': case '\t': lf = 0; /* folding */ ++ws; break; case '\r': ++ws; break; case '\n': ++ws; if (!lf++) break; ++p; --l; /* eoh */ /*@fallthrough@*/ default: if (lf || (*p == ',' && comsep && !quote)) { if (!name.l) { err = EBADMSG; goto out; } err = hdr_add(msg, &name, id, cv ? cv : p, cv ? p - cv - ws : 0, true, cv == v && lf); if (err) goto out; if (!lf) { /* comma separated */ cv = NULL; break; } if (cv != v) { err = hdr_add(msg, &name, id, v ? v : p, v ? p - v - ws : 0, false, true); if (err) goto out; } if (lf > 1) { /* eoh */ err = 0; goto out; } comsep = false; name.p = NULL; cv = v = NULL; lf = 0; } if (!name.p) { name.p = p; name.l = 0; ws = 0; } if (!name.l) { if (*p != ':') { ws = 0; break; } name.l = MAX((int)(p - name.p - ws), 0); if (!name.l) { err = EBADMSG; goto out; } id = hdr_hash(&name); comsep = hdr_comma_separated(id); break; } if (!cv) { quote = false; cv = p; } if (!v) { v = p; } if (*p == '"') quote = !quote; ws = 0; break; } } err = ENODATA; out: if (err) mem_deref(msg); else { *msgp = msg; mb->pos = mb->end - l; } return err; }
static int alloc(struct vidsrc_st **stp, struct vidsrc *vs, struct media_ctx **ctx, struct vidsrc_prm *prm, const struct vidsz *size, const char *fmt, const char *dev, vidsrc_frame_h *frameh, vidsrc_error_h *errorh, void *arg) { struct vidsrc_st *st; int err; (void)ctx; (void)prm; (void)fmt; (void)errorh; if (!stp || !size || !frameh) return EINVAL; if (!str_isset(dev)) dev = "/dev/video0"; st = mem_zalloc(sizeof(*st), destructor); if (!st) return ENOMEM; st->vs = mem_ref(vs); st->fd = -1; st->size = *size; st->frameh = frameh; st->arg = arg; DEBUG_NOTICE("open: %s %ix%i\n", dev, size->w, size->h); err = vd_open(st, dev); if (err) goto out; v4l_get_caps(st); err = v4l_check_palette(st); if (err) goto out; err = v4l_get_win(st->fd, st->size.w, st->size.h); if (err) goto out; /* note: assumes RGB24 */ st->mb = mbuf_alloc(rgb24_size(&st->size)); if (!st->mb) { err = ENOMEM; goto out; } st->run = true; err = pthread_create(&st->thread, NULL, read_thread, st); if (err) { st->run = false; goto out; } out: if (err) mem_deref(st); else *stp = st; return err; }
static void tcp_recv_handler(struct mbuf *mb, void *arg) { struct turnserver *conn = arg; int err = 0; if (conn->mb) { size_t pos; pos = conn->mb->pos; conn->mb->pos = conn->mb->end; err = mbuf_write_mem(conn->mb, mbuf_buf(mb),mbuf_get_left(mb)); if (err) { DEBUG_WARNING("tcp: buffer write error: %m\n", err); goto out; } conn->mb->pos = pos; } else { conn->mb = mem_ref(mb); } for (;;) { size_t len, pos, end; uint16_t typ; if (mbuf_get_left(conn->mb) < 4) break; typ = ntohs(mbuf_read_u16(conn->mb)); len = ntohs(mbuf_read_u16(conn->mb)); if (len > TCP_MAX_LENGTH) { re_printf("tcp: bad length: %zu\n", len); err = EBADMSG; goto out; } if (typ < 0x4000) len += STUN_HEADER_SIZE; else if (typ < 0x8000) len += 4; else { re_printf("tcp: bad type: 0x%04x\n", typ); err = EBADMSG; goto out; } conn->mb->pos -= 4; if (mbuf_get_left(conn->mb) < len) break; pos = conn->mb->pos; end = conn->mb->end; conn->mb->end = pos + len; process_msg(conn, IPPROTO_TCP, conn->tc, &conn->paddr, conn->mb); /* 4 byte alignment */ while (len & 0x03) ++len; conn->mb->pos = pos + len; conn->mb->end = end; if (conn->mb->pos >= conn->mb->end) { conn->mb = mem_deref(conn->mb); break; } } out: if (err) { conn->mb = mem_deref(conn->mb); } }