/** * Decode an RTCP Transport Layer Feedback Message * * @param mb Buffer to decode * @param msg RTCP Message to decode into * * @return 0 for success, otherwise errorcode */ int rtcp_rtpfb_decode(struct mbuf *mb, struct rtcp_msg *msg) { size_t i, sz; if (!msg) return EINVAL; switch (msg->hdr.count) { case RTCP_RTPFB_GNACK: sz = msg->r.fb.n * sizeof(*msg->r.fb.fci.gnackv); msg->r.fb.fci.gnackv = mem_alloc(sz, NULL); if (!msg->r.fb.fci.gnackv) return ENOMEM; if (mbuf_get_left(mb) < msg->r.fb.n * GNACK_SIZE) return EBADMSG; for (i=0; i<msg->r.fb.n; i++) { msg->r.fb.fci.gnackv[i].pid = ntohs(mbuf_read_u16(mb)); msg->r.fb.fci.gnackv[i].blp = ntohs(mbuf_read_u16(mb)); } break; default: DEBUG_NOTICE("unknown RTPFB fmt %d\n", msg->hdr.count); break; } return 0; }
/** * 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; }
static bool raw_handler(int proto, const struct sa *src, const struct sa *dst, struct mbuf *mb) { struct allocation *al; uint16_t numb, len; struct perm *perm; struct chan *chan; int err; al = allocation_find(proto, src, dst); if (!al) return false; if (mbuf_get_left(mb) < 4) return false; numb = ntohs(mbuf_read_u16(mb)); len = ntohs(mbuf_read_u16(mb)); if (mbuf_get_left(mb) < len) return false; // strip any optional padding if (len != mbuf_get_left(mb)) mbuf_set_end(mb, mb->pos + len); chan = chan_numb_find(al->chans, numb); if (!chan) return false; perm = perm_find(al->perms, chan_peer(chan)); if (!perm) { ++al->dropc_tx; return false; } err = udp_send(al->rel_us, chan_peer(chan), mb); if (err) turnd.errc_tx++; else { const size_t bytes = mbuf_get_left(mb); perm_tx_stat(perm, bytes); turnd.bytec_tx += bytes; } return true; }
int dns_dname_decode(struct mbuf *mb, char **name, size_t start) { uint32_t i = 0, loopc = 0; bool comp = false; size_t pos = 0; char buf[256]; if (!mb || !name) return EINVAL; while (mb->pos < mb->end) { uint8_t len = mb->buf[mb->pos++]; if (!len) { if (comp) mb->pos = pos; buf[i++] = '\0'; *name = mem_alloc(i, NULL); if (!*name) return ENOMEM; str_ncpy(*name, buf, i); return 0; } else if ((len & COMP_MASK) == COMP_MASK) { uint16_t offset; if (loopc++ > COMP_LOOP) break; --mb->pos; offset = ntohs(mbuf_read_u16(mb)) & OFFSET_MASK; if (!comp) { pos = mb->pos; comp = true; } mb->pos = offset + start; continue; } else if (len > mbuf_get_left(mb)) break; else if (len > sizeof(buf) - i - 2) break; if (i > 0) buf[i++] = '.'; while (len--) buf[i++] = mb->buf[mb->pos++]; } return EINVAL; }
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; }
/** * Decode the RTCP Header * * @param mb Buffer to decode from * @param hdr RTCP Header to decode into * * @return 0 for success, otherwise errorcode */ int rtcp_hdr_decode(struct mbuf *mb, struct rtcp_hdr *hdr) { uint8_t b; if (!hdr) return EINVAL; if (mbuf_get_left(mb) < RTCP_HDR_SIZE) return EBADMSG; b = mbuf_read_u8(mb); hdr->pt = mbuf_read_u8(mb); hdr->length = ntohs(mbuf_read_u16(mb)); hdr->version = (b >> 6) & 0x3; hdr->p = (b >> 5) & 0x1; hdr->count = (b >> 0) & 0x1f; return 0; }
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); } }
int pcp_option_decode(struct pcp_option **optp, struct mbuf *mb) { struct pcp_option *opt; size_t start, len; uint16_t port; int err = 0; if (!optp || !mb) return EINVAL; if (mbuf_get_left(mb) < 4) return EBADMSG; opt = mem_zalloc(sizeof(*opt), destructor); if (!opt) return ENOMEM; opt->code = mbuf_read_u8(mb); (void)mbuf_read_u8(mb); len = ntohs(mbuf_read_u16(mb)); if (mbuf_get_left(mb) < len) goto badmsg; start = mb->pos; switch (opt->code) { case PCP_OPTION_THIRD_PARTY: if (len < 16) goto badmsg; err = pcp_ipaddr_decode(mb, &opt->u.third_party); break; case PCP_OPTION_PREFER_FAILURE: /* no payload */ break; case PCP_OPTION_FILTER: if (len < 20) goto badmsg; (void)mbuf_read_u8(mb); opt->u.filter.prefix_length = mbuf_read_u8(mb); port = ntohs(mbuf_read_u16(mb)); err = pcp_ipaddr_decode(mb, &opt->u.filter.remote_peer); sa_set_port(&opt->u.filter.remote_peer, port); break; case PCP_OPTION_DESCRIPTION: err = mbuf_strdup(mb, &opt->u.description, len); break; default: mb->pos += len; (void)re_printf("pcp: ignore option code %d (len=%zu)\n", opt->code, len); break; } if (err) goto error; /* padding */ while (((mb->pos - start) & 0x03) && mbuf_get_left(mb)) ++mb->pos; *optp = opt; return 0; badmsg: err = EBADMSG; error: mem_deref(opt); return err; }
static void tcp_recv_handler(struct mbuf *mbrx, void *arg) { struct tcpconn *tc = arg; struct mbuf *mb = tc->mb; int err = 0; size_t n; next: /* frame length */ if (!tc->flen) { n = min(2 - mb->end, mbuf_get_left(mbrx)); err = mbuf_write_mem(mb, mbuf_buf(mbrx), n); if (err) goto error; mbrx->pos += n; if (mb->end < 2) return; mb->pos = 0; tc->flen = ntohs(mbuf_read_u16(mb)); mb->pos = 0; mb->end = 0; } /* content */ n = min(tc->flen - mb->end, mbuf_get_left(mbrx)); err = mbuf_write_mem(mb, mbuf_buf(mbrx), n); if (err) goto error; mbrx->pos += n; if (mb->end < tc->flen) return; mb->pos = 0; err = reply_recv(tc->dnsc, mb); if (err) goto error; /* reset tcp buffer */ tc->flen = 0; mb->pos = 0; mb->end = 0; /* more data ? */ if (mbuf_get_left(mbrx) > 0) { DEBUG_INFO("%u bytes of tcp data left\n", mbuf_get_left(mbrx)); goto next; } return; error: tcpconn_close(tc, err); }
static int reply_recv(struct dnsc *dnsc, struct mbuf *mb) { struct dns_query *q = NULL; uint32_t i, j, nv[3]; struct dnsquery dq; int err = 0; if (!dnsc || !mb) return EINVAL; dq.name = NULL; if (dns_hdr_decode(mb, &dq.hdr) || !dq.hdr.qr) { err = EBADMSG; goto out; } err = dns_dname_decode(mb, &dq.name, 0); if (err) goto out; if (mbuf_get_left(mb) < 4) { err = EBADMSG; goto out; } dq.type = ntohs(mbuf_read_u16(mb)); dq.dnsclass = ntohs(mbuf_read_u16(mb)); q = list_ledata(hash_lookup(dnsc->ht_query, hash_joaat_str_ci(dq.name), query_cmp_handler, &dq)); if (!q) { err = ENOENT; goto out; } /* try next server */ if (dq.hdr.rcode == DNS_RCODE_SRV_FAIL && q->ntx < *q->srvc) { if (!q->tc) /* try next UDP server immediately */ tmr_start(&q->tmr, 0, udp_timeout_handler, q); err = EPROTO; goto out; } nv[0] = dq.hdr.nans; nv[1] = dq.hdr.nauth; nv[2] = dq.hdr.nadd; for (i=0; i<ARRAY_SIZE(nv); i++) { for (j=0; j<nv[i]; j++) { struct dnsrr *rr = NULL; err = dns_rr_decode(mb, &rr, 0); if (err) { query_handler(q, err, NULL, NULL, NULL, NULL); mem_deref(q); goto out; } list_append(&q->rrlv[i], &rr->le_priv, rr); } } if (q->type == DNS_QTYPE_AXFR) { struct dnsrr *rrh, *rrt; rrh = list_ledata(list_head(&q->rrlv[0])); rrt = list_ledata(list_tail(&q->rrlv[0])); /* Wait for last AXFR reply with terminating SOA record */ if (dq.hdr.rcode == DNS_RCODE_OK && dq.hdr.nans > 0 && (!rrt || rrt->type != DNS_TYPE_SOA || rrh == rrt)) { DEBUG_INFO("waiting for last SOA record in reply\n"); goto out; } } query_handler(q, 0, &dq.hdr, &q->rrlv[0], &q->rrlv[1], &q->rrlv[2]); mem_deref(q); out: mem_deref(dq.name); 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); } }
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); } }
static bool shim_recv_handler(int *errp, struct mbuf *mbx, bool *estab, void *arg) { struct shim *shim = arg; int err = 0; (void)estab; /* handle re-assembly */ if (!shim->mb) { shim->mb = mbuf_alloc(1024); if (!shim->mb) { *errp = ENOMEM; return true; } } if (shim->mb) { size_t pos; pos = shim->mb->pos; shim->mb->pos = shim->mb->end; err = mbuf_write_mem(shim->mb, mbuf_buf(mbx), mbuf_get_left(mbx)); if (err) goto out; shim->mb->pos = pos; } /* extract all SHIM-frames in the TCP-stream */ for (;;) { size_t start, len, pos, end; bool hdld; start = shim->mb->pos; if (mbuf_get_left(shim->mb) < (SHIM_HDR_SIZE)) break; len = ntohs(mbuf_read_u16(shim->mb)); if (mbuf_get_left(shim->mb) < len) goto rewind; pos = shim->mb->pos; end = shim->mb->end; shim->mb->end = pos + len; ++shim->n_rx; hdld = shim->frameh(shim->mb, shim->arg); if (!hdld) { /* XXX: handle multiple frames per segment */ shim->mb->pos = pos; shim->mb->end = pos + len; mbx->pos = mbx->end = 2; err = mbuf_write_mem(mbx, mbuf_buf(shim->mb), len); if (err) goto out; mbx->pos = 2; shim->mb->pos = pos + len; shim->mb->end = end; return false; /* continue recv-handlers */ } shim->mb->pos = pos + len; shim->mb->end = end; if (shim->mb->pos >= shim->mb->end) { shim->mb = mem_deref(shim->mb); break; } continue; rewind: shim->mb->pos = start; break; } out: if (err) *errp = err; return true; /* always handled */ }
/** * 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; }
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; }
static void decode_dns_query(struct dns_server *srv, const struct sa *src, struct mbuf *mb) { struct list rrl = LIST_INIT; struct dnshdr hdr; struct le *le; char *qname = NULL; size_t start, end; uint16_t type, dnsclass; int err = 0; start = mb->pos; end = mb->end; if (dns_hdr_decode(mb, &hdr) || hdr.qr || hdr.nq != 1) { DEBUG_WARNING("unable to decode query header\n"); return; } err = dns_dname_decode(mb, &qname, start); if (err) { DEBUG_WARNING("unable to decode query name\n"); goto out; } if (mbuf_get_left(mb) < 4) { err = EBADMSG; DEBUG_WARNING("unable to decode query type/class\n"); goto out; } type = ntohs(mbuf_read_u16(mb)); dnsclass = ntohs(mbuf_read_u16(mb)); DEBUG_INFO("dnssrv: type=%s query-name='%s'\n", dns_rr_typename(type), qname); if (dnsclass == DNS_CLASS_IN) { dns_server_match(srv, &rrl, qname, type); } hdr.qr = true; hdr.tc = false; hdr.rcode = DNS_RCODE_OK; hdr.nq = 1; hdr.nans = list_count(&rrl); mb->pos = start; err = dns_hdr_encode(mb, &hdr); if (err) goto out; mb->pos = end; DEBUG_INFO("dnssrv: @@ found %u answers for %s\n", list_count(&rrl), qname); for (le = rrl.head; le; le = le->next) { struct dnsrr *rr = le->data; err = dns_rr_encode(mb, rr, 0, NULL, start); if (err) goto out; } mb->pos = start; (void)udp_send(srv->us, src, mb); out: list_clear(&rrl); mem_deref(qname); }
static void process_msg(struct turnserver *turn, int proto, void *sock, const struct sa *src, struct mbuf *mb) { struct stun_msg *msg = NULL; struct sa laddr; int err = 0; if (stun_msg_decode(&msg, mb, NULL)) { uint16_t numb, len; struct channel *chan; if (!turn->us_relay) return; ++turn->n_raw; numb = ntohs(mbuf_read_u16(mb)); len = ntohs(mbuf_read_u16(mb)); if (mbuf_get_left(mb) < len) { DEBUG_WARNING("short length: %zu < %u\n", mbuf_get_left(mb), len); } chan = find_channel_numb(turn, numb); if (!chan) { DEBUG_WARNING("channel not found: numb=%u\n", numb); return; } /* relay data from channel to peer */ (void)udp_send(turn->us_relay, &chan->peer, mb); return; } #if 0 re_printf("process: %s:%p:%J %s\n", net_proto2name(proto), sock, src, stun_method_name(stun_msg_method(msg))); #endif switch (stun_msg_method(msg)) { case STUN_METHOD_ALLOCATE: /* Max 1 allocation for now */ ++turn->n_allocate; if (turn->us_relay) { err = EALREADY; goto out; } turn->cli = *src; err = sa_set_str(&laddr, "127.0.0.1", 0); if (err) goto out; err = udp_listen(&turn->us_relay, &laddr, relay_udp_recv, turn); if (err) goto out; err = udp_local_get(turn->us_relay, &turn->relay); if (err) goto out; udp_rxbuf_presz_set(turn->us_relay, 4); err = stun_reply(proto, sock, src, 0, msg, NULL, 0, false, 2, STUN_ATTR_XOR_MAPPED_ADDR, src, STUN_ATTR_XOR_RELAY_ADDR, &turn->relay); break; case STUN_METHOD_CREATEPERM: { struct stun_attr *peer; ++turn->n_createperm; peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); TEST_ASSERT(peer != NULL); add_permission(turn, &peer->v.xor_peer_addr); /* todo: install permissions and check them */ err = stun_reply(proto, sock, src, 0, msg, NULL, 0, false, 0); } break; case STUN_METHOD_CHANBIND: { struct stun_attr *chnr, *peer; ++turn->n_chanbind; TEST_ASSERT(turn->us_relay != NULL); chnr = stun_msg_attr(msg, STUN_ATTR_CHANNEL_NUMBER); peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); if (!chnr || !peer) { DEBUG_WARNING("CHANBIND: missing chnr/peer attrib\n"); } TEST_ASSERT(turn->chanc < ARRAY_SIZE(turn->chanv)); turn->chanv[turn->chanc].nr = chnr->v.channel_number; turn->chanv[turn->chanc].peer = peer->v.xor_peer_addr; ++turn->chanc; err = stun_reply(proto, sock, src, 0, msg, NULL, 0, false, 0); } break; case STUN_METHOD_SEND: { struct stun_attr *peer, *data; ++turn->n_send; TEST_ASSERT(turn->us_relay != NULL); peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); data = stun_msg_attr(msg, STUN_ATTR_DATA); if (!peer || !data) { DEBUG_WARNING("SEND: missing peer/data attrib\n"); goto out; } /* check for valid Permission */ if (!find_permission(turn, &peer->v.xor_peer_addr)) { DEBUG_NOTICE("no permission to peer %j\n", &peer->v.xor_peer_addr); goto out; } err = udp_send(turn->us_relay, &peer->v.xor_peer_addr, &data->v.data); } break; default: DEBUG_WARNING("unknown STUN method: %s\n", stun_method_name(stun_msg_method(msg))); err = EPROTO; break; } if (err) goto out; out: if (err && stun_msg_class(msg) == STUN_CLASS_REQUEST) { (void)stun_ereply(proto, sock, src, 0, msg, 500, "Server Error", NULL, 0, false, 0); } mem_deref(msg); }