/** * Compare media line protocols * * @param m SDP Media line * @param proto Transport protocol * @param update Update media protocol if match is found in alternate set * * @return True if matching, False if not */ bool sdp_media_proto_cmp(struct sdp_media *m, const struct pl *proto, bool update) { unsigned i; if (!m || !proto) return false; if (!pl_strcmp(proto, m->proto)) return true; for (i=0; i<ARRAY_SIZE(m->protov); i++) { if (!m->protov[i] || pl_strcmp(proto, m->protov[i])) continue; if (update) { mem_deref(m->proto); m->proto = mem_ref(m->protov[i]); } return true; } return false; }
static int media_decode(struct sdp_media **mp, struct sdp_session *sess, bool offer, const struct pl *pl) { struct pl name, port, proto, fmtv, fmt; struct sdp_media *m; int err; if (re_regex(pl->p, pl->l, "[a-z]+ [^ ]+ [^ ]+[^]*", &name, &port, &proto, &fmtv)) return EBADMSG; m = list_ledata(*mp ? (*mp)->le.next : sess->medial.head); if (!m) { if (!offer) return EPROTO; m = sdp_media_find(sess, &name, &proto); if (!m) { err = sdp_media_radd(&m, sess, &name, &proto); if (err) return err; } else { list_unlink(&m->le); list_append(&sess->medial, &m->le, m); } } else { if (pl_strcmp(&name, m->name)) return offer ? ENOTSUP : EPROTO; if (pl_strcmp(&proto, m->proto)) return ENOTSUP; } while (!re_regex(fmtv.p, fmtv.l, " [^ ]+", &fmt)) { pl_advance(&fmtv, fmt.p + fmt.l - fmtv.p); err = sdp_format_radd(m, &fmt); if (err) return err; } m->raddr = sess->raddr; sa_set_port(&m->raddr, pl_u32(&port)); m->rdir = sess->rdir; *mp = m; return 0; }
static bool cmp_handler(struct le *le, void *arg) { struct sip_ctrans *ct = le->data; const struct sip_msg *msg = arg; if (pl_strcmp(&msg->via.branch, ct->branch)) return false; if (pl_strcmp(&msg->cseq.met, ct->met)) return false; return true; }
/** * Compare a half SIP Dialog against a SIP Message * * @param dlg SIP Dialog * @param msg SIP Message * * @return True if match, False if no match */ bool sip_dialog_cmp_half(const struct sip_dialog *dlg, const struct sip_msg *msg) { if (!dlg || !msg) return false; if (pl_strcmp(&msg->callid, dlg->callid)) return false; if (pl_strcmp(msg->req ? &msg->to.tag : &msg->from.tag, dlg->ltag)) return false; return true; }
static bool sdp_attr_handler(const char *name, const char *value, void *arg) { struct menc_st *st = arg; struct crypto c; (void)name; if (sdes_decode_crypto(&c, value)) return false; if (0 != pl_strcmp(&c.key_method, "inline")) return false; if (!cryptosuite_issupported(&c.suite)) return false; st->crypto_suite = mem_deref(st->crypto_suite); pl_strdup(&st->crypto_suite, &c.suite); if (start_crypto(st, &c.key_info)) return false; sdp_enc(st, st->sdpm, c.tag, st->crypto_suite); return true; }
static bool contact_handler(const struct sip_hdr *hdr, const struct sip_msg *msg, void *arg) { struct sipreg *reg = arg; struct sip_addr c; struct pl pval; char uri[256]; if (sip_addr_decode(&c, &hdr->val)) return false; if (re_snprintf(uri, sizeof(uri), "sip:%s@%J%s", reg->cuser, ®->laddr, sip_transp_param(reg->tp)) < 0) return false; if (pl_strcmp(&c.auri, uri)) return false; if (!sip_param_decode(&c.params, "expires", &pval)) { reg->wait = pl_u32(&pval); } else if (pl_isset(&msg->expires)) reg->wait = pl_u32(&msg->expires); else reg->wait = DEFAULT_EXPIRES; return true; }
static void sip_recv(struct sip *sip, const struct sip_msg *msg) { struct le *le = sip->lsnrl.head; while (le) { struct sip_lsnr *lsnr = le->data; le = le->next; if (msg->req != lsnr->req) continue; if (lsnr->msgh(msg, lsnr->arg)) return; } if (msg->req) { (void)re_fprintf(stderr, "unhandeled request from %J: %r %r\n", &msg->src, &msg->met, &msg->ruri); if (!pl_strcmp(&msg->met, "CANCEL")) (void)sip_reply(sip, msg, 481, "Transaction Does Not Exist"); else (void)sip_reply(sip, msg, 501, "Not Implemented"); } else { (void)re_fprintf(stderr, "unhandeled response from %J:" " %u %r (%r)\n", &msg->src, msg->scode, &msg->reason, &msg->cseq.met); } }
static bool request_handler(const struct sip_msg *msg, void *arg) { struct message *message = arg; struct ua *ua; struct le *le = message->lsnrl.head; bool hdld = false; if (pl_strcmp(&msg->met, "MESSAGE")) return false; ua = uag_find(&msg->uri.user); if (!ua) { (void)sip_treply(NULL, uag_sip(), msg, 404, "Not Found"); return true; } while (le) { struct message_lsnr *lsnr = le->data; le = le->next; handle_message(lsnr, ua, msg); hdld = true; } return hdld; }
/** * Find an SDP Media line from name and transport protocol * * @param sess SDP Session * @param name Media name * @param proto Transport protocol * @param update_proto Update media transport protocol * * @return Matching media line if found, NULL if not found */ struct sdp_media *sdp_media_find(const struct sdp_session *sess, const struct pl *name, const struct pl *proto, bool update_proto) { struct le *le; if (!sess || !name || !proto) return NULL; for (le=sess->lmedial.head; le; le=le->next) { struct sdp_media *m = le->data; if (pl_strcmp(name, m->name)) continue; if (!sdp_media_proto_cmp(m, proto, update_proto)) continue; return m; } return NULL; }
void http_init(struct httpc *app, struct request **rpp, char *str_uri) { int ok; struct request *request; struct pl pl_uri; struct url url; *rpp = NULL; pl_uri.p = NULL; str_dup((char**)&pl_uri.p, str_uri); pl_uri.l = strlen(str_uri); ok = url_decode(&url, &pl_uri); if(ok!=0) goto err_uri; request = mem_zalloc(sizeof(*request), destructor); ok = hash_alloc(&request->hdrht, HDR_HASH_SIZE); request->err_h = dummy_err; request->done_h = http_done; request->post = NULL; request->form = 0; request->www_auth.p = NULL; request->www_auth.l = 0; request->auth = NULL; request->retry = 0; pl_strdup(&request->host, &url.host); pl_strdup(&request->path, &url.path); request->secure = !pl_strcmp(&url.scheme, "https"); memcpy(&request->meth, "GET", 4); request->meth[4] = 0; if(url.port) request->port = url.port; else request->port = request->secure ? 443 : 80; DEBUG_INFO("secure: %d port %d\n", request->secure, request->port); sa_init(&request->dest, AF_INET); ok = sa_set_str(&request->dest, request->host, request->port); request->state = ok ? START : RESOLVED; request->app = app; *rpp = request; err_uri: if(pl_uri.p) mem_deref((void*)pl_uri.p); return; }
/** * Decode a pointer-length string into a SIP Via header * * @param via SIP Via header * @param pl Pointer-length string * * @return 0 for success, otherwise errorcode */ int sip_via_decode(struct sip_via *via, const struct pl *pl) { struct pl transp, host, port; int err; if (!via || !pl) return EINVAL; err = re_regex(pl->p, pl->l, "SIP[ \t\r\n]*/[ \t\r\n]*2.0[ \t\r\n]*/[ \t\r\n]*" "[A-Z]+[ \t\r\n]*[^; \t\r\n]+[ \t\r\n]*[^]*", NULL, NULL, NULL, NULL, &transp, NULL, &via->sentby, NULL, &via->params); if (err) return err; if (!pl_strcmp(&transp, "TCP")) via->tp = SIP_TRANSP_TCP; else if (!pl_strcmp(&transp, "TLS")) via->tp = SIP_TRANSP_TLS; else if (!pl_strcmp(&transp, "UDP")) via->tp = SIP_TRANSP_UDP; else via->tp = SIP_TRANSP_NONE; err = decode_hostport(&via->sentby, &host, &port); if (err) return err; sa_init(&via->addr, AF_INET); (void)sa_set(&via->addr, &host, 0); if (pl_isset(&port)) sa_set_port(&via->addr, pl_u32(&port)); via->val = *pl; return msg_param_decode(&via->params, "branch", &via->branch); }
static bool response_handler(const struct sip_msg *msg, void *arg) { struct sipsess_sock *sock = arg; if (pl_strcmp(&msg->cseq.met, "INVITE")) return false; if (msg->scode < 200 || msg->scode > 299) return false; (void)sipsess_ack_again(sock, msg); return true; }
static bool amr_octet_align(const char *fmtp) { struct pl pl, oa; if (!fmtp) return false; pl_set_str(&pl, fmtp); if (fmt_param_get(&pl, "octet-align", &oa)) return 0 == pl_strcmp(&oa, "1"); return false; }
static bool request_handler(const struct sip_msg *msg, void *arg) { struct sipsess_sock *sock = arg; if (!pl_strcmp(&msg->met, "INVITE")) { if (pl_isset(&msg->to.tag)) reinvite_handler(sock, msg); else invite_handler(sock, msg); return true; } else if (!pl_strcmp(&msg->met, "ACK")) { ack_handler(sock, msg); return true; } else if (!pl_strcmp(&msg->met, "BYE")) { bye_handler(sock, msg); return true; } else if (!pl_strcmp(&msg->met, "INFO")) { info_handler(sock, msg); return true; } else if (!pl_strcmp(&msg->met, "REFER")) { if (!pl_isset(&msg->to.tag)) return false; refer_handler(sock, msg); return true; } return false; }
static int bandwidth_decode(int32_t *bwv, const struct pl *pl) { struct pl type, bw; if (re_regex(pl->p, pl->l, "[^:]+:[0-9]+", &type, &bw)) return EBADMSG; if (!pl_strcmp(&type, "CT")) bwv[SDP_BANDWIDTH_CT] = pl_u32(&bw); else if (!pl_strcmp(&type, "AS")) bwv[SDP_BANDWIDTH_AS] = pl_u32(&bw); else if (!pl_strcmp(&type, "RS")) bwv[SDP_BANDWIDTH_RS] = pl_u32(&bw); else if (!pl_strcmp(&type, "RR")) bwv[SDP_BANDWIDTH_RR] = pl_u32(&bw); else if (!pl_strcmp(&type, "TIAS")) bwv[SDP_BANDWIDTH_TIAS] = pl_u32(&bw); return 0; }
static bool request_handler(const struct sip_msg *msg, void *arg) { struct ua *ua; (void)arg; if (pl_strcmp(&msg->met, "MESSAGE")) return false; ua = uag_find(&msg->uri.user); if (!ua) { (void)sip_treply(NULL, uag_sip(), msg, 404, "Not Found"); return true; } handle_message(ua, msg); return true; }
struct sdp_format *sdp_format_find(const struct list *lst, const struct pl *id) { struct le *le; if (!lst || !id) return NULL; for (le=lst->head; le; le=le->next) { struct sdp_format *fmt = le->data; if (pl_strcmp(id, fmt->id)) continue; return fmt; } return NULL; }
void http_request_h(struct http_conn *conn, const struct http_msg *msg, void *arg) { int err; enum app_cmd cmd; const struct http_hdr * expect_hdr; struct mbuf *mb = msg->mb; uint8_t *ret_buf; size_t ret_len; if(pl_strcmp(&msg->met, "POST")) { http_creply(conn, 405, "Method not allowed", "text/plain", "EMET"); return; } expect_hdr = http_msg_hdr(msg, HTTP_HDR_EXPECT); if(expect_hdr != NULL && version_cmp(version, &expect_hdr->val) < 0) { http_creply(conn, 417, "Expectation Failed", "text/plain", "%s", version); return; } cmd = (enum app_cmd)(hash_joaat_ci(msg->path.p, msg->path.l) & 0xfff); err = app_handle(cmd, mbuf_buf(mb), mbuf_get_left(mb), &ret_buf, &ret_len); if(err < 0) { http_creply(conn, 500, "Internal Server Error", "text/plain", "EINT"); return; } if(err > 200) { http_creply(conn, err, "Error", "text/plain", "NO", 2); return; } if(err == 0) { http_creply(conn, 200, "OK", "text/plain; charset=utf-8", "%b", ret_buf, ret_len); } else { http_creply(conn, 403, "Forbidden", "text/plain", "%b", ret_buf, ret_len); } free(ret_buf); }
static void udp_recv(const struct sa *src, struct mbuf *mb, void *arg) { struct sip_server *srv = arg; struct sip_msg *msg; int err; #if 0 re_printf("sip: %zu bytes from %J\n", mbuf_get_left(mb), src); re_printf("%b\n", mb->buf, mb->end); #endif err = sip_msg_decode(&msg, mb); if (err) { warning("selftest: sip_msg_decode: %m\n", err); return; } if (0 == pl_strcmp(&msg->met, "REGISTER")) srv->got_register_req = true; msg->sock = mem_ref(srv->us); msg->src = *src; msg->dst = srv->laddr; msg->tp = SIP_TRANSP_UDP; if (srv->terminate) err = sip_reply(srv->sip, msg, 503, "Server Error"); else err = sip_reply(srv->sip, msg, 200, "OK"); if (err) { warning("selftest: could not reply: %m\n", err); } mem_deref(msg); if (srv->terminate) re_cancel(); }
static int attr_decode(struct sdp_session *sess, struct sdp_media *m, enum sdp_dir *dir, const struct pl *pl) { struct pl name, val; int err = 0; if (re_regex(pl->p, pl->l, "[^:]+:[^]+", &name, &val)) { name = *pl; val = pl_null; } if (!pl_strcmp(&name, "fmtp")) err = attr_decode_fmtp(m, &val); else if (!pl_strcmp(&name, "inactive")) *dir = SDP_INACTIVE; else if (!pl_strcmp(&name, "recvonly")) *dir = SDP_SENDONLY; else if (!pl_strcmp(&name, "rtcp")) err = attr_decode_rtcp(m, &val); else if (!pl_strcmp(&name, "rtpmap")) err = attr_decode_rtpmap(m, &val); else if (!pl_strcmp(&name, "sendonly")) *dir = SDP_RECVONLY; else if (!pl_strcmp(&name, "sendrecv")) *dir = SDP_SENDRECV; else err = sdp_attr_add(m ? &m->rattrl : &sess->rattrl, &name, &val); return err; }
static bool find_handler(struct le *le, void *arg) { struct contact *c = le->data; return 0 == pl_strcmp(&c->addr.auri, arg); }
static void notify_handler(struct sip *sip, const struct sip_msg *msg, void *arg) { enum presence_status status = PRESENCE_CLOSED; struct presence *pres = arg; const struct sip_hdr *type_hdr, *length_hdr; struct pl pl; pres->failc = 0; type_hdr = sip_msg_hdr(msg, SIP_HDR_CONTENT_TYPE); if (!type_hdr) { length_hdr = sip_msg_hdr(msg, SIP_HDR_CONTENT_LENGTH); if (0 == pl_strcmp(&length_hdr->val, "0")) { status = PRESENCE_UNKNOWN; goto done; } } if (!type_hdr || 0 != pl_strcasecmp(&type_hdr->val, "application/pidf+xml")) { if (type_hdr) warning("presence: unsupported content-type: '%r'\n", &type_hdr->val); sip_treplyf(NULL, NULL, sip, msg, false, 415, "Unsupported Media Type", "Accept: application/pidf+xml\r\n" "Content-Length: 0\r\n" "\r\n"); return; } if (!re_regex((const char *)mbuf_buf(msg->mb), mbuf_get_left(msg->mb), "<basic>[^<]+</basic>", &pl)) { if (!pl_strcasecmp(&pl, "open")) status = PRESENCE_OPEN; } if (!re_regex((const char *)mbuf_buf(msg->mb), mbuf_get_left(msg->mb), "<rpid:away/>")) { status = PRESENCE_CLOSED; } else if (!re_regex((const char *)mbuf_buf(msg->mb), mbuf_get_left(msg->mb), "<rpid:busy/>")) { status = PRESENCE_BUSY; } else if (!re_regex((const char *)mbuf_buf(msg->mb), mbuf_get_left(msg->mb), "<rpid:on-the-phone/>")) { status = PRESENCE_BUSY; } done: (void)sip_treply(NULL, sip, msg, 200, "OK"); contact_set_presence(pres->contact, status); }
int sipevent_accept(struct sipnot **notp, struct sipevent_sock *sock, const struct sip_msg *msg, struct sip_dialog *dlg, const struct sipevent_event *event, uint16_t scode, const char *reason, uint32_t expires_min, uint32_t expires_dfl, uint32_t expires_max, const char *cuser, const char *ctype, sip_auth_h *authh, void *aarg, bool aref, sipnot_close_h *closeh, void *arg, const char *fmt, ...) { struct sipnot *not; uint32_t expires; int err; if (!notp || !sock || !msg || !scode || !reason || !expires_dfl || !expires_max || !cuser || !ctype || expires_dfl < expires_min) return EINVAL; not = mem_zalloc(sizeof(*not), destructor); if (!not) return ENOMEM; if (!pl_strcmp(&msg->met, "REFER")) { err = str_dup(¬->event, "refer"); if (err) goto out; err = re_sdprintf(¬->id, "%u", msg->cseq.num); if (err) goto out; } else { if (!event) { err = EINVAL; goto out; } err = pl_strdup(¬->event, &event->event); if (err) goto out; if (pl_isset(&event->id)) { err = pl_strdup(¬->id, &event->id); if (err) goto out; } } if (dlg) { not->dlg = mem_ref(dlg); } else { err = sip_dialog_accept(¬->dlg, msg); if (err) goto out; } hash_append(sock->ht_not, hash_joaat_str(sip_dialog_callid(not->dlg)), ¬->he, not); err = sip_auth_alloc(¬->auth, authh, aarg, aref); if (err) goto out; err = str_dup(¬->cuser, cuser); if (err) goto out; err = str_dup(¬->ctype, ctype); if (err) goto out; if (fmt) { va_list ap; va_start(ap, fmt); err = re_vsdprintf(¬->hdrs, fmt, ap); va_end(ap); if (err) goto out; } not->expires_min = expires_min; not->expires_dfl = expires_dfl; not->expires_max = expires_max; not->substate = SIPEVENT_PENDING; not->sock = mem_ref(sock); not->sip = mem_ref(sock->sip); not->closeh = closeh ? closeh : internal_close_handler; not->arg = arg; if (pl_isset(&msg->expires)) expires = pl_u32(&msg->expires); else expires = not->expires_dfl; sipnot_refresh(not, expires); err = sipnot_reply(not, msg, scode, reason); if (err) goto out; not->subscribed = true; out: if (err) mem_deref(not); else *notp = not; return err; }
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"); }
int location_update(struct list *locl, const struct sip_msg *msg, const struct sip_addr *contact, uint32_t expires) { struct location *loc, *loc_new = NULL; struct loctmp *tmp; struct pl pl; int err; if (!locl || !msg || !contact) return EINVAL; loc = list_ledata(list_apply(locl, true, cmp_handler, (void *)&contact->uri)); if (!loc) { if (expires == 0) return 0; loc = loc_new = mem_zalloc(sizeof(*loc), destructor_location); if (!loc) return ENOMEM; list_append(locl, &loc->le, loc); } else { if (!pl_strcmp(&msg->callid, loc->callid) && msg->cseq.num <= loc->cseq) return EPROTO; if (expires == 0) { loc->rm = true; return 0; } } tmp = mem_zalloc(sizeof(*tmp), destructor_loctmp); if (!tmp) { err = ENOMEM; goto out; } err = pl_strdup(&tmp->uri, &contact->auri); if (err) goto out; pl_set_str(&pl, tmp->uri); if (uri_decode(&tmp->duri, &pl)) { err = EBADMSG; goto out; } err = pl_strdup(&tmp->callid, &msg->callid); if (err) goto out; if (!msg_param_decode(&contact->params, "q", &pl)) tmp->q = pl_float(&pl); else tmp->q = 1; tmp->cseq = msg->cseq.num; tmp->expires = expires; tmp->src = msg->src; out: if (err) { mem_deref(loc_new); mem_deref(tmp); } else { mem_deref(loc->tmp); loc->tmp = tmp; } return err; }
static int version_decode(const struct pl *pl) { return pl_strcmp(pl, "0") ? ENOSYS : 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"); }
/** * Decode a SIP message * * @param msgp Pointer to allocated SIP Message * @param mb Buffer containing SIP Message * * @return 0 if success, otherwise errorcode */ int sip_msg_decode(struct sip_msg **msgp, struct mbuf *mb) { struct pl x, y, z, e, name; const char *p, *v, *cv; struct sip_msg *msg; bool comsep, quote; enum sip_hdrid id = SIP_HDR_NONE; uint32_t ws, lf; size_t l; int err; if (!msgp || !mb) return EINVAL; p = (const char *)mbuf_buf(mb); l = mbuf_get_left(mb); if (re_regex(p, l, "[^ \t\r\n]+ [^ \t\r\n]+ [^\r\n]*[\r]*[\n]1", &x, &y, &z, NULL, &e) || x.p != (char *)mbuf_buf(mb)) return (l > STARTLINE_MAX) ? EBADMSG : ENODATA; msg = mem_zalloc(sizeof(*msg), destructor); if (!msg) return ENOMEM; err = hash_alloc(&msg->hdrht, HDR_HASH_SIZE); if (err) goto out; msg->tag = rand_u64(); msg->mb = mem_ref(mb); msg->req = (0 == pl_strcmp(&z, "SIP/2.0")); if (msg->req) { msg->met = x; msg->ruri = y; msg->ver = z; if (uri_decode(&msg->uri, &y)) { err = EBADMSG; goto out; } } else { msg->ver = x; msg->scode = pl_u32(&y); msg->reason = z; if (!msg->scode) { err = EBADMSG; goto out; } } l -= e.p + e.l - p; p = e.p + e.l; name.p = v = cv = NULL; name.l = ws = lf = 0; comsep = false; quote = false; for (; l > 0; p++, l--) { switch (*p) { case ' ': case '\t': lf = 0; /* folding */ ++ws; break; case '\r': ++ws; break; case '\n': ++ws; if (!lf++) break; ++p; --l; /* eoh */ /*@fallthrough@*/ default: if (lf || (*p == ',' && comsep && !quote)) { if (!name.l) { err = EBADMSG; goto out; } err = hdr_add(msg, &name, id, cv ? cv : p, cv ? p - cv - ws : 0, true, cv == v && lf); if (err) goto out; if (!lf) { /* comma separated */ cv = NULL; break; } if (cv != v) { err = hdr_add(msg, &name, id, v ? v : p, v ? p - v - ws : 0, false, true); if (err) goto out; } if (lf > 1) { /* eoh */ err = 0; goto out; } comsep = false; name.p = NULL; cv = v = NULL; lf = 0; } if (!name.p) { name.p = p; name.l = 0; ws = 0; } if (!name.l) { if (*p != ':') { ws = 0; break; } name.l = MAX((int)(p - name.p - ws), 0); if (!name.l) { err = EBADMSG; goto out; } id = hdr_hash(&name); comsep = hdr_comma_separated(id); break; } if (!cv) { quote = false; cv = p; } if (!v) { v = p; } if (*p == '"') quote = !quote; ws = 0; break; } } err = ENODATA; out: if (err) mem_deref(msg); else { *msgp = msg; mb->pos = mb->end - l; } return err; }