static bool udp_send_handler(int *err, struct sa *dst, struct mbuf *mb, void *arg) { struct turnc *turnc = arg; size_t pos, indlen; struct chan *chan; if (mb->pos < CHAN_HDR_SIZE) return false; chan = turnc_chan_find_peer(turnc, dst); if (chan) { struct chan_hdr hdr; hdr.nr = turnc_chan_numb(chan); hdr.len = mbuf_get_left(mb); mb->pos -= CHAN_HDR_SIZE; *err = turnc_chan_hdr_encode(&hdr, mb); mb->pos -= CHAN_HDR_SIZE; *dst = turnc->srv; return false; } indlen = stun_indlen(dst); if (mb->pos < indlen) return false; mb->pos -= indlen; pos = mb->pos; *err = stun_msg_encode(mb, STUN_METHOD_SEND, STUN_CLASS_INDICATION, sendind_tid, NULL, NULL, 0, false, 0x00, 2, STUN_ATTR_XOR_PEER_ADDR, dst, STUN_ATTR_DATA, mb); mb->pos = pos; *dst = turnc->srv; return false; }
int turnc_send(struct turnc *turnc, const struct sa *dst, struct mbuf *mb) { size_t pos, indlen; struct chan *chan; int err; if (!turnc || !dst || !mb) return EINVAL; chan = turnc_chan_find_peer(turnc, dst); if (chan) { struct chan_hdr hdr; if (mb->pos < CHAN_HDR_SIZE) return EINVAL; hdr.nr = turnc_chan_numb(chan); hdr.len = mbuf_get_left(mb); mb->pos -= CHAN_HDR_SIZE; pos = mb->pos; err = turnc_chan_hdr_encode(&hdr, mb); if (err) return err; if (turnc->proto == IPPROTO_TCP) { mb->pos = mb->end; /* padding */ while (hdr.len++ & 0x03) { err = mbuf_write_u8(mb, 0x00); if (err) return err; } } mb->pos = pos; } else { indlen = stun_indlen(dst); if (mb->pos < indlen) return EINVAL; mb->pos -= indlen; pos = mb->pos; err = stun_msg_encode(mb, STUN_METHOD_SEND, STUN_CLASS_INDICATION, sendind_tid, NULL, NULL, 0, false, 0x00, 2, STUN_ATTR_XOR_PEER_ADDR, dst, STUN_ATTR_DATA, mb); if (err) return err; mb->pos = pos; } switch (turnc->proto) { case IPPROTO_UDP: err = udp_send(turnc->sock, &turnc->srv, mb); break; case IPPROTO_TCP: err = tcp_send(turnc->sock, mb); break; #ifdef USE_DTLS case STUN_TRANSP_DTLS: err = dtls_send(turnc->sock, mb); break; #endif default: err = EPROTONOSUPPORT; break; } return err; }
int test_stun_req(void) { struct stun_msg *msg = NULL; struct mbuf *mb; struct stun_attr *attr; int err; mb = mbuf_alloc(1024); if (!mb) { err = ENOMEM; goto out; } err = stun_msg_encode(mb, STUN_METHOD_BINDING, STUN_CLASS_REQUEST, tid, NULL, (uint8_t *)password.p, password.l, true, 0x20, 4, STUN_ATTR_SOFTWARE, client_sw, STUN_ATTR_PRIORITY, &ice_prio, STUN_ATTR_CONTROLLED, &ice_contr, STUN_ATTR_USERNAME, username); if (err) goto out; TEST_MEMCMP(req, sizeof(req)-1, mb->buf, mb->end); /* Decode STUN message */ mb->pos = 0; err = stun_msg_decode(&msg, mb, NULL); if (err) goto out; if (STUN_CLASS_REQUEST != stun_msg_class(msg)) goto bad; if (STUN_METHOD_BINDING != stun_msg_method(msg)) goto out; err = stun_msg_chk_mi(msg, (uint8_t *)password.p, password.l); if (err) goto out; err = stun_msg_chk_fingerprint(msg); if (err) goto out; attr = stun_msg_attr(msg, STUN_ATTR_PRIORITY); if (!attr || ice_prio != attr->v.priority) goto bad; attr = stun_msg_attr(msg, STUN_ATTR_CONTROLLED); if (!attr || ice_contr != attr->v.controlled) goto bad; attr = stun_msg_attr(msg, STUN_ATTR_USERNAME); if (!attr || strcmp(username, attr->v.username)) goto bad; attr = stun_msg_attr(msg, STUN_ATTR_SOFTWARE); if (!attr || strcmp(client_sw, attr->v.software)) goto bad; goto out; bad: err = EBADMSG; out: mem_deref(msg); mem_deref(mb); return err; }
static int test_stun_req_attributes(void) { struct stun_msg *msg = NULL; struct mbuf *mb; struct stun_attr *attr; const uint64_t rsv_token = 0x1100c0ffee; const uint32_t lifetime = 3600; const uint16_t chan = 0x4000; const uint8_t req_addr_fam = AF_INET; int err; mb = mbuf_alloc(1024); if (!mb) { err = ENOMEM; goto out; } err = stun_msg_encode(mb, STUN_METHOD_BINDING, STUN_CLASS_REQUEST, tid, NULL, NULL, 0, false, 0x00, 4, STUN_ATTR_REQ_ADDR_FAMILY, &req_addr_fam, STUN_ATTR_CHANNEL_NUMBER, &chan, STUN_ATTR_LIFETIME, &lifetime, STUN_ATTR_RSV_TOKEN, &rsv_token); if (err) goto out; /* Decode STUN message */ mb->pos = 0; err = stun_msg_decode(&msg, mb, NULL); if (err) goto out; TEST_EQUALS(STUN_CLASS_REQUEST, stun_msg_class(msg)); TEST_EQUALS(STUN_METHOD_BINDING, stun_msg_method(msg)); /* verify integer attributes of different sizes */ /* 8-bit */ attr = stun_msg_attr(msg, STUN_ATTR_REQ_ADDR_FAMILY); TEST_ASSERT(attr != NULL); TEST_EQUALS(req_addr_fam, attr->v.req_addr_family); /* 16-bit */ attr = stun_msg_attr(msg, STUN_ATTR_CHANNEL_NUMBER); TEST_ASSERT(attr != NULL); TEST_EQUALS(chan, attr->v.channel_number); /* 32-bit */ attr = stun_msg_attr(msg, STUN_ATTR_LIFETIME); TEST_ASSERT(attr != NULL); TEST_EQUALS(lifetime, attr->v.lifetime); /* 64-bit */ attr = stun_msg_attr(msg, STUN_ATTR_RSV_TOKEN); TEST_ASSERT(attr != NULL); TEST_EQUALS(rsv_token, attr->v.rsv_token); out: mem_deref(msg); mem_deref(mb); return err; }
int test_stun_reqltc(void) { struct stun_msg *msg = NULL; struct stun_attr *attr; struct mbuf *mb; uint8_t md5_hash[MD5_SIZE]; int r, err; mb = mbuf_alloc(1024); if (!mb) { err = ENOMEM; goto out; } /* use long-term credentials */ err = md5_printf(md5_hash, "%s:%s:%s", username_ltc, realm_ltc, password_ltc); if (err) goto out; err = stun_msg_encode(mb, STUN_METHOD_BINDING, STUN_CLASS_REQUEST, tid_ltc, NULL, md5_hash, sizeof(md5_hash), false, 0x00, 3, STUN_ATTR_USERNAME, username_ltc, STUN_ATTR_NONCE, nonce_ltc, STUN_ATTR_REALM, realm_ltc); if (err) goto out; r = memcmp(mb->buf, reqltc, mb->end); if ((sizeof(reqltc)-1) != mb->end || 0 != r) { err = EBADMSG; DEBUG_WARNING("compare failed (r=%d)\n", r); (void)re_printf("msg: [%02w]\n", mb->buf, mb->end); (void)re_printf("ref: [%02w]\n", reqltc, sizeof(reqltc)-1); goto out; } /* Decode STUN message */ mb->pos = 0; err = stun_msg_decode(&msg, mb, NULL); if (err) goto out; if (STUN_CLASS_REQUEST != stun_msg_class(msg)) goto bad; if (STUN_METHOD_BINDING != stun_msg_method(msg)) goto bad; err = stun_msg_chk_mi(msg, md5_hash, sizeof(md5_hash)); if (err) goto out; if (EPROTO != stun_msg_chk_fingerprint(msg)) goto bad; attr = stun_msg_attr(msg, STUN_ATTR_USERNAME); if (!attr || strcmp(username_ltc, attr->v.username)) goto bad; attr = stun_msg_attr(msg, STUN_ATTR_NONCE); if (!attr || strcmp(nonce_ltc, attr->v.nonce)) goto bad; attr = stun_msg_attr(msg, STUN_ATTR_REALM); if (!attr || strcmp(realm_ltc, attr->v.realm)) goto bad; goto out; bad: err = EBADMSG; out: mem_deref(msg); mem_deref(mb); return err; }
static int test_resp(const struct pl *resp, const struct sa *addr) { struct stun_msg *msg = NULL; struct stun_attr *attr; struct mbuf *mb = NULL; int err; mb = mbuf_alloc(1024); if (!mb) { err = ENOMEM; goto out; } err = stun_msg_encode(mb, STUN_METHOD_BINDING, STUN_CLASS_SUCCESS_RESP, tid, NULL, (uint8_t *)password.p, password.l, true, 0x20, 2, STUN_ATTR_SOFTWARE, server_sw, STUN_ATTR_XOR_MAPPED_ADDR, addr); if (err) goto out; if (resp->l != mb->end || 0 != memcmp(mb->buf, resp->p, mb->end)) { err = EBADMSG; DEBUG_WARNING("compare failed (%J)\n", addr); (void)re_printf("msg: [%02w]\n", mb->buf, mb->end); (void)re_printf("ref: [%02w]\n", resp->p, resp->l); goto out; } /* Decode STUN message */ mb->pos = 0; err = stun_msg_decode(&msg, mb, NULL); if (err) goto out; if (STUN_CLASS_SUCCESS_RESP != stun_msg_class(msg)) goto bad; if (STUN_METHOD_BINDING != stun_msg_method(msg)) goto bad; err = stun_msg_chk_mi(msg, (uint8_t *)password.p, password.l); if (err) goto out; err = stun_msg_chk_fingerprint(msg); if (err) goto out; attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!attr || !sa_cmp(&attr->v.xor_mapped_addr, addr, SA_ALL)) goto bad; attr = stun_msg_attr(msg, STUN_ATTR_SOFTWARE); if (!attr || strcmp(server_sw, attr->v.software)) goto bad; goto out; bad: err = EBADMSG; out: mem_deref(msg); mem_deref(mb); return err; }