From 37555868f656cbe5aa513ed8008028e8dffe26c2 Mon Sep 17 00:00:00 2001 From: Steffen Jaeckel Date: Fri, 10 Mar 2023 11:11:44 +0100 Subject: [PATCH] improve JID parsing according to RFC7622 Signed-off-by: Steffen Jaeckel --- src/jid.c | 79 ++++++++++++++++------ tests/test.h | 24 ++++--- tests/test_jid.c | 169 +++++++++++++++++++++-------------------------- 3 files changed, 146 insertions(+), 126 deletions(-) diff --git a/src/jid.c b/src/jid.c index 703c8f8..03f8387 100644 --- a/src/jid.c +++ b/src/jid.c @@ -37,8 +37,10 @@ char *xmpp_jid_new(xmpp_ctx_t *ctx, size_t len, nlen, dlen, rlen; /* jid must at least have a domain */ - if (domain == NULL) + if (domain == NULL) { + strophe_error(ctx, "jid", "domainpart missing."); return NULL; + } /* accumulate lengths */ dlen = strlen(domain); @@ -46,6 +48,26 @@ char *xmpp_jid_new(xmpp_ctx_t *ctx, rlen = (resource) ? strlen(resource) + 1 : 0; len = nlen + dlen + rlen; + if (dlen > 1023) { + strophe_error(ctx, "jid", "domainpart too long."); + return NULL; + } + if (nlen > 1024) { + strophe_error(ctx, "jid", "localpart too long."); + return NULL; + } + if (rlen > 1024) { + strophe_error(ctx, "jid", "resourcepart too long."); + return NULL; + } + + if (node) { + if (strcspn(node, "\"&'/:<>@") != nlen - 1) { + strophe_error(ctx, "jid", "localpart contained invalid character."); + return NULL; + } + } + /* concat components */ result = strophe_alloc(ctx, len + 1); if (result != NULL) { @@ -96,17 +118,29 @@ char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid) */ char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid) { + char *dup_jid = strophe_strdup(ctx, jid); char *result = NULL; const char *c; - c = strchr(jid, '@'); + /* Apply the same parsing rules from rfc7622 Section 3.2 + * 1. Strip resource + * 2. take part before the '@' + */ + + char *resource = strchr(dup_jid, '/'); + if (resource != NULL) { + *resource = '\0'; + } + + c = strchr(dup_jid, '@'); if (c != NULL) { - result = strophe_alloc(ctx, (c - jid) + 1); + result = strophe_alloc(ctx, (c - dup_jid) + 1); if (result != NULL) { - memcpy(result, jid, (c - jid)); - result[c - jid] = '\0'; + memcpy(result, dup_jid, (c - dup_jid)); + result[c - dup_jid] = '\0'; } } + strophe_free(ctx, dup_jid); return result; } @@ -120,24 +154,29 @@ char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid) */ char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid) { - char *result = NULL; - const char *c; - size_t dlen; + char *dup_jid = strophe_strdup(ctx, jid); - c = strchr(jid, '@'); - if (c == NULL) { - /* no node, assume domain */ - c = jid; + /* rfc7622 Section 3.2 + * 1. Remove any portion from the first '/' character to the end of the + * string (if there is a '/' character present). + */ + + char *resource = strchr(dup_jid, '/'); + if (resource != NULL) { + *resource = '\0'; + } + + /* 2. Remove any portion from the beginning of the string to the first + * '@' character (if there is an '@' character present). + */ + char *at_sign = strchr(dup_jid, '@'); + char *result = NULL; + if (at_sign != NULL) { + result = strophe_strdup(ctx, (at_sign + 1)); } else { - /* advance past the separator */ - c++; - } - dlen = strcspn(c, "/"); /* do not include resource */ - result = strophe_alloc(ctx, dlen + 1); - if (result != NULL) { - memcpy(result, c, dlen); - result[dlen] = '\0'; + result = strophe_strdup(ctx, dup_jid); } + strophe_free(ctx, dup_jid); return result; } diff --git a/tests/test.h b/tests/test.h index 509975a..99c234b 100644 --- a/tests/test.h +++ b/tests/test.h @@ -34,17 +34,19 @@ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #endif -#define COMPARE(v1, v2) \ - do { \ - const char *__v1 = v1; \ - const char *__v2 = v2; \ - if (!__v1 || !__v2 || strcmp(__v1, __v2) != 0) { \ - printf("Error: %s\n" \ - "Expected: %s\n" \ - "Got: %s\n", \ - #v1 " != " #v2, __v1, __v2); \ - exit(1); \ - } \ +#define COMPARE(v1, v2) \ + do { \ + const char *__v1 = v1; \ + const char *__v2 = v2; \ + if ((__v1 == NULL) && (__v2 == NULL)) { \ + /* noop */ \ + } else if (!__v1 || !__v2 || strcmp(__v1, __v2) != 0) { \ + printf("Error: %s\n" \ + "Expected: %s\n" \ + "Got: %s\n", \ + #v1 " != " #v2, __v1, __v2); \ + exit(1); \ + } \ } while (0) #define COMPARE_BUF(v1, len1, v2, len2) \ diff --git a/tests/test_jid.c b/tests/test_jid.c index 629f4b6..ccd5fd2 100644 --- a/tests/test_jid.c +++ b/tests/test_jid.c @@ -15,114 +15,80 @@ #include "strophe.h" #include "common.h" -static const char jid1[] = "foo@bar.com"; -static const char jid2[] = "anyone@example.com/hullo"; -static const char jid3[] = "manic.porter@xyz.net/frob"; -static const char jid4[] = "domain.tld"; +#include "test.h" static const char *_s(const char *s) { return s == NULL ? "" : s; } -int test_jid(xmpp_ctx_t *ctx) +static int test_jid(xmpp_ctx_t *ctx) { char *bare; char *node; char *domain; char *resource; + size_t n; + struct { + const char *jid; + const char *bare; + const char *node; + const char *domain; + const char *resource; + } testcases[] = { + {"foo@bar.com", "foo@bar.com", "foo", "bar.com", NULL}, + { + "anyone@example.com/hullo", + "anyone@example.com", + "anyone", + "example.com", + "hullo", + }, + { + "a.example.com/b@example.net", + "a.example.com", + NULL, + "a.example.com", + "b@example.net", + }, + { + "manic.porter@xyz.net/frob", + "manic.porter@xyz.net", + "manic.porter", + "xyz.net", + "frob", + }, + { + "domain.tld", + "domain.tld", + NULL, + "domain.tld", + NULL, + }, + }; - bare = xmpp_jid_bare(ctx, jid1); - node = xmpp_jid_node(ctx, jid1); - domain = xmpp_jid_domain(ctx, jid1); - resource = xmpp_jid_resource(ctx, jid1); - printf("jid '%s' parsed to %s, %s, %s\n", jid1, _s(node), _s(domain), - _s(resource)); - if (bare == NULL || strcmp(bare, "foo@bar.com")) - return 1; - if (node == NULL || strcmp(node, "foo")) - return 1; - if (domain == NULL || strcmp(domain, "bar.com")) - return 1; - if (resource != NULL) - return 1; - if (bare) - strophe_free(ctx, bare); - if (node) - strophe_free(ctx, node); - if (domain) - strophe_free(ctx, domain); - if (resource) - strophe_free(ctx, resource); + for (n = 0; n < sizeof(testcases) / sizeof(testcases[0]); ++n) { + bare = xmpp_jid_bare(ctx, testcases[n].jid); + node = xmpp_jid_node(ctx, testcases[n].jid); + domain = xmpp_jid_domain(ctx, testcases[n].jid); + resource = xmpp_jid_resource(ctx, testcases[n].jid); + printf("jid '%s' parsed to %s, %s, %s\n", testcases[n].jid, _s(node), + _s(domain), _s(resource)); + COMPARE(testcases[n].bare, bare); + COMPARE(testcases[n].node, node); + COMPARE(testcases[n].domain, domain); + COMPARE(testcases[n].resource, resource); + if (bare) + strophe_free(ctx, bare); + if (node) + strophe_free(ctx, node); + if (domain) + strophe_free(ctx, domain); + if (resource) + strophe_free(ctx, resource); + } - bare = xmpp_jid_bare(ctx, jid2); - node = xmpp_jid_node(ctx, jid2); - domain = xmpp_jid_domain(ctx, jid2); - resource = xmpp_jid_resource(ctx, jid2); - printf("jid '%s' parsed to %s, %s, %s\n", jid2, _s(node), _s(domain), - _s(resource)); - if (bare == NULL || strcmp(bare, "anyone@example.com")) - return 1; - if (node == NULL || strcmp(node, "anyone")) - return 1; - if (domain == NULL || strcmp(domain, "example.com")) - return 1; - if (resource == NULL || strcmp(resource, "hullo")) - return 1; - if (bare) - strophe_free(ctx, bare); - if (node) - strophe_free(ctx, node); - if (domain) - strophe_free(ctx, domain); - if (resource) - strophe_free(ctx, resource); - - bare = xmpp_jid_bare(ctx, jid3); - node = xmpp_jid_node(ctx, jid3); - domain = xmpp_jid_domain(ctx, jid3); - resource = xmpp_jid_resource(ctx, jid3); - printf("jid '%s' parsed to %s, %s, %s\n", jid3, _s(node), _s(domain), - _s(resource)); - if (bare == NULL || strcmp(bare, "manic.porter@xyz.net")) - return 1; - if (node == NULL || strcmp(node, "manic.porter")) - return 1; - if (domain == NULL || strcmp(domain, "xyz.net")) - return 1; - if (resource == NULL || strcmp(resource, "frob")) - return 1; - if (bare) - strophe_free(ctx, bare); - if (node) - strophe_free(ctx, node); - if (domain) - strophe_free(ctx, domain); - if (resource) - strophe_free(ctx, resource); - - bare = xmpp_jid_bare(ctx, jid4); - node = xmpp_jid_node(ctx, jid4); - domain = xmpp_jid_domain(ctx, jid4); - resource = xmpp_jid_resource(ctx, jid4); - printf("jid '%s' parsed to %s, %s, %s\n", jid4, _s(node), _s(domain), - _s(resource)); - if (bare == NULL || strcmp(bare, "domain.tld")) - return 1; - if (node != NULL) - return 1; - if (domain == NULL || strcmp(domain, "domain.tld")) - return 1; - if (resource != NULL) - return 1; - if (bare) - strophe_free(ctx, bare); - if (node) - strophe_free(ctx, node); - if (domain) - strophe_free(ctx, domain); - if (resource) - strophe_free(ctx, resource); + printf("test_jid() finished successfully\n"); return 0; } @@ -143,6 +109,19 @@ int test_jid_new(xmpp_ctx_t *ctx) return 1; strophe_free(ctx, jid); + const char *invalid_chars = "\"&'/:<>@"; + char localpart[] = "localpart"; + do { + localpart[1] = *invalid_chars; + jid = xmpp_jid_new(ctx, localpart, "bar.com", NULL); + if (jid != NULL) { + printf("Shouldn't have created JID with localpart=\"%s\"\n", + localpart); + return 1; + } + invalid_chars++; + } while (*invalid_chars != '\0'); + return 0; }