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);
}
Example #2
0
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);
}
Example #3
0
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();
}
Example #4
0
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;
}
Example #5
0
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);
}
Example #8
0
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);
}
Example #9
0
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);
}
Example #10
0
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;
}
Example #11
0
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");
}
Example #13
0
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;
}
Example #14
0
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;
}
Example #15
0
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;
}
Example #16
0
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;
}
Example #17
0
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;
}
Example #18
0
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;
}
Example #19
0
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;
}
Example #20
0
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);
}
Example #21
0
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;
	}
}
Example #22
0
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);
	}
}
Example #23
0
/* 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);
}
Example #24
0
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");
}
Example #25
0
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);
}