int conf_get_vidsz(const struct conf *conf, const char *name, struct vidsz *sz) { struct pl r, w, h; int err; err = conf_get(conf, name, &r); if (err) return err; w.l = h.l = 0; err = re_regex(r.p, r.l, "[0-9]+x[0-9]+", &w, &h); if (err) return err; if (pl_isset(&w) && pl_isset(&h)) { sz->w = pl_u32(&w); sz->h = pl_u32(&h); } /* check resolution */ if (sz->w & 0x1 || sz->h & 0x1) { warning("conf: %s: should be multiple of 2 (%u x %u)\n", name, sz->w, sz->h); return EINVAL; } return 0; }
static int html_print_cmd(struct re_printf *pf, const struct http_msg *req) { struct pl params; if (!pf || !req) return EINVAL; if (pl_isset(&req->prm)) { params.p = req->prm.p + 1; params.l = req->prm.l - 1; } else { params.p = "h"; params.l = 1; } return re_hprintf(pf, "%H" "<body>\n" "<pre>\n" "%H" "</pre>\n" "</body>\n" "</html>\n", html_print_head, NULL, ui_input_pl, ¶ms); }
/** * Print a HTTP Message * * @param pf Print function for output * @param msg HTTP Message * * @return 0 if success, otherwise errorcode */ int http_msg_print(struct re_printf *pf, const struct http_msg *msg) { struct le *le; int err; if (!msg) return 0; if (pl_isset(&msg->met)) err = re_hprintf(pf, "%r %r%r HTTP/%r\n", &msg->met, &msg->path, &msg->prm, &msg->ver); else err = re_hprintf(pf, "HTTP/%r %u %r\n", &msg->ver, msg->scode, &msg->reason); for (le=msg->hdrl.head; le; le=le->next) { const struct http_hdr *hdr = le->data; err |= re_hprintf(pf, "%r: %r (%i)\n", &hdr->name, &hdr->val, hdr->id); } return err; }
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 uint32_t wait_term(const struct sipevent_substate *substate) { uint32_t wait; switch (substate->reason) { case SIPEVENT_DEACTIVATED: case SIPEVENT_TIMEOUT: wait = 5; break; case SIPEVENT_REJECTED: case SIPEVENT_NORESOURCE: wait = 3600; break; case SIPEVENT_PROBATION: case SIPEVENT_GIVEUP: default: wait = 300; if (pl_isset(&substate->retry_after)) wait = max(wait, pl_u32(&substate->retry_after)); break; } return wait; }
int test_sdp_extmap(void) { static const char *extmapv[3] = { "extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level", "extmap:2/sendrecv http://example.com/ext.htm#xmeta short", "extmap:4096/recvonly URI-gps-string" }; struct sdp_extmap ext; int err = 0; /* extmap 1 */ err = sdp_extmap_decode(&ext, extmapv[0]); TEST_EQUALS(0, err); TEST_STRCMP("urn:ietf:params:rtp-hdrext:ssrc-audio-level", strlen("urn:ietf:params:rtp-hdrext:ssrc-audio-level"), ext.name.p, ext.name.l); TEST_ASSERT(!pl_isset(&ext.attrs)); TEST_EQUALS(SDP_SENDRECV, ext.dir); TEST_ASSERT(!ext.dir_set); TEST_EQUALS(1, ext.id); /* extmap 2 */ err = sdp_extmap_decode(&ext, extmapv[1]); TEST_EQUALS(0, err); TEST_STRCMP("http://example.com/ext.htm#xmeta", strlen("http://example.com/ext.htm#xmeta"), ext.name.p, ext.name.l); TEST_STRCMP("short", strlen("short"), ext.attrs.p, ext.attrs.l); TEST_EQUALS(SDP_SENDRECV, ext.dir); TEST_ASSERT(ext.dir_set); TEST_EQUALS(2, ext.id); /* extmap 3 */ err = sdp_extmap_decode(&ext, extmapv[2]); TEST_EQUALS(0, err); TEST_STRCMP("URI-gps-string", strlen("URI-gps-string"), ext.name.p, ext.name.l); TEST_ASSERT(!pl_isset(&ext.attrs)); TEST_EQUALS(SDP_RECVONLY, ext.dir); TEST_ASSERT(ext.dir_set); TEST_EQUALS(4096, ext.id); out: return err; }
static int parse_resolv_conf(char *domain, size_t dsize, struct sa *srvv, uint32_t *n) { FILE *f; struct pl dom = pl_null; uint32_t i = 0; int err = 0; if (!srvv || !n || !*n) return EINVAL; f = fopen("/etc/resolv.conf", "r"); if (!f) return errno; for (;;) { char line[128]; struct pl srv; size_t len; if (1 != fscanf(f, "%127[^\n]\n", line)) break; if ('#' == line[0]) continue; len = str_len(line); /* Set domain if not already set */ if (!pl_isset(&dom)) { if (0 == re_regex(line, len, "domain [^ ]+", &dom)) { (void)pl_strcpy(&dom, domain, dsize); } if (0 == re_regex(line, len, "search [^ ]+", &dom)) { (void)pl_strcpy(&dom, domain, dsize); } } /* Use the first entry */ if (i < *n && 0 == re_regex(line, len, "nameserver [^\n]+", &srv)) { err = sa_set(&srvv[i], &srv, DNS_PORT); if (err) { DEBUG_WARNING("sa_set: %r (%m)\n", &srv, err); } ++i; } } *n = i; (void)fclose(f); return err; }
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; }
int cmd_process_long(struct commands *commands, const char *str, size_t len, struct re_printf *pf_resp, void *data) { struct cmd_arg arg; const struct cmd *cmd_long; char *name = NULL, *prm = NULL; struct pl pl_name, pl_prm; int err; if (!str || !len) return EINVAL; memset(&arg, 0, sizeof(arg)); err = re_regex(str, len, "[^ ]+[ ]*[~]*", &pl_name, NULL, &pl_prm); if (err) { return err; } err = pl_strdup(&name, &pl_name); if (pl_isset(&pl_prm)) err |= pl_strdup(&prm, &pl_prm); if (err) goto out; cmd_long = cmd_find_long(commands, name); if (cmd_long) { arg.key = LONG_PREFIX; arg.prm = prm; arg.complete = true; arg.data = data; if (cmd_long->h) err = cmd_long->h(pf_resp, &arg); } else { err = re_hprintf(pf_resp, "command not found (%s)\n", name); } out: mem_deref(name); mem_deref(prm); return err; }
int conf_get_csv(const struct conf *conf, const char *name, char *str1, size_t sz1, char *str2, size_t sz2) { struct pl r, pl1, pl2 = pl_null; int err; err = conf_get(conf, name, &r); if (err) return err; /* note: second value may be quoted */ err = re_regex(r.p, r.l, "[^,]+,[~]*", &pl1, &pl2); if (err) return err; (void)pl_strcpy(&pl1, str1, sz1); if (pl_isset(&pl2)) (void)pl_strcpy(&pl2, str2, sz2); return 0; }
/** * 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 inline int hdr_add(struct sip_msg *msg, const struct pl *name, enum sip_hdrid id, const char *p, ssize_t l, bool atomic, bool line) { struct sip_hdr *hdr; int err = 0; hdr = mem_zalloc(sizeof(*hdr), hdr_destructor); if (!hdr) return ENOMEM; hdr->name = *name; hdr->val.p = p; hdr->val.l = MAX(l, 0); hdr->id = id; switch (id) { case SIP_HDR_VIA: case SIP_HDR_ROUTE: if (!atomic) break; hash_append(msg->hdrht, id, &hdr->he, mem_ref(hdr)); list_append(&msg->hdrl, &hdr->le, mem_ref(hdr)); break; default: if (atomic) hash_append(msg->hdrht, id, &hdr->he, mem_ref(hdr)); if (line) list_append(&msg->hdrl, &hdr->le, mem_ref(hdr)); break; } /* parse common headers */ switch (id) { case SIP_HDR_VIA: if (!atomic || pl_isset(&msg->via.sentby)) break; err = sip_via_decode(&msg->via, &hdr->val); break; case SIP_HDR_TO: err = sip_addr_decode((struct sip_addr *)&msg->to, &hdr->val); if (err) break; (void)msg_param_decode(&msg->to.params, "tag", &msg->to.tag); msg->to.val = hdr->val; break; case SIP_HDR_FROM: err = sip_addr_decode((struct sip_addr *)&msg->from, &hdr->val); if (err) break; (void)msg_param_decode(&msg->from.params, "tag", &msg->from.tag); msg->from.val = hdr->val; break; case SIP_HDR_CALL_ID: msg->callid = hdr->val; break; case SIP_HDR_CSEQ: err = sip_cseq_decode(&msg->cseq, &hdr->val); break; case SIP_HDR_MAX_FORWARDS: msg->maxfwd = hdr->val; break; case SIP_HDR_CONTENT_TYPE: err = msg_ctype_decode(&msg->ctyp, &hdr->val); break; case SIP_HDR_CONTENT_LENGTH: msg->clen = hdr->val; break; case SIP_HDR_EXPIRES: msg->expires = hdr->val; break; default: /* re_printf("%r = %u\n", &hdr->name, id); */ break; } mem_deref(hdr); return err; }
int call_accept(struct call *call, struct sipsess_sock *sess_sock, const struct sip_msg *msg) { bool got_offer; int err; if (!call || !msg) return EINVAL; call->outgoing = false; got_offer = (mbuf_get_left(msg->mb) > 0); err = pl_strdup(&call->peer_uri, &msg->from.auri); if (err) return err; if (pl_isset(&msg->from.dname)) { err = pl_strdup(&call->peer_name, &msg->from.dname); if (err) return err; } if (got_offer) { struct sdp_media *m; const struct sa *raddr; err = sdp_decode(call->sdp, msg->mb, true); if (err) return err; call->got_offer = true; /* * Each media description in the SDP answer MUST * use the same network type as the corresponding * media description in the offer. * * See RFC 6157 */ m = stream_sdpmedia(audio_strm(call->audio)); raddr = sdp_media_raddr(m); if (sa_af(raddr) != call->af) { info("call: incompatible address-family" " (local=%s, remote=%s)\n", net_af2name(call->af), net_af2name(sa_af(raddr))); sip_treply(NULL, uag_sip(), msg, 488, "Not Acceptable Here"); call_event_handler(call, CALL_EVENT_CLOSED, "Wrong address family"); return 0; } /* Check if we have any common audio codecs, after * the SDP offer has been parsed */ if (!have_common_audio_codecs(call)) { info("call: no common audio codecs - rejected\n"); sip_treply(NULL, uag_sip(), msg, 488, "Not Acceptable Here"); call_event_handler(call, CALL_EVENT_CLOSED, "No audio codecs"); return 0; } } err = sipsess_accept(&call->sess, sess_sock, msg, 180, "Ringing", ua_cuser(call->ua), "application/sdp", NULL, auth_handler, call->acc, true, sipsess_offer_handler, sipsess_answer_handler, sipsess_estab_handler, sipsess_info_handler, sipsess_refer_handler, sipsess_close_handler, call, "Allow: %s\r\n", uag_allowed_methods()); if (err) { warning("call: sipsess_accept: %m\n", err); return err; } set_state(call, STATE_INCOMING); /* New call */ tmr_start(&call->tmr_inv, LOCAL_TIMEOUT*1000, invite_timeout, call); if (!call->acc->mnat) call_event_handler(call, CALL_EVENT_INCOMING, call->peer_uri); return err; }
static bool auth_handler(const struct sip_hdr *hdr, const struct sip_msg *msg, void *arg) { struct httpauth_digest_chall ch; struct sip_auth *auth = arg; struct realm *realm = NULL; int err; (void)msg; if (httpauth_digest_challenge_decode(&ch, &hdr->val)) { err = EBADMSG; goto out; } if (pl_isset(&ch.algorithm) && pl_strcasecmp(&ch.algorithm, "md5")) { err = ENOSYS; goto out; } realm = list_ledata(list_apply(&auth->realml, true, cmp_handler, &ch.realm)); if (!realm) { realm = mem_zalloc(sizeof(*realm), realm_destructor); if (!realm) { err = ENOMEM; goto out; } list_append(&auth->realml, &realm->le, realm); err = pl_strdup(&realm->realm, &ch.realm); if (err) goto out; err = auth->authh(&realm->user, &realm->pass, realm->realm, auth->arg); if (err) goto out; } else { if (!pl_isset(&ch.stale) || pl_strcasecmp(&ch.stale, "true")) { err = EAUTH; goto out; } realm->nonce = mem_deref(realm->nonce); realm->qop = mem_deref(realm->qop); realm->opaque = mem_deref(realm->opaque); } realm->hdr = hdr->id; realm->nc = 1; err = pl_strdup(&realm->nonce, &ch.nonce); if (pl_isset(&ch.qop)) err |= pl_strdup(&realm->qop, &ch.qop); if (pl_isset(&ch.opaque)) err |= pl_strdup(&realm->opaque, &ch.opaque); out: if (err) { mem_deref(realm); auth->err = err; return true; } return false; }
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; }
static int cand_decode(struct icem *icem, const char *val) { struct pl foundation, compid, transp, prio, addr, port, cand_type; struct pl extra = pl_null; struct sa caddr, rel_addr; char type[8]; uint8_t cid; int err; sa_init(&rel_addr, AF_INET); err = re_regex(val, strlen(val), "[^ ]+ [0-9]+ [^ ]+ [0-9]+ [^ ]+ [0-9]+ typ [a-z]+[^]*", &foundation, &compid, &transp, &prio, &addr, &port, &cand_type, &extra); if (err) return err; if (ICE_TRANSP_NONE == transp_resolve(&transp)) { DEBUG_NOTICE("<%s> ignoring candidate with" " unknown transport=%r (%r:%r)\n", icem->name, &transp, &cand_type, &addr); return 0; } if (pl_isset(&extra)) { struct pl name, value; /* Loop through " SP attr SP value" pairs */ while (!re_regex(extra.p, extra.l, " [^ ]+ [^ ]+", &name, &value)) { pl_advance(&extra, value.p + value.l - extra.p); if (0 == pl_strcasecmp(&name, rel_addr_str)) { err = sa_set(&rel_addr, &value, sa_port(&rel_addr)); if (err) break; } else if (0 == pl_strcasecmp(&name, rel_port_str)) { sa_set_port(&rel_addr, pl_u32(&value)); } } } err = sa_set(&caddr, &addr, pl_u32(&port)); if (err) return err; cid = pl_u32(&compid); /* add only if not exist */ if (icem_cand_find(&icem->rcandl, cid, &caddr)) return 0; (void)pl_strcpy(&cand_type, type, sizeof(type)); return icem_rcand_add(icem, ice_cand_name2type(type), cid, pl_u32(&prio), &caddr, &rel_addr, &foundation); }
int call_accept(struct call *call, struct sipsess_sock *sess_sock, const struct sip_msg *msg) { bool got_offer; int err; if (!call || !msg) return EINVAL; got_offer = (mbuf_get_left(msg->mb) > 0); err = pl_strdup(&call->peer_uri, &msg->from.auri); if (err) return err; if (pl_isset(&msg->from.dname)) { err = pl_strdup(&call->peer_name, &msg->from.dname); if (err) return err; } if (got_offer) { err = sdp_decode(call->sdp, msg->mb, true); if (err) return err; call->got_offer = true; /* Check if we have any common audio codecs, after * the SDP offer has been parsed */ if (!have_common_audio_codecs(call)) { info("call: no common audio codecs - rejected\n"); sip_treply(NULL, uag_sip(), msg, 488, "Not Acceptable Here"); call_event_handler(call, CALL_EVENT_CLOSED, "No audio codecs"); return 0; } } err = sipsess_accept(&call->sess, sess_sock, msg, 180, "Ringing", ua_cuser(call->ua), "application/sdp", NULL, auth_handler, call->acc, true, sipsess_offer_handler, sipsess_answer_handler, sipsess_estab_handler, sipsess_info_handler, sipsess_refer_handler, sipsess_close_handler, call, "Allow: %s\r\n", uag_allowed_methods()); if (err) { warning("call: sipsess_accept: %m\n", err); return err; } set_state(call, STATE_INCOMING); /* New call */ tmr_start(&call->tmr_inv, LOCAL_TIMEOUT*1000, invite_timeout, call); if (!call->acc->mnat) call_event_handler(call, CALL_EVENT_INCOMING, call->peer_uri); return err; }
/** * Send an HTTP request * * @param reqp Pointer to allocated HTTP request object * @param cli HTTP Client * @param met Request method * @param uri Request URI * @param resph Response handler * @param datah Content handler (optional) * @param arg Handler argument * @param fmt Formatted HTTP headers and body (optional) * * @return 0 if success, otherwise errorcode */ int http_request(struct http_req **reqp, struct http_cli *cli, const char *met, const char *uri, http_resp_h *resph, http_data_h *datah, void *arg, const char *fmt, ...) { struct pl scheme, host, port, path; struct http_req *req; uint16_t defport; bool secure; va_list ap; int err; if (!reqp || !cli || !met || !uri) return EINVAL; if (re_regex(uri, strlen(uri), "[a-z]+://[^:/]+[:]*[0-9]*[^]+", &scheme, &host, NULL, &port, &path) || scheme.p != uri) return EINVAL; if (!pl_strcasecmp(&scheme, "http") || !pl_strcasecmp(&scheme, "ws")) { secure = false; defport = 80; } #ifdef USE_TLS else if (!pl_strcasecmp(&scheme, "https") || !pl_strcasecmp(&scheme, "wss")) { secure = true; defport = 443; } #endif else return ENOTSUP; req = mem_zalloc(sizeof(*req), req_destructor); if (!req) return ENOMEM; req->tls = mem_ref(cli->tls); req->secure = secure; req->port = pl_isset(&port) ? pl_u32(&port) : defport; req->resph = resph; req->datah = datah; req->arg = arg; err = pl_strdup(&req->host, &host); if (err) goto out; req->mbreq = mbuf_alloc(1024); if (!req->mbreq) { err = ENOMEM; goto out; } err = mbuf_printf(req->mbreq, "%s %r HTTP/1.1\r\n" "Host: %r\r\n", met, &path, &host); if (fmt) { va_start(ap, fmt); err |= mbuf_vprintf(req->mbreq, fmt, ap); va_end(ap); } else { err |= mbuf_write_str(req->mbreq, "\r\n"); } if (err) goto out; req->mbreq->pos = 0; if (!sa_set_str(&req->srvv[0], req->host, req->port)) { req->srvc = 1; err = req_connect(req); if (err) goto out; } else { err = dnsc_query(&req->dq, cli->dnsc, req->host, DNS_TYPE_A, DNS_CLASS_IN, true, query_handler, req); if (err) goto out; } out: if (err) mem_deref(req); else { req->reqp = reqp; *reqp = req; } return err; }