From 1953d78dacc439a3f415188cea4b6087d86f0c7f Mon Sep 17 00:00:00 2001 From: Maximilian Fridrich Date: Mon, 20 Mar 2023 17:27:13 +0100 Subject: [PATCH] Escape SIP URIs According to RFC 3261, SIP URIs need to escape UTF-8 characters. --- include/re_uri.h | 3 ++ src/fmt/print.c | 14 +++++--- src/sip/ctrans.c | 3 +- src/sip/dialog.c | 22 +++++++++---- src/sip/request.c | 3 +- src/uri/uri.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++ src/uri/uric.c | 46 +++++++++++++++++++++++++- test/uri.c | 3 +- 8 files changed, 161 insertions(+), 15 deletions(-) diff --git a/include/re_uri.h b/include/re_uri.h index 4b9871beb..143297a67 100644 --- a/include/re_uri.h +++ b/include/re_uri.h @@ -43,3 +43,6 @@ int uri_param_escape(struct re_printf *pf, const struct pl *pl); int uri_param_unescape(struct re_printf *pf, const struct pl *pl); int uri_header_escape(struct re_printf *pf, const struct pl *pl); int uri_header_unescape(struct re_printf *pf, const struct pl *pl); +int uri_display_name_escape(struct re_printf *pf, const struct pl *pl); +int uri_escape_sip_uri(struct re_printf *pf, const char *uri); +int uri_escape_sip_uri_pl(struct re_printf *pf, const struct pl *pl); diff --git a/src/fmt/print.c b/src/fmt/print.c index 080ef973c..14ea2f0cb 100644 --- a/src/fmt/print.c +++ b/src/fmt/print.c @@ -382,10 +382,16 @@ int re_vhprintf(const char *fmt, va_list ap, re_vprintf_h *vph, void *arg) } len = local_itoa(num, n, base, uc); - - err |= write_padded(num, len, pad, - plr ? ' ' : pch, plr, NULL, - vph, arg); + if (fpad != (size_t)-1) { + err |= write_padded(num + len - fpad, fpad, 0, + plr ? ' ' : pch, plr, NULL, + vph, arg); + } + else { + err |= write_padded(num, len, pad, + plr ? ' ' : pch, plr, NULL, + vph, arg); + } break; case 'v': diff --git a/src/sip/ctrans.c b/src/sip/ctrans.c index 10b5f28d0..2d1f14970 100644 --- a/src/sip/ctrans.c +++ b/src/sip/ctrans.c @@ -59,7 +59,8 @@ static bool route_handler(const struct sip_hdr *hdr, const struct sip_msg *msg, void *arg) { (void)msg; - return 0 != mbuf_printf(arg, "Route: %r\r\n", &hdr->val); + return 0 != mbuf_printf(arg, "Route: %H\r\n", uri_escape_sip_uri_pl, + &hdr->val); } diff --git a/src/sip/dialog.c b/src/sip/dialog.c index e605e17c3..ecfd4f3bf 100644 --- a/src/sip/dialog.c +++ b/src/sip/dialog.c @@ -80,6 +80,7 @@ int sip_dialog_alloc(struct sip_dialog **dlgp, struct sip_addr addr; size_t rend = 0; struct pl pl; + struct pl from_name_pl; uint32_t i; int err; @@ -113,16 +114,22 @@ int sip_dialog_alloc(struct sip_dialog **dlgp, } for (i=0; imb, "Route: <%s;lr>\r\n", routev[i]); + err |= mbuf_printf(dlg->mb, "Route: <%H;lr>\r\n", + uri_escape_sip_uri, routev[i]); if (i == 0) rend = dlg->mb->pos - 2; } - err |= mbuf_printf(dlg->mb, "To: <%s>\r\n", to_uri); + err |= mbuf_printf(dlg->mb, "To: <%H>\r\n", uri_escape_sip_uri, + to_uri); dlg->cpos = dlg->mb->pos; - err |= mbuf_printf(dlg->mb, "From: %s%s%s<%s>;tag=%016llx\r\n", - from_name ? "\"" : "", from_name, - from_name ? "\" " : "", - from_uri, ltag); + err |= mbuf_printf(dlg->mb, "From: "); + if (from_name) { + pl_set_str(&from_name_pl, from_name); + err |= mbuf_printf(dlg->mb, "\"%H\" ", uri_display_name_escape, + &from_name_pl); + } + err |= mbuf_printf(dlg->mb, "<%H>", uri_escape_sip_uri, from_uri); + err |= mbuf_printf(dlg->mb, ";tag=%016llx\r\n", ltag); if (err) goto out; @@ -156,7 +163,8 @@ static bool record_route_handler(const struct sip_hdr *hdr, struct route_enc *renc = arg; (void)msg; - if (mbuf_printf(renc->mb, "Route: %r\r\n", &hdr->val)) + if (mbuf_printf(renc->mb, "Route: %H\r\n", uri_escape_sip_uri_pl, + &hdr->val)) return true; if (!renc->end) diff --git a/src/sip/request.c b/src/sip/request.c index 3e16721c4..96404cd12 100644 --- a/src/sip/request.c +++ b/src/sip/request.c @@ -189,7 +189,8 @@ static int request(struct sip_request *req, enum sip_transp tp, goto out; mbuf_set_pos(mbs, 0); - err = mbuf_printf(mb, "%s %s SIP/2.0\r\n", req->met, req->uri); + err = mbuf_printf(mb, "%s ", req->met); + err |= mbuf_printf(mb, "%H SIP/2.0\r\n", uri_escape_sip_uri, req->uri); err |= mbuf_printf(mb, "Via: SIP/2.0/%s %J;branch=%s;rport\r\n", sip_transp_name(tp), &laddr, branch); err |= mbuf_write_mem(mb, mbuf_buf(mbs), mbuf_get_left(mbs)); diff --git a/src/uri/uri.c b/src/uri/uri.c index 11d623728..3e27fcc06 100644 --- a/src/uri/uri.c +++ b/src/uri/uri.c @@ -271,3 +271,85 @@ int uri_headers_apply(const struct pl *pl, uri_apply_h *ah, void *arg) return err; } + + +/** + * Escape SIP URI + * + * @param pf Print function to encode into + * @param uri URI + * + * @return 0 if success, otherwise errorcode + */ +int uri_escape_sip_uri(struct re_printf *pf, const char *uri) +{ + struct pl pl; + pl_set_str(&pl, uri); + return uri_escape_sip_uri_pl(pf, &pl); +} + + +/** + * Escape SIP URI + * + * @param pf Print function to encode into + * @param pl URI + * + * @return 0 if success, otherwise errorcode + */ +int uri_escape_sip_uri_pl(struct re_printf *pf, const struct pl *pl) +{ + int err; + struct uri uri; + + if (!pl) + return 0; + + err = uri_decode(&uri, pl); + if (err) + return err; + + if (!pl_isset(&uri.scheme) || !pl_isset(&uri.host)) + return EINVAL; + + err = re_hprintf(pf, "%r:", &uri.scheme); + if (err) + return err; + + if (pl_isset(&uri.user)) { + err = re_hprintf(pf, "%H", uri_user_escape, &uri.user); + + if (pl_isset(&uri.password)) + err |= re_hprintf(pf, ":%H", uri_password_escape, + &uri.password); + + err |= pf->vph("@", 1, pf->arg); + + if (err) + return err; + } + + /* The IPv6 address is delimited by '[' and ']' */ + switch (uri.af) { + +#ifdef HAVE_INET6 + case AF_INET6: + err = re_hprintf(pf, "[%r]", &uri.host); + break; +#endif + + default: + err = re_hprintf(pf, "%r", &uri.host); + break; + } + if (err) + return err; + + if (uri.port) + err = re_hprintf(pf, ":%u", uri.port); + + err |= re_hprintf(pf, "%r%r%r", &uri.path, &uri.params, + &uri.headers); + + return err; +} diff --git a/src/uri/uric.c b/src/uri/uric.c index 1d97dfe70..96203d044 100644 --- a/src/uri/uric.c +++ b/src/uri/uric.c @@ -120,6 +120,33 @@ static bool is_param_unreserved(char c) } +static bool is_token_non_alphanum(int c) +{ + switch (c) { + + case '-': + case '.': + case '!': + case '%': + case '*': + case '_': + case '+': + case '`': + case '\'': + case '~': + return true; + default: + return false; + } +} + + +static bool is_token(char c) +{ + return isalnum(c) || is_token_non_alphanum(c); +} + + static bool is_paramchar(char c) { return is_param_unreserved(c) || is_unreserved(c); @@ -147,7 +174,10 @@ static int comp_escape(struct re_printf *pf, const struct pl *pl, esc_h *eh) err = pf->vph(&c, 1, pf->arg); } else { - err = re_hprintf(pf, "%%%02X", c); + if (c < 0) + err = re_hprintf(pf, "%%%02.X", c); + else + err = re_hprintf(pf, "%%%02X", c); } } @@ -304,3 +334,17 @@ int uri_header_unescape(struct re_printf *pf, const struct pl *pl) { return comp_unescape(pf, pl, is_hvalue); } + + +/** + * Escape display name + * + * @param pf Print function + * @param pl String to escape + * + * @return 0 if success, otherwise errorcode + */ +int uri_display_name_escape(struct re_printf *pf, const struct pl *pl) +{ + return comp_escape(pf, pl, is_token); +} diff --git a/test/uri.c b/test/uri.c index 83c4785c4..51f91ba02 100644 --- a/test/uri.c +++ b/test/uri.c @@ -178,7 +178,8 @@ int test_uri_user(void) } uriv[] = { {"alice%40atlanta.com", "alice@atlanta.com"}, {"project%20x", "project x"}, - {"*21%23", "*21#"} + {"*21%23", "*21#"}, + {"*21%23%C3%A4%E2%82%AC%40%20", "*21#ä€@ "} }; struct mbuf mbe, mbd; int err = EINVAL;