static void stun_response_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct nat_hairpinning *nh = arg; const struct stun_attr *attr; (void)reason; if (err) { nh->hph(err, false, nh->arg); return; } attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!attr) attr = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR); if (scode || !attr) { nh->hph(EBADMSG, false, nh->arg); return; } /* Send hairpinning test message */ err = hairpin_send(nh, &attr->v.sa); if (err) { DEBUG_WARNING("hairpin_send: (%m)\n", err); } if (err) nh->hph(err, false, nh->arg); }
static void stun_resp_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct icem_comp *comp = arg; struct icem *icem = comp->icem; struct stun_attr *attr; struct cand *lcand; --icem->nstun; if (err || scode > 0) { DEBUG_WARNING("{%s.%u} STUN Request failed: %m\n", icem->name, comp->id, err); goto out; } lcand = icem_cand_find(&icem->lcandl, comp->id, NULL); if (!lcand) goto out; attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!attr) attr = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR); if (!attr) { DEBUG_WARNING("no Mapped Address in Response\n"); err = EPROTO; goto out; } err = icem_lcand_add(icem, lcand->base, CAND_TYPE_SRFLX, &attr->v.sa); out: call_gather_handler(err, icem, scode, reason); }
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(); }
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; }
int turnc_keygen(struct turnc *turnc, const struct stun_msg *msg) { struct stun_attr *realm, *nonce; realm = stun_msg_attr(msg, STUN_ATTR_REALM); nonce = stun_msg_attr(msg, STUN_ATTR_NONCE); if (!realm || !nonce) return EPROTO; mem_deref(turnc->realm); mem_deref(turnc->nonce); turnc->realm = mem_ref(realm->v.realm); turnc->nonce = mem_ref(nonce->v.nonce); return md5_printf(turnc->md5_hash, "%s:%s:%s", turnc->username, turnc->realm, turnc->password); }
static void stun_response_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct sip_udpconn *uc = arg; struct stun_attr *attr; (void)reason; if (err || scode) { err = err ? err : EPROTO; goto out; } attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!attr) { attr = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR); if (!attr) { err = EPROTO; goto out; } } if (!sa_isset(&uc->maddr, SA_ALL)) { uc->maddr = attr->v.sa; } else if (!sa_cmp(&uc->maddr, &attr->v.sa, SA_ALL)) { err = ENOTCONN; goto out; } out: if (err) { udpconn_close(uc, err); mem_deref(uc); } else { tmr_start(&uc->tmr_ka, sip_keepalive_wait(uc->ka_interval), udpconn_keepalive_handler, uc); } }
static void stunc_resp_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct ice_candpair *cp = arg; struct icem *icem = cp->icem; struct stun_attr *attr; (void)reason; #if ICE_TRACE icecomp_printf(cp->comp, "Rx %H <--- %H '%u %s'%H\n", icem_cand_print, cp->lcand, icem_cand_print, cp->rcand, scode, reason, print_err, &err); #endif if (err) { icem_candpair_failed(cp, err, scode); goto out; } switch (scode) { case 0: /* Success case */ attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!attr) { DEBUG_WARNING("no XOR-MAPPED-ADDR in response\n"); icem_candpair_failed(cp, EBADMSG, 0); break; } handle_success(icem, cp, &attr->v.sa); break; case 487: /* Role Conflict */ ice_switch_local_role(icem); (void)icem_conncheck_send(cp, false, true); break; default: DEBUG_WARNING("{%s.%u} STUN Response: %u %s\n", icem->name, cp->comp->id, scode, reason); icem_candpair_failed(cp, err, scode); break; } out: pace_next(icem); }
static void stun_resp_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct candidate *cand = arg; struct stun_attr *attr = NULL; if (err || scode) { re_printf("STUN Request failed: %u %s (%m)\n", scode, reason, err); candidate_done(cand); return; } attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!attr) { re_fprintf(stderr, "no XOR-MAPPED-ADDR in response\n"); return; } stun_mapped_handler(err, &attr->v.sa, cand); }
static void refresh_resp_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct turnc *turnc = arg; struct stun_attr *ltm; if (err || turnc_request_loops(&turnc->ls, scode)) goto out; switch (scode) { case 0: ltm = stun_msg_attr(msg, STUN_ATTR_LIFETIME); if (ltm) turnc->lifetime = ltm->v.lifetime; refresh_timer(turnc); return; case 401: case 438: err = turnc_keygen(turnc, msg); if (err) break; err = refresh_request(turnc, turnc->lifetime, false, refresh_resp_handler, turnc); if (err) break; return; default: break; } out: turnc->th(err, scode, reason, NULL, NULL, msg, turnc->arg); }
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 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; }
int trice_stund_recv(struct trice *icem, struct ice_lcand *lcand, void *sock, const struct sa *src, struct stun_msg *req, size_t presz) { struct stun_attr *attr; struct pl lu, ru; bool remote_controlling; uint64_t tiebrk = 0; uint32_t prio_prflx; bool use_cand = false; int err; /* RFC 5389: Fingerprint errors are silently discarded */ err = stun_msg_chk_fingerprint(req); if (err) return err; err = stun_msg_chk_mi(req, (uint8_t *)icem->lpwd, strlen(icem->lpwd)); if (err) { DEBUG_WARNING("message-integrity failed (src=%J)\n", src); if (err == EBADMSG) goto unauth; else goto badmsg; } attr = stun_msg_attr(req, STUN_ATTR_USERNAME); if (!attr) goto badmsg; err = re_regex(attr->v.username, strlen(attr->v.username), "[^:]+:[^]+", &lu, &ru); if (err) { DEBUG_WARNING("could not parse USERNAME attribute (%s)\n", attr->v.username); goto unauth; } if (pl_strcmp(&lu, icem->lufrag)) { DEBUG_WARNING("local ufrag err (expected %s, actual %r)\n", icem->lufrag, &lu); goto unauth; } if (str_isset(icem->rufrag) && pl_strcmp(&ru, icem->rufrag)) { DEBUG_WARNING("remote ufrag err (expected %s, actual %r)\n", icem->rufrag, &ru); goto unauth; } attr = stun_msg_attr(req, STUN_ATTR_CONTROLLED); if (attr) { remote_controlling = false; tiebrk = attr->v.uint64; } attr = stun_msg_attr(req, STUN_ATTR_CONTROLLING); if (attr) { remote_controlling = true; tiebrk = attr->v.uint64; } if (remote_controlling == icem->controlling) { if (icem->tiebrk >= tiebrk) trice_switch_local_role(icem); else goto conflict; } attr = stun_msg_attr(req, STUN_ATTR_PRIORITY); if (attr) prio_prflx = attr->v.uint32; else goto badmsg; attr = stun_msg_attr(req, STUN_ATTR_USE_CAND); if (attr) use_cand = true; err = handle_stun_full(icem, lcand, sock, src, prio_prflx, use_cand); if (err) goto badmsg; trice_tracef(icem, 32, "[%u] STUNSRV: Tx success respons [%H ---> %J]\n", lcand->attr.compid, trice_cand_print, lcand, src); return stun_reply(lcand->attr.proto, sock, src, presz, req, (uint8_t *)icem->lpwd, strlen(icem->lpwd), true, 2, STUN_ATTR_XOR_MAPPED_ADDR, src, STUN_ATTR_SOFTWARE, icem->sw ? icem->sw : sw); badmsg: return stunsrv_ereply(icem, lcand, sock, src, presz, req, 400, "Bad Request"); unauth: return stunsrv_ereply(icem, lcand, sock, src, presz, req, 401, "Unauthorized"); conflict: return stunsrv_ereply(icem, lcand, sock, src, presz, req, 487, "Role Conflict"); }
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; }
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 uint32_t now = (uint32_t)time(NULL); char nstr[NONCE_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 (restund_get_ha1(user->v.username, ctx->key)) { restund_info("auth: unknown user '%s'\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; } if (stun_msg_chk_mi(msg, ctx->key, ctx->keylen)) { restund_info("auth: bad passwd for '%s'\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; } return false; unauth: if (err) { restund_warning("auth reply error: %s\n", strerror(err)); } return true; }
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_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; }
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; }
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 allocate_resp_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct stun_attr *map = NULL, *rel = NULL, *ltm, *alt; struct turnc *turnc = arg; if (err || turnc_request_loops(&turnc->ls, scode)) goto out; switch (scode) { case 0: map = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); rel = stun_msg_attr(msg, STUN_ATTR_XOR_RELAY_ADDR); ltm = stun_msg_attr(msg, STUN_ATTR_LIFETIME); if (!rel || !map) { DEBUG_WARNING("xor_mapped/relay addr attr missing\n"); err = EPROTO; break; } if (ltm) turnc->lifetime = ltm->v.lifetime; turnc->allocated = true; refresh_timer(turnc); break; case 300: if (turnc->proto == IPPROTO_TCP || turnc->proto == STUN_TRANSP_DTLS) break; alt = stun_msg_attr(msg, STUN_ATTR_ALT_SERVER); if (!alt) break; turnc->psrv = turnc->srv; turnc->srv = alt->v.alt_server; err = allocate_request(turnc); if (err) break; return; case 401: case 438: err = turnc_keygen(turnc, msg); if (err) break; err = allocate_request(turnc); if (err) break; return; default: break; } out: turnc->th(err, scode, reason, rel ? &rel->v.xor_relay_addr : NULL, map ? &map->v.xor_mapped_addr : NULL, msg, turnc->arg); }
static void stun_response_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct nat_mapping *nm = arg; struct stun_attr *map, *other; if (err) { DEBUG_WARNING("stun_response_handler: (%m)\n", err); nm->mh(err, NAT_TYPE_UNKNOWN, nm->arg); return; } switch (scode) { case 0: other = stun_msg_attr(msg, STUN_ATTR_OTHER_ADDR); map = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!map) map = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR); if (!map || !other) { DEBUG_WARNING("missing attributes: %s %s\n", map ? "" : "MAPPED-ADDR", other ? "" : "OTHER-ADDR"); nm->mh(EPROTO, NAT_TYPE_UNKNOWN, nm->arg); return; } nm->map[nm->test_phase-1] = map->v.sa; break; default: DEBUG_WARNING("Binding Error Resp: %u %s\n", scode, reason); nm->mh(EPROTO, NAT_TYPE_UNKNOWN, nm->arg); return; } switch (nm->test_phase) { case 1: /* Test I completed */ if (sa_cmp(&nm->laddr, &nm->map[0], SA_ALL)) { nm->mh(0, NAT_TYPE_ENDP_INDEP, nm->arg); return; } /* Start Test II - the client sends a Binding Request to the alternate *address* */ ++nm->test_phase; sa_set_port(&other->v.other_addr, sa_port(&nm->srv)); sa_cpy(&nm->srv, &other->v.other_addr); err = mapping_send(nm); if (err) { DEBUG_WARNING("stunc_request_send: (%m)\n", err); nm->mh(err, NAT_TYPE_UNKNOWN, nm->arg); } break; case 2: /* Test II completed */ if (sa_cmp(&nm->map[0], &nm->map[1], SA_ALL)) { nm->mh(0, NAT_TYPE_ENDP_INDEP, nm->arg); return; } /* Start Test III - the client sends a Binding Request to the alternate address and port */ ++nm->test_phase; sa_set_port(&nm->srv, sa_port(&other->v.other_addr)); err = mapping_send(nm); if (err) { DEBUG_WARNING("stunc_request_send: (%m)\n", err); nm->mh(err, NAT_TYPE_UNKNOWN, nm->arg); } break; case 3: /* Test III completed */ if (sa_cmp(&nm->map[1], &nm->map[2], SA_ALL)) { nm->mh(0, NAT_TYPE_ADDR_DEP, nm->arg); } else { nm->mh(0, NAT_TYPE_ADDR_PORT_DEP, nm->arg); } ++nm->test_phase; break; default: DEBUG_WARNING("invalid test phase %d\n", nm->test_phase); nm->mh(EINVAL, NAT_TYPE_UNKNOWN, nm->arg); break; } }
void chanbind_request(struct allocation *al, struct restund_msgctx *ctx, int proto, void *sock, const struct sa *src, const struct stun_msg *msg) { struct chan *chan = NULL, *ch_numb = NULL, *ch_peer; struct perm *perm = NULL, *permx = NULL; struct stun_attr *chnr, *peer; int err = ENOMEM, rerr; chnr = stun_msg_attr(msg, STUN_ATTR_CHANNEL_NUMBER); peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); if (!chnr || !chan_numb_valid(chnr->v.channel_number) || !peer) { restund_info("turn: bad chanbind attributes\n"); rerr = stun_ereply(proto, sock, src, 0, msg, 400, "Bad Attributes", ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); goto out; } if (sa_af(&peer->v.xor_peer_addr) != sa_af(&al->rel_addr)) { restund_info("turn: chanbind peer address family mismatch\n"); rerr = stun_ereply(proto, sock, src, 0, msg, 443, "Peer Address Family Mismatch", ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); goto out; } ch_numb = chan_numb_find(al->chans, chnr->v.channel_number); ch_peer = chan_peer_find(al->chans, &peer->v.xor_peer_addr); if (ch_numb != ch_peer) { restund_info("turn: channel %p/peer %p already bound\n", ch_numb, ch_peer); rerr = stun_ereply(proto, sock, src, 0, msg, 400, "Channel/Peer Already Bound", ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); goto out; } if (!ch_numb) { chan = chan_create(al->chans, chnr->v.channel_number, &peer->v.xor_peer_addr, al); if (!chan) { restund_info("turn: unable to create channel\n"); rerr = stun_ereply(proto, sock, src, 0, msg, 500, "Server Error", ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE,restund_software); goto out; } } permx = perm_find(al->perms, &peer->v.xor_peer_addr); if (!permx) { perm = perm_create(al->perms, &peer->v.xor_peer_addr, al); if (!perm) { restund_info("turn: unable to create permission\n"); rerr = stun_ereply(proto, sock, src, 0, msg, 500, "Server Error", ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE,restund_software); goto out; } } err = rerr = stun_reply(proto, sock, src, 0, msg, ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); out: if (rerr) restund_warning("turn: chanbind reply: %m\n", rerr); if (err) { mem_deref(chan); mem_deref(perm); } else { chan_refresh(ch_numb); perm_refresh(permx); } }
/* NOTE: this code must be fast, and not do any calculations */ 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 allocation *alloc = arg; struct allocator *allocator = alloc->allocator; struct timeval now; struct sa peer; if (err) { (void)re_fprintf(stderr, "[%u] turn error: %m\n", alloc->ix, err); alloc->err = err; goto term; } if (scode) { if (scode == 300 && is_connection_oriented(alloc) && alloc->redirc++ < REDIRC_MAX) { const struct stun_attr *alt; alt = stun_msg_attr(msg, STUN_ATTR_ALT_SERVER); if (!alt) goto term; re_printf("[%u] redirecting to new server %J\n", alloc->ix, &alt->v.alt_server); alloc->srv = alt->v.alt_server; alloc->turnc = mem_deref(alloc->turnc); alloc->tlsc = mem_deref(alloc->tlsc); alloc->tc = mem_deref(alloc->tc); alloc->dtls_sock = mem_deref(alloc->dtls_sock); alloc->us = mem_deref(alloc->us); err = start(alloc); if (err) goto term; return; } (void)re_fprintf(stderr, "[%u] turn error: %u %s\n", alloc->ix, scode, reason); alloc->err = EPROTO; goto term; } if (sa_af(relay_addr) != sa_af(mapped_addr)) { re_fprintf(stderr, "allocation: address-family mismatch" " (mapped=%J, relay=%J)\n", mapped_addr, relay_addr); err = EAFNOSUPPORT; goto term; } alloc->ok = true; alloc->relay = *relay_addr; (void)gettimeofday(&now, NULL); alloc->atime = (double)(now.tv_sec - alloc->sent.tv_sec) * 1000; alloc->atime += (double)(now.tv_usec - alloc->sent.tv_usec) / 1000; /* save information from the TURN server */ if (!allocator->server_info) { struct stun_attr *attr; allocator->server_auth = (NULL != stun_msg_attr(msg, STUN_ATTR_MSG_INTEGRITY)); attr = stun_msg_attr(msg, STUN_ATTR_SOFTWARE); if (attr) { str_ncpy(allocator->server_software, attr->v.software, sizeof(allocator->server_software)); } allocator->mapped_addr = *mapped_addr; allocator->server_info = true; attr = stun_msg_attr(msg, STUN_ATTR_LIFETIME); if (attr) { allocator->lifetime = attr->v.lifetime; } } peer = *mapped_addr; sa_set_port(&peer, sa_port(&alloc->laddr_tx)); err = set_peer(alloc, &peer); if (err) goto term; return; term: alloc->alloch(err, scode, reason, NULL, NULL, alloc->arg); }
int icem_stund_recv(struct icem_comp *comp, const struct sa *src, struct stun_msg *req, size_t presz) { struct icem *icem = comp->icem; struct ice *ice = icem->ice; struct stun_attr *attr; struct pl lu, ru; enum role rrole = ROLE_UNKNOWN; uint64_t tiebrk = 0; uint32_t prio_prflx; bool use_cand = false; int err; /* RFC 5389: Fingerprint errors are silently discarded */ err = stun_msg_chk_fingerprint(req); if (err) return err; err = stun_msg_chk_mi(req, (uint8_t *)ice->lpwd, strlen(ice->lpwd)); if (err) { if (err == EBADMSG) goto unauth; else goto badmsg; } attr = stun_msg_attr(req, STUN_ATTR_USERNAME); if (!attr) goto badmsg; err = re_regex(attr->v.username, strlen(attr->v.username), "[^:]+:[^]+", &lu, &ru); if (err) { DEBUG_WARNING("could not parse USERNAME attribute (%s)\n", attr->v.username); goto unauth; } if (pl_strcmp(&lu, ice->lufrag)) goto unauth; if (str_isset(icem->rufrag) && pl_strcmp(&ru, icem->rufrag)) goto unauth; attr = stun_msg_attr(req, STUN_ATTR_CONTROLLED); if (attr) { rrole = ROLE_CONTROLLED; tiebrk = attr->v.uint64; } attr = stun_msg_attr(req, STUN_ATTR_CONTROLLING); if (attr) { rrole = ROLE_CONTROLLING; tiebrk = attr->v.uint64; } if (rrole == ice->lrole) { if (ice->tiebrk >= tiebrk) ice_switch_local_role(ice); else goto conflict; } attr = stun_msg_attr(req, STUN_ATTR_PRIORITY); if (attr) prio_prflx = attr->v.uint32; else goto badmsg; attr = stun_msg_attr(req, STUN_ATTR_USE_CAND); if (attr) use_cand = true; err = handle_stun(ice, icem, comp, src, prio_prflx, use_cand, presz > 0); if (err) goto badmsg; return stun_reply(icem->proto, comp->sock, src, presz, req, (uint8_t *)ice->lpwd, strlen(ice->lpwd), true, 2, STUN_ATTR_XOR_MAPPED_ADDR, src, STUN_ATTR_SOFTWARE, sw); badmsg: return stunsrv_ereply(comp, src, presz, req, 400, "Bad Request"); unauth: return stunsrv_ereply(comp, src, presz, req, 401, "Unauthorized"); conflict: return stunsrv_ereply(comp, src, presz, req, 487, "Role Conflict"); }
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); }