static void stun_resp_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct test *test = arg; struct stun_attr *attr; (void)reason; if (err) goto out; ++test->n_resp; /* verify STUN response */ TEST_EQUALS(0, scode); TEST_EQUALS(0x0101, stun_msg_type(msg)); TEST_EQUALS(STUN_CLASS_SUCCESS_RESP, stun_msg_class(msg)); TEST_EQUALS(STUN_METHOD_BINDING, stun_msg_method(msg)); TEST_EQUALS(0, stun_msg_chk_fingerprint(msg)); attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); TEST_ASSERT(attr != NULL); test->mapped_addr = attr->v.sa; out: if (err) test->err = err; /* done */ re_cancel(); }
/** * Send a STUN response message * * @param proto Transport Protocol * @param sock Socket; UDP (struct udp_sock) or TCP (struct tcp_conn) * @param dst Destination network address * @param presz Number of bytes in preamble, if sending over TURN * @param req Matching STUN request * @param key Authentication key (optional) * @param keylen Number of bytes in authentication key * @param fp Use STUN Fingerprint attribute * @param attrc Number of attributes to encode (variable arguments) * @param ... Variable list of attribute-tuples * Each attribute has 2 arguments, attribute type and value * * @return 0 if success, otherwise errorcode */ int stun_reply(int proto, void *sock, const struct sa *dst, size_t presz, const struct stun_msg *req, const uint8_t *key, size_t keylen, bool fp, uint32_t attrc, ...) { struct mbuf *mb = NULL; int err = ENOMEM; va_list ap; if (!sock || !req) return EINVAL; mb = mbuf_alloc(256); if (!mb) goto out; va_start(ap, attrc); mb->pos = presz; err = stun_msg_vencode(mb, stun_msg_method(req), STUN_CLASS_SUCCESS_RESP, stun_msg_tid(req), NULL, key, keylen, fp, 0x00, attrc, ap); va_end(ap); if (err) goto out; mb->pos = presz; err = stun_send(proto, sock, dst, mb); out: mem_deref(mb); return err; }
static bool helper_recv_handler(struct sa *src, struct mbuf *mb, void *arg) { struct icem_comp *comp = arg; struct icem *icem = comp->icem; struct stun_msg *msg = NULL; struct stun_unknown_attr ua; const size_t start = mb->pos; #if 0 re_printf("{%d} UDP recv_helper: %u bytes from %J\n", comp->id, mbuf_get_left(mb), src); #endif if (stun_msg_decode(&msg, mb, &ua)) return false; if (STUN_METHOD_BINDING == stun_msg_method(msg)) { switch (stun_msg_class(msg)) { case STUN_CLASS_REQUEST: (void)icem_stund_recv(comp, src, msg, start); break; default: (void)stun_ctrans_recv(icem->ice->stun, msg, &ua); break; } } mem_deref(msg); return true; /* handled */ }
static void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg) { struct sip_transport *transp = arg; struct stun_unknown_attr ua; struct stun_msg *stun_msg; struct sip_msg *msg; int err; if (mb->end <= 4) return; if (!stun_msg_decode(&stun_msg, mb, &ua)) { if (stun_msg_method(stun_msg) == STUN_METHOD_BINDING) { switch (stun_msg_class(stun_msg)) { case STUN_CLASS_REQUEST: (void)stun_reply(IPPROTO_UDP, transp->sock, src, 0, stun_msg, NULL, 0, false, 2, STUN_ATTR_XOR_MAPPED_ADDR, src, STUN_ATTR_SOFTWARE, transp->sip->software); break; default: (void)stun_ctrans_recv(transp->sip->stun, stun_msg, &ua); break; } } mem_deref(stun_msg); return; } err = sip_msg_decode(&msg, mb); if (err) { (void)re_fprintf(stderr, "sip: msg decode err: %m\n", err); return; } msg->sock = mem_ref(transp->sock); msg->src = *src; msg->dst = transp->laddr; msg->tp = SIP_TRANSP_UDP; sip_recv(transp->sip, msg); mem_deref(msg); }
static bool indication_handler(struct restund_msgctx *ctx, int proto, void *sock, const struct sa *src, const struct sa *dst, const struct stun_msg *msg) { struct stun_attr *data, *peer; struct allocation *al; struct perm *perm; int err; (void)sock; (void)ctx; if (stun_msg_method(msg) != STUN_METHOD_SEND) return false; if (ctx->ua.typec > 0) return true; al = allocation_find(proto, src, dst); if (!al) return true; peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); data = stun_msg_attr(msg, STUN_ATTR_DATA); if (!peer || !data) return true; perm = perm_find(al->perms, &peer->v.xor_peer_addr); if (!perm) { ++al->dropc_tx; return true; } err = udp_send(al->rel_us, &peer->v.xor_peer_addr, &data->v.data); if (err) turnd.errc_tx++; else { const size_t bytes = mbuf_get_left(&data->v.data); perm_tx_stat(perm, bytes); turnd.bytec_tx += bytes; } return true; }
/* sock = [ struct udp_sock | struct tcp_conn ] */ bool trice_stun_process(struct trice *icem, struct ice_lcand *lcand, int proto, void *sock, const struct sa *src, struct mbuf *mb) { struct stun_msg *msg = NULL; struct stun_unknown_attr ua; size_t start = mb->pos; (void)proto; if (stun_msg_decode(&msg, mb, &ua)) { return false; /* continue recv-processing */ } if (STUN_METHOD_BINDING == stun_msg_method(msg)) { switch (stun_msg_class(msg)) { case STUN_CLASS_REQUEST: (void)trice_stund_recv(icem, lcand, sock, src, msg, start); break; default: if (icem->checklist) { (void)stun_ctrans_recv(icem->checklist->stun, msg, &ua); } else { DEBUG_NOTICE("STUN resp from %J dropped" " (no checklist)\n", src); } break; } } mem_deref(msg); return true; }
/** * Send a STUN error response * * @param proto Transport Protocol * @param sock Socket; UDP (struct udp_sock) or TCP (struct tcp_conn) * @param dst Destination network address * @param presz Number of bytes in preamble, if sending over TURN * @param req Matching STUN request * @param scode Status code * @param reason Reason string * @param key Authentication key (optional) * @param keylen Number of bytes in authentication key * @param fp Use STUN Fingerprint attribute * @param attrc Number of attributes to encode (variable arguments) * @param ... Variable list of attribute-tuples * Each attribute has 2 arguments, attribute type and value * * @return 0 if success, otherwise errorcode */ int stun_ereply(int proto, void *sock, const struct sa *dst, size_t presz, const struct stun_msg *req, uint16_t scode, const char *reason, const uint8_t *key, size_t keylen, bool fp, uint32_t attrc, ...) { struct stun_errcode ec; struct mbuf *mb = NULL; int err = ENOMEM; va_list ap; if (!sock || !req || !scode || !reason) return EINVAL; mb = mbuf_alloc(256); if (!mb) goto out; ec.code = scode; ec.reason = (char *)reason; va_start(ap, attrc); mb->pos = presz; err = stun_msg_vencode(mb, stun_msg_method(req), STUN_CLASS_ERROR_RESP, stun_msg_tid(req), &ec, key, keylen, fp, 0x00, attrc, ap); va_end(ap); if (err) goto out; mb->pos = presz; err = stun_send(proto, sock, dst, mb); out: mem_deref(mb); return err; }
static bool udp_recv_handler(struct sa *src, struct mbuf *mb, void *arg) { struct stun_attr *peer, *data; struct stun_unknown_attr ua; struct turnc *turnc = arg; struct stun_msg *msg; bool hdld = true; if (!sa_cmp(&turnc->srv, src, SA_ALL) && !sa_cmp(&turnc->psrv, src, SA_ALL)) return false; if (stun_msg_decode(&msg, mb, &ua)) { struct chan_hdr hdr; struct chan *chan; if (turnc_chan_hdr_decode(&hdr, mb)) return true; if (mbuf_get_left(mb) < hdr.len) return true; chan = turnc_chan_find_numb(turnc, hdr.nr); if (!chan) return true; *src = *turnc_chan_peer(chan); return false; } switch (stun_msg_class(msg)) { case STUN_CLASS_INDICATION: if (ua.typec > 0) break; if (stun_msg_method(msg) != STUN_METHOD_DATA) break; peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); data = stun_msg_attr(msg, STUN_ATTR_DATA); if (!peer || !data) break; *src = peer->v.xor_peer_addr; mb->pos = data->v.data.pos; mb->end = data->v.data.end; hdld = false; break; case STUN_CLASS_ERROR_RESP: case STUN_CLASS_SUCCESS_RESP: (void)stun_ctrans_recv(turnc->stun, msg, &ua); break; default: break; } mem_deref(msg); return hdld; }
static bool request_handler(struct restund_msgctx *ctx, int proto, void *sock, const struct sa *src, const struct sa *dst, const struct stun_msg *msg) { const uint16_t met = stun_msg_method(msg); struct allocation *al; int err = 0; switch (met) { case STUN_METHOD_ALLOCATE: case STUN_METHOD_REFRESH: case STUN_METHOD_CREATEPERM: case STUN_METHOD_CHANBIND: break; default: return false; } if (ctx->ua.typec > 0) { err = stun_ereply(proto, sock, src, 0, msg, 420, "Unknown Attribute", ctx->key, ctx->keylen, ctx->fp, 2, STUN_ATTR_UNKNOWN_ATTR, &ctx->ua, STUN_ATTR_SOFTWARE, restund_software); goto out; } al = allocation_find(proto, src, dst); if (!al && met != STUN_METHOD_ALLOCATE) { restund_debug("turn: allocation does not exist\n"); err = stun_ereply(proto, sock, src, 0, msg, 437, "Allocation Mismatch", ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); goto out; } if (al && al->username && ctx->key) { struct stun_attr *usr = stun_msg_attr(msg, STUN_ATTR_USERNAME); if (!usr || strcmp(usr->v.username, al->username)) { restund_debug("turn: wrong credetials\n"); err = stun_ereply(proto, sock, src, 0, msg, 441, "Wrong Credentials", ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE,restund_software); goto out; } } switch (met) { case STUN_METHOD_ALLOCATE: allocate_request(&turnd, al, ctx, proto, sock, src, dst, msg); break; case STUN_METHOD_REFRESH: refresh_request(&turnd, al, ctx, proto, sock, src, msg); break; case STUN_METHOD_CREATEPERM: createperm_request(al, ctx, proto, sock, src, msg); break; case STUN_METHOD_CHANBIND: chanbind_request(al, ctx, proto, sock, src, msg); break; } out: if (err) { restund_warning("turn reply error: %m\n", err); } return true; }
static void turnc_handler(int err, uint16_t scode, const char *reason, const struct sa *relay_addr, const struct sa *mapped_addr, const struct stun_msg *msg, void *arg) { struct candidate *cand = arg; struct ice_lcand *lcand_relay=0, *lcand_srflx=0, *base = cand->base; uint32_t prio; if (err || scode) { re_printf("TURN client error: %u %s (%m)\n", scode, reason, err); goto out; } /* check if the relayed address is of the same Address Family * as the base candidate */ if (sa_af(relay_addr) != sa_af(&base->attr.addr)) { re_printf("could not use RELAY address (AF mismatch)\n"); goto out; } if (stun_msg_method(msg) == STUN_METHOD_ALLOCATE) { re_printf("TURN allocation okay (turn-proto=%s)\n", net_proto2name(cand->turn_proto)); cand->turn_ok = true; } /* RELAY */ re_printf("adding RELAY candidate %s.%J\n", net_proto2name(base->attr.proto), relay_addr); prio = calc_prio(ICE_CAND_TYPE_RELAY, base->attr.proto, base->attr.tcptype, sa_af(&base->attr.addr), cand->turn_proto); err = trice_lcand_add(&lcand_relay, base->icem, base->attr.compid, base->attr.proto, prio, relay_addr, relay_addr, ICE_CAND_TYPE_RELAY, mapped_addr, base->attr.tcptype, base->us, LAYER_ICE); if (err) { re_fprintf(stderr, "failed to add RELAY candidate (%m)\n", err); goto out; } if (cand->turn_proto == IPPROTO_TCP) { /* NOTE: this is needed to snap up outgoing UDP-packets */ err = udp_register_helper(&cand->uh_turntcp, lcand_relay->us, LAYER_TURN, turntcp_send_handler, NULL, cand); if (err) { re_printf("helper error\n"); goto out; } } /* SRFLX */ if (cand->turn_proto == base->attr.proto) { re_printf("adding SRFLX candidate %s.%J\n", net_proto2name(base->attr.proto), mapped_addr); prio = calc_prio(ICE_CAND_TYPE_SRFLX, base->attr.proto, base->attr.tcptype, sa_af(&base->attr.addr), base->attr.proto); err = trice_lcand_add(&lcand_srflx, base->icem, base->attr.compid, base->attr.proto, prio, mapped_addr, &base->attr.addr, ICE_CAND_TYPE_SRFLX, &base->attr.addr, base->attr.tcptype, NULL, LAYER_ICE); if (err) { re_fprintf(stderr, "failed to add SRFLX" " candidate (%m)\n", err); goto out; } } err = control_send_message(cand->ag->cli, "a=candidate:%H\r\n" "a=candidate:%H\r\n" , ice_cand_attr_encode, &lcand_relay->attr, ice_cand_attr_encode, lcand_srflx ? &lcand_srflx->attr : 0); if (err) goto out; candidate_add_permissions(cand); out: candidate_done(cand); }
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; }
int turnc_recv(struct turnc *turnc, struct sa *src, struct mbuf *mb) { struct stun_attr *peer, *data; struct stun_unknown_attr ua; struct stun_msg *msg; int err = 0; if (!turnc || !src || !mb) return EINVAL; if (stun_msg_decode(&msg, mb, &ua)) { struct chan_hdr hdr; struct chan *chan; if (turnc_chan_hdr_decode(&hdr, mb)) return EBADMSG; if (mbuf_get_left(mb) < hdr.len) return EBADMSG; chan = turnc_chan_find_numb(turnc, hdr.nr); if (!chan) return EBADMSG; *src = *turnc_chan_peer(chan); return 0; } switch (stun_msg_class(msg)) { case STUN_CLASS_INDICATION: if (ua.typec > 0) { err = ENOSYS; break; } if (stun_msg_method(msg) != STUN_METHOD_DATA) { err = ENOSYS; break; } peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); data = stun_msg_attr(msg, STUN_ATTR_DATA); if (!peer || !data) { err = EPROTO; break; } *src = peer->v.xor_peer_addr; mb->pos = data->v.data.pos; mb->end = data->v.data.end; break; case STUN_CLASS_ERROR_RESP: case STUN_CLASS_SUCCESS_RESP: (void)stun_ctrans_recv(turnc->stun, msg, &ua); mb->pos = mb->end; break; default: err = ENOSYS; break; } mem_deref(msg); return err; }
static bool request_handler(struct restund_msgctx *ctx, int proto, void *sock, const struct sa *src, const struct sa *dst, const struct stun_msg *msg) { struct stun_attr *mi, *user, *realm, *nonce; const time_t now = time(NULL); char nstr[NONCE_MAX_SIZE + 1]; int err; (void)dst; if (ctx->key) return false; mi = stun_msg_attr(msg, STUN_ATTR_MSG_INTEGRITY); user = stun_msg_attr(msg, STUN_ATTR_USERNAME); realm = stun_msg_attr(msg, STUN_ATTR_REALM); nonce = stun_msg_attr(msg, STUN_ATTR_NONCE); if (!mi) { err = stun_ereply(proto, sock, src, 0, msg, 401, "Unauthorized", NULL, 0, ctx->fp, 3, STUN_ATTR_REALM, restund_realm(), STUN_ATTR_NONCE, mknonce(nstr, now, src), STUN_ATTR_SOFTWARE, restund_software); goto unauth; } if (!user || !realm || !nonce) { err = stun_ereply(proto, sock, src, 0, msg, 400, "Bad Request", NULL, 0, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); goto unauth; } if (!nonce_validate(nonce->v.nonce, now, src)) { err = stun_ereply(proto, sock, src, 0, msg, 438, "Stale Nonce", NULL, 0, ctx->fp, 3, STUN_ATTR_REALM, restund_realm(), STUN_ATTR_NONCE, mknonce(nstr, now, src), STUN_ATTR_SOFTWARE, restund_software); goto unauth; } ctx->key = mem_alloc(MD5_SIZE, NULL); if (!ctx->key) { restund_warning("auth: can't to allocate memory for MI key\n"); err = stun_ereply(proto, sock, src, 0, msg, 500, "Server Error", NULL, 0, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); goto unauth; } ctx->keylen = MD5_SIZE; if (auth.sharedsecret_length > 0 || auth.sharedsecret2_length > 0) { if (!((sharedsecret_auth_calc_ha1(user, (uint8_t*) auth.sharedsecret, auth.sharedsecret_length, ctx->key) && !stun_msg_chk_mi(msg, ctx->key, ctx->keylen)) || (sharedsecret_auth_calc_ha1(user, (uint8_t*) auth.sharedsecret2, auth.sharedsecret2_length, ctx->key) && !stun_msg_chk_mi(msg, ctx->key, ctx->keylen)))) { restund_info("auth: shared secret auth for user '%s' (%j) failed\n", user->v.username, src); err = stun_ereply(proto, sock, src, 0, msg, 401, "Unauthorized", NULL, 0, ctx->fp, 3, STUN_ATTR_REALM, restund_realm(), STUN_ATTR_NONCE, mknonce(nstr, now, src), STUN_ATTR_SOFTWARE, restund_software); goto unauth; } else { /* restund_info("auth: shared secret auth for user '%s' (%j) worked\n", user->v.username, src); */ if (STUN_METHOD_ALLOCATE == stun_msg_method(msg) && !sharedsecret_auth_check_timestamp(user, now)) { restund_info("auth: shared secret auth for user '%s' expired)\n", user->v.username); err = stun_ereply(proto, sock, src, 0, msg, 401, "Unauthorized", NULL, 0, ctx->fp, 3, STUN_ATTR_REALM, restund_realm(), STUN_ATTR_NONCE, mknonce(nstr, now, src), STUN_ATTR_SOFTWARE, restund_software); goto unauth; } } } else if (restund_get_ha1(user->v.username, ctx->key)) { restund_info("auth: unknown user '%s' (%j)\n", user->v.username, src); err = stun_ereply(proto, sock, src, 0, msg, 401, "Unauthorized", NULL, 0, ctx->fp, 3, STUN_ATTR_REALM, restund_realm(), STUN_ATTR_NONCE, mknonce(nstr, now, src), STUN_ATTR_SOFTWARE, restund_software); goto unauth; } if (stun_msg_chk_mi(msg, ctx->key, ctx->keylen)) { restund_info("auth: bad password for user '%s' (%j)\n", user->v.username, src); err = stun_ereply(proto, sock, src, 0, msg, 401, "Unauthorized", NULL, 0, ctx->fp, 3, STUN_ATTR_REALM, restund_realm(), STUN_ATTR_NONCE, mknonce(nstr, now, src), STUN_ATTR_SOFTWARE, restund_software); goto unauth; } return false; unauth: if (err) { restund_warning("auth reply error: %m\n", err); } return true; }
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); }