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); }
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"); }