/** * Decode SDES items from a buffer * * @param mb Buffer to decode from * @param sdes RTCP SDES to decode into * * @return 0 if success, otherwise errorcode */ int rtcp_sdes_decode(struct mbuf *mb, struct rtcp_sdes *sdes) { size_t start; if (!sdes) return EINVAL; if (mbuf_get_left(mb) < RTCP_SRC_SIZE) return EBADMSG; start = mb->pos; sdes->src = ntohl(mbuf_read_u32(mb)); /* Decode all SDES items */ while (mbuf_get_left(mb) >= RTCP_SDES_MIN_SIZE) { uint8_t type; struct rtcp_sdes_item *item; type = mbuf_read_u8(mb); if (type == RTCP_SDES_END) break; if (mbuf_get_left(mb) < 1) return EBADMSG; if (!sdes->itemv) { sdes->itemv = mem_alloc(sizeof(*sdes->itemv), NULL); if (!sdes->itemv) return ENOMEM; } else { const size_t sz = (sdes->n + 1) * sizeof(*sdes->itemv); struct rtcp_sdes_item *itemv; itemv = mem_realloc(sdes->itemv, sz); if (!itemv) return ENOMEM; sdes->itemv = itemv; } item = &sdes->itemv[sdes->n]; item->type = (enum rtcp_sdes_type)type; item->length = mbuf_read_u8(mb); if (mbuf_get_left(mb) < item->length) return EBADMSG; item->data = mem_alloc(item->length, NULL); if (!item->data) return ENOMEM; (void)mbuf_read_mem(mb, (uint8_t *)item->data, item->length); sdes->n++; } /* slurp padding */ while ((mb->pos - start) & 0x3 && mbuf_get_left(mb)) ++mb->pos; return 0; }
/** * Duplicate a null-terminated string from a memory buffer * * @param mb Memory buffer * @param strp Pointer to destination string; allocated and set * @param len Length of string * * @return 0 if success, otherwise errorcode */ int mbuf_strdup(struct mbuf *mb, char **strp, size_t len) { char *str; int err; if (!mb || !strp) return EINVAL; str = mem_alloc(len + 1, NULL); if (!str) return ENOMEM; err = mbuf_read_mem(mb, (uint8_t *)str, len); if (err) goto out; str[len] = '\0'; out: if (err) mem_deref(str); else *strp = str; return err; }
/** * Decode an RTP header from a buffer * * @param hdr RTP Header to decode into * @param mb Buffer to decode from * * @return 0 if success, otherwise errorcode */ int rtp_hdr_decode(struct rtp_header *hdr, struct mbuf *mb) { uint8_t buf[2]; int err, i; size_t header_len; if (!hdr || !mb) return EINVAL; if (mbuf_get_left(mb) < RTP_HEADER_SIZE) return EBADMSG; err = mbuf_read_mem(mb, buf, sizeof(buf)); if (err) return err; hdr->ver = (buf[0] >> 6) & 0x03; hdr->pad = (buf[0] >> 5) & 0x01; hdr->ext = (buf[0] >> 4) & 0x01; hdr->cc = (buf[0] >> 0) & 0x0f; hdr->m = (buf[1] >> 7) & 0x01; hdr->pt = (buf[1] >> 0) & 0x7f; hdr->seq = ntohs(mbuf_read_u16(mb)); hdr->ts = ntohl(mbuf_read_u32(mb)); hdr->ssrc = ntohl(mbuf_read_u32(mb)); header_len = hdr->cc*sizeof(uint32_t); if (mbuf_get_left(mb) < header_len) return EBADMSG; for (i=0; i<hdr->cc; i++) { hdr->csrc[i] = ntohl(mbuf_read_u32(mb)); } if (hdr->ext) { if (mbuf_get_left(mb) < 4) return EBADMSG; hdr->x.type = ntohs(mbuf_read_u16(mb)); hdr->x.len = ntohs(mbuf_read_u16(mb)); if (mbuf_get_left(mb) < hdr->x.len*sizeof(uint32_t)) return EBADMSG; mb->pos += hdr->x.len*sizeof(uint32_t); } return 0; }
/** * NOTE: DSP cannot be destroyed inside handler */ static bool write_handler(uint8_t *buf, size_t sz, void *arg) { struct play *play = arg; lock_write_get(play->lock); if (play->eof) goto silence; if (mbuf_get_left(play->mb) < sz) { play->eof = true; } else { (void)mbuf_read_mem(play->mb, buf, sz); } silence: if (play->eof) memset(buf, 0, sz); lock_rel(play->lock); return true; }
int stun_hdr_decode(struct mbuf *mb, struct stun_hdr *hdr) { if (!mb || !hdr) return EINVAL; if (mbuf_get_left(mb) < STUN_HEADER_SIZE) return EBADMSG; hdr->type = ntohs(mbuf_read_u16(mb)); if (hdr->type & 0xc000) return EBADMSG; hdr->len = ntohs(mbuf_read_u16(mb)); if (hdr->len & 0x3) return EBADMSG; hdr->cookie = ntohl(mbuf_read_u32(mb)); (void)mbuf_read_mem(mb, hdr->tid, sizeof(hdr->tid)); if (mbuf_get_left(mb) < hdr->len) return EBADMSG; return 0; }
/** * Read a 64-bit value from a memory buffer * * @param mb Memory buffer * * @return 64-bit value */ uint64_t mbuf_read_u64(struct mbuf *mb) { uint64_t v; return (0 == mbuf_read_mem(mb, (uint8_t *)&v, sizeof(v))) ? v : 0; }
/** * Read an 8-bit value from a memory buffer * * @param mb Memory buffer * * @return 8-bit value */ uint8_t mbuf_read_u8(struct mbuf *mb) { uint8_t v; return (0 == mbuf_read_mem(mb, &v, sizeof(v))) ? v : 0; }
int stun_attr_decode(struct stun_attr **attrp, struct mbuf *mb, const uint8_t *tid, struct stun_unknown_attr *ua) { struct stun_attr *attr; size_t start, len; uint32_t i, n; int err = 0; if (!mb || !attrp) return EINVAL; if (mbuf_get_left(mb) < 4) return EBADMSG; attr = mem_zalloc(sizeof(*attr), destructor); if (!attr) return ENOMEM; attr->type = htons(mbuf_read_u16(mb)); len = htons(mbuf_read_u16(mb)); if (mbuf_get_left(mb) < len) goto badmsg; start = mb->pos; switch (attr->type) { case STUN_ATTR_MAPPED_ADDR: case STUN_ATTR_ALT_SERVER: case STUN_ATTR_RESP_ORIGIN: case STUN_ATTR_OTHER_ADDR: tid = NULL; /*@fallthrough@*/ case STUN_ATTR_XOR_PEER_ADDR: case STUN_ATTR_XOR_RELAY_ADDR: case STUN_ATTR_XOR_MAPPED_ADDR: err = stun_addr_decode(mb, &attr->v.sa, tid); break; case STUN_ATTR_CHANGE_REQ: if (len != 4) goto badmsg; n = ntohl(mbuf_read_u32(mb)); attr->v.change_req.ip = (n >> 2) & 0x1; attr->v.change_req.port = (n >> 1) & 0x1; break; case STUN_ATTR_USERNAME: case STUN_ATTR_REALM: case STUN_ATTR_NONCE: case STUN_ATTR_SOFTWARE: err = str_decode(mb, &attr->v.str, len); break; case STUN_ATTR_MSG_INTEGRITY: if (len != 20) goto badmsg; err = mbuf_read_mem(mb, attr->v.msg_integrity, 20); break; case STUN_ATTR_ERR_CODE: if (len < 4) goto badmsg; mb->pos += 2; attr->v.err_code.code = (mbuf_read_u8(mb) & 0x7) * 100; attr->v.err_code.code += mbuf_read_u8(mb); err = str_decode(mb, &attr->v.err_code.reason, len - 4); break; case STUN_ATTR_UNKNOWN_ATTR: for (i=0; i<len/2; i++) { uint16_t type = ntohs(mbuf_read_u16(mb)); if (i >= ARRAY_SIZE(attr->v.unknown_attr.typev)) continue; attr->v.unknown_attr.typev[i] = type; attr->v.unknown_attr.typec++; } break; case STUN_ATTR_CHANNEL_NUMBER: case STUN_ATTR_RESP_PORT: if (len < 2) goto badmsg; attr->v.uint16 = ntohs(mbuf_read_u16(mb)); break; case STUN_ATTR_LIFETIME: case STUN_ATTR_PRIORITY: case STUN_ATTR_FINGERPRINT: if (len != 4) goto badmsg; attr->v.uint32 = ntohl(mbuf_read_u32(mb)); break; case STUN_ATTR_DATA: case STUN_ATTR_PADDING: attr->v.mb.buf = mem_ref(mb->buf); attr->v.mb.size = mb->size; attr->v.mb.pos = mb->pos; attr->v.mb.end = mb->pos + len; mb->pos += len; break; case STUN_ATTR_REQ_ADDR_FAMILY: case STUN_ATTR_REQ_TRANSPORT: if (len < 1) goto badmsg; attr->v.uint8 = mbuf_read_u8(mb); break; case STUN_ATTR_EVEN_PORT: if (len < 1) goto badmsg; attr->v.even_port.r = (mbuf_read_u8(mb) >> 7) & 0x1; break; case STUN_ATTR_DONT_FRAGMENT: case STUN_ATTR_USE_CAND: if (len > 0) goto badmsg; /* no value */ break; case STUN_ATTR_RSV_TOKEN: case STUN_ATTR_CONTROLLING: case STUN_ATTR_CONTROLLED: if (len != 8) goto badmsg; attr->v.uint64 = sys_ntohll(mbuf_read_u64(mb)); break; default: mb->pos += len; if (attr->type >= 0x8000) break; if (ua && ua->typec < ARRAY_SIZE(ua->typev)) ua->typev[ua->typec++] = attr->type; break; } if (err) goto error; /* padding */ while (((mb->pos - start) & 0x03) && mbuf_get_left(mb)) ++mb->pos; *attrp = attr; return 0; badmsg: err = EBADMSG; error: mem_deref(attr); return err; }
int srtp_decrypt(struct srtp *srtp, struct mbuf *mb) { struct srtp_stream *strm; struct rtp_header hdr; struct comp *comp; uint64_t ix; size_t start; int diff; int err; if (!srtp || !mb) return EINVAL; comp = &srtp->rtp; start = mb->pos; err = rtp_hdr_decode(&hdr, mb); if (err) return err; err = stream_get_seq(&strm, srtp, hdr.ssrc, hdr.seq); if (err) return err; diff = seq_diff(strm->s_l, hdr.seq); if (diff > 32768) return ETIMEDOUT; /* Roll-Over Counter (ROC) */ if (diff <= -32768) { strm->roc++; strm->s_l = 0; } ix = srtp_get_index(strm->roc, strm->s_l, hdr.seq); if (comp->hmac) { uint8_t tag_calc[SHA_DIGEST_LENGTH]; uint8_t tag_pkt[SHA_DIGEST_LENGTH]; size_t pld_start, tag_start; if (mbuf_get_left(mb) < comp->tag_len) return EBADMSG; pld_start = mb->pos; tag_start = mb->end - comp->tag_len; mb->pos = tag_start; err = mbuf_read_mem(mb, tag_pkt, comp->tag_len); if (err) return err; mb->pos = mb->end = tag_start; err = mbuf_write_u32(mb, htonl(strm->roc)); if (err) return err; mb->pos = start; err = hmac_digest(comp->hmac, tag_calc, sizeof(tag_calc), mbuf_buf(mb), mbuf_get_left(mb)); if (err) return err; mb->pos = pld_start; mb->end = tag_start; if (0 != memcmp(tag_calc, tag_pkt, comp->tag_len)) return EAUTH; /* * 3.3.2. Replay Protection * * Secure replay protection is only possible when * integrity protection is present. */ if (!srtp_replay_check(&strm->replay_rtp, ix)) return EALREADY; } if (comp->aes) { union vect128 iv; uint8_t *p = mbuf_buf(mb); srtp_iv_calc(&iv, &comp->k_s, strm->ssrc, ix); aes_set_iv(comp->aes, iv.u8); err = aes_decr(comp->aes, p, p, mbuf_get_left(mb)); if (err) return err; } if (hdr.seq > strm->s_l) strm->s_l = hdr.seq; mb->pos = start; return 0; }
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 } } }
/** * Decode one RTCP message from a buffer * * @param msgp Pointer to allocated RTCP Message * @param mb Buffer to decode from * * @return 0 for success, otherwise errorcode */ int rtcp_decode(struct rtcp_msg **msgp, struct mbuf *mb) { struct rtcp_msg *msg = NULL; size_t start, i, sz, count, rem; int err; if (!msgp) return EINVAL; if (mbuf_get_left(mb) < RTCP_HDR_SIZE) return EBADMSG; msg = mem_zalloc(sizeof(*msg), rtcp_destructor); if (!msg) return ENOMEM; start = mb->pos; /* decode and check header */ err = rtcp_hdr_decode(mb, &msg->hdr); if (err) goto out; if (msg->hdr.version != RTCP_VERSION) goto badmsg; /* check length and remaining */ rem = msg->hdr.length * sizeof(uint32_t); if (mbuf_get_left(mb) < rem) goto badmsg; count = msg->hdr.count; switch (msg->hdr.pt) { case RTCP_SR: if (mbuf_get_left(mb) < (RTCP_SRC_SIZE + RTCP_SR_SIZE)) goto badmsg; msg->r.sr.ssrc = ntohl(mbuf_read_u32(mb)); msg->r.sr.ntp_sec = ntohl(mbuf_read_u32(mb)); msg->r.sr.ntp_frac = ntohl(mbuf_read_u32(mb)); msg->r.sr.rtp_ts = ntohl(mbuf_read_u32(mb)); msg->r.sr.psent = ntohl(mbuf_read_u32(mb)); msg->r.sr.osent = ntohl(mbuf_read_u32(mb)); err = rtcp_rr_alloc(&msg->r.sr.rrv, count); if (err) goto out; for (i=0; i<count && !err; i++) err = rtcp_rr_decode(mb, &msg->r.sr.rrv[i]); break; case RTCP_RR: if (mbuf_get_left(mb) < RTCP_SRC_SIZE) goto badmsg; msg->r.rr.ssrc = ntohl(mbuf_read_u32(mb)); err = rtcp_rr_alloc(&msg->r.rr.rrv, count); if (err) goto out; for (i=0; i<count && !err; i++) err = rtcp_rr_decode(mb, &msg->r.rr.rrv[i]); break; case RTCP_SDES: if (count == 0) break; sz = count * sizeof(*msg->r.sdesv); msg->r.sdesv = mem_zalloc(sz, NULL); if (!msg->r.sdesv) { err = ENOMEM; goto out; } for (i=0; i<msg->hdr.count && !err; i++) err = rtcp_sdes_decode(mb, &msg->r.sdesv[i]); break; case RTCP_BYE: sz = count * sizeof(*msg->r.bye.srcv); msg->r.bye.srcv = mem_alloc(sz, NULL); if (!msg->r.bye.srcv) { err = ENOMEM; goto out; } if (mbuf_get_left(mb) < sz) goto badmsg; for (i=0; i<count; i++) msg->r.bye.srcv[i] = ntohl(mbuf_read_u32(mb)); /* decode reason (optional) */ if (rem > count*sizeof(uint32_t)) { const size_t len = mbuf_read_u8(mb); if (mbuf_get_left(mb) < len) goto badmsg; err = mbuf_strdup(mb, &msg->r.bye.reason, len); } break; case RTCP_APP: if (mbuf_get_left(mb) < RTCP_APP_SIZE) goto badmsg; msg->r.app.src = ntohl(mbuf_read_u32(mb)); (void)mbuf_read_mem(mb, (uint8_t *)msg->r.app.name, sizeof(msg->r.app.name)); if (rem > RTCP_APP_SIZE) { msg->r.app.data_len = rem - RTCP_APP_SIZE; msg->r.app.data = mem_alloc(msg->r.app.data_len, NULL); if (!msg->r.app.data) { err = ENOMEM; goto out; } if (mbuf_get_left(mb) < msg->r.app.data_len) goto badmsg; (void)mbuf_read_mem(mb, msg->r.app.data, msg->r.app.data_len); } break; case RTCP_FIR: if (mbuf_get_left(mb) < RTCP_FIR_SIZE) goto badmsg; msg->r.fir.ssrc = ntohl(mbuf_read_u32(mb)); break; case RTCP_NACK: if (mbuf_get_left(mb) < RTCP_NACK_SIZE) goto badmsg; msg->r.nack.ssrc = ntohl(mbuf_read_u32(mb)); msg->r.nack.fsn = ntohs(mbuf_read_u16(mb)); msg->r.nack.blp = ntohs(mbuf_read_u16(mb)); break; case RTCP_RTPFB: if (mbuf_get_left(mb) < RTCP_FB_SIZE) goto badmsg; msg->r.fb.ssrc_packet = ntohl(mbuf_read_u32(mb)); msg->r.fb.ssrc_media = ntohl(mbuf_read_u32(mb)); msg->r.fb.n = msg->hdr.length - 2; err = rtcp_rtpfb_decode(mb, msg); break; case RTCP_PSFB: if (mbuf_get_left(mb) < RTCP_FB_SIZE) goto badmsg; msg->r.fb.ssrc_packet = ntohl(mbuf_read_u32(mb)); msg->r.fb.ssrc_media = ntohl(mbuf_read_u32(mb)); msg->r.fb.n = msg->hdr.length - 2; err = rtcp_psfb_decode(mb, msg); break; default: /* unknown message type */ mbuf_advance(mb, rem); break; } if (err) goto out; /* slurp padding */ while ((mb->pos - start) & 0x3 && mbuf_get_left(mb)) ++mb->pos; out: if (err) mem_deref(msg); else *msgp = msg; return err; badmsg: mem_deref(msg); return EBADMSG; }