diff --git a/src/url.c b/src/url.c index 4e5910d08..ddcce91cc 100644 --- a/src/url.c +++ b/src/url.c @@ -15,6 +15,7 @@ #include "util.h" #include +#include #include "mem.h" @@ -46,6 +47,42 @@ _parsePort(int *port, const char *sport) return s; } +static natsStatus _decodeAndDup(char **decoded, const char *encoded) +{ + size_t len = strlen(encoded); + const char *p = encoded; + const char *e = encoded + len; + char *d; + + *decoded = NATS_MALLOC(len + 1); + if (*decoded == NULL) + { + return nats_setDefaultError(NATS_NO_MEMORY); + } + d = *decoded; + for (; p < e; p++) + { + if (*p != '%') + { + *d++ = *p; + continue; + } + + if (e - p < 3 || (!isxdigit(*(p + 1)) || !isxdigit(*(p + 2)))) + { + NATS_FREE(*decoded); + *decoded = NULL; + return nats_setError(NATS_ERR, "invalid percent encoding in URL: %s", encoded); + } + + char buf[3] = {p[1], p[2], '\0'}; + *d++ = (char)strtol(buf, NULL, 16); + p += 2; + } + *d = '\0'; + return NATS_OK; +} + natsStatus natsUrl_Create(natsUrl **newUrl, const char *urlStr) { @@ -166,9 +203,9 @@ natsUrl_Create(natsUrl **newUrl, const char *urlStr) DUP_STRING(s, url->host, host); if (user != NULL) - IF_OK_DUP_STRING(s, url->username, user); + IFOK(s, _decodeAndDup(&url->username, user)); if (pwd != NULL) - IF_OK_DUP_STRING(s, url->password, pwd); + IFOK(s, _decodeAndDup(&url->password, pwd)); if ((s == NATS_OK) && nats_asprintf(&url->fullUrl, "%s://%s%s%s%s%s:%d%s%s", scheme, userval, usep, pwdval, hsep, host, url->port, pathsep, pathval) < 0) diff --git a/test/test.c b/test/test.c index ec2d2e888..a2c898038 100644 --- a/test/test.c +++ b/test/test.c @@ -1764,6 +1764,36 @@ test_natsUrl(void) natsUrl_Destroy(u); u = NULL; + test("'tcp://%4C%65v%00ignored:p%77d%00ignoredalso@localhost':"); + s = natsUrl_Create(&u, "tcp://%4C%65v%00ignored:p%77d%00ignoredalso@localhost"); + testCond((s == NATS_OK) + && (u != NULL) + && (u->host != NULL) + && (strcmp(u->host, "localhost") == 0) + && (u->username != NULL) + && (strcmp(u->username, "Lev") == 0) + && (u->password != NULL) + && (strcmp(u->password, "pwd") == 0) + && (u->port == 4222)); + natsUrl_Destroy(u); + u = NULL; + + test("'tcp://%4C%65v:p%77d@localhost':"); + s = natsUrl_Create(&u, "tcp://%4C%65v:p%77d@localhost"); + testCond((s == NATS_OK) && (u != NULL) && (u->host != NULL) && (strcmp(u->host, "localhost") == 0) && (u->username != NULL) && (strcmp(u->username, "Lev") == 0) && (u->password != NULL) && (strcmp(u->password, "pwd") == 0) && (u->port == 4222)); + natsUrl_Destroy(u); + u = NULL; + + test("'tcp://%4c%65v:p%@localhost':"); + s = natsUrl_Create(&u, "tcp://%4c%65v:p%@localhost"); + testCond((s == NATS_ERR) && (u == NULL) && (strstr(nats_GetLastError(NULL), "invalid percent encoding in URL: p%") != NULL)); + nats_clearLastError(); + + test("'tcp://%4H%65v:p%@localhost':"); + s = natsUrl_Create(&u, "tcp://%4H%65v:p%@localhost"); + testCond((s == NATS_ERR) && (u == NULL) && (strstr(nats_GetLastError(NULL), "invalid percent encoding in URL: %4H%65v") != NULL)); + nats_clearLastError(); + test("'tcp://localhost: 4222':"); s = natsUrl_Create(&u, "tcp://localhost: 4222"); testCond((s == NATS_INVALID_ARG) @@ -27951,18 +27981,35 @@ test_JetStreamSubscribeIdleHearbeat(void) s = js_Publish(NULL, js, "foo", "msg2", 4, NULL, &jerr); testCond((s == NATS_OK) && (jerr == 0)); + // Cheat by pretending that the server sends message seq 3, while client + // received only seq 1. Disable auto-ack for this message, or we mess up the + // server state. +#define PUBLISH_FAKE_JS_MSG_WITH_SEQ(_reply, _msg) \ + { \ + natsSub_Lock(sub); \ + inbox = sub->subject; \ + sub->jsi->ackNone = true; \ + natsSub_Unlock(sub); \ + \ + natsConn_setFilterWithClosure(nc, _setMsgReply, (void *)(_reply)); \ + s = natsConnection_PublishString(nc, inbox, (_msg)); \ + } + +#define PUBLISH_FAKE_RESET() \ + { \ + natsSub_Lock(sub); \ + sub->jsi->ackNone = false; \ + natsSub_Unlock(sub); \ + } + test("Check seq mismatch: "); - natsSub_Lock(sub); - inbox = sub->subject; - natsSub_Unlock(sub); - // Cheat by pretending that the server sends message seq 3, while client received only seq 1. - natsConn_setFilterWithClosure(nc, _setMsgReply, (void*) "$JS.ACK.TEST.dur1.1.3.3.1624472520000000000.0"); - s = natsConnection_PublishString(nc, inbox, "msg3"); + PUBLISH_FAKE_JS_MSG_WITH_SEQ("$JS.ACK.TEST.dur1.1.3.3.1624472520000000000.0", "msg3 fake"); // Wait for past the next HB and we should get an async error natsMutex_Lock(args.m); while ((s != NATS_TIMEOUT) && !args.done) s = natsCondition_TimedWait(args.c, args.m, 300); natsMutex_Unlock(args.m); + PUBLISH_FAKE_RESET(); testCond(s == NATS_OK); test("Check that notification is sent only once: "); @@ -27996,8 +28043,7 @@ test_JetStreamSubscribeIdleHearbeat(void) testCond(s == NATS_OK); test("Skip again: "); - natsConn_setFilterWithClosure(nc, _setMsgReply, (void*) "$JS.ACK.TEST.dur1.1.4.4.1624482520000000000.0"); - s = natsConnection_PublishString(nc, inbox, "msg4"); + PUBLISH_FAKE_JS_MSG_WITH_SEQ("$JS.ACK.TEST.dur1.1.4.4.1624482520000000000.0", "msg4 fake"); testCond(s == NATS_OK); test("Check async cb invoked: "); @@ -28005,6 +28051,7 @@ test_JetStreamSubscribeIdleHearbeat(void) while ((s != NATS_TIMEOUT) && !args.done) s = natsCondition_TimedWait(args.c, args.m, 1000); natsMutex_Unlock(args.m); + PUBLISH_FAKE_RESET(); testCond(s == NATS_OK); test("Check HB timer reports missed HB: "); @@ -28059,11 +28106,7 @@ test_JetStreamSubscribeIdleHearbeat(void) nats_clearLastError(); test("Skip: "); - natsSub_Lock(sub); - inbox = sub->subject; - natsSub_Unlock(sub); - natsConn_setFilterWithClosure(nc, _setMsgReply, (void*) "$JS.ACK.TEST.dur2.1.4.4.1624482520000000000.0"); - s = natsConnection_PublishString(nc, inbox, "msg4"); + PUBLISH_FAKE_JS_MSG_WITH_SEQ("$JS.ACK.TEST.dur2.1.4.4.1624482520000000000.0", "msg4 fake"); testCond(s == NATS_OK); // For sync subs, we should not get async error @@ -28074,6 +28117,7 @@ test_JetStreamSubscribeIdleHearbeat(void) natsMutex_Unlock(args.m); testCond(s == NATS_TIMEOUT); nats_clearLastError(); + PUBLISH_FAKE_RESET(); test("NextMsg reports error: "); s = natsSubscription_NextMsg(&msg, sub, 1000); @@ -28103,8 +28147,7 @@ test_JetStreamSubscribeIdleHearbeat(void) testCond(s == NATS_OK); test("Skip again: "); - natsConn_setFilterWithClosure(nc, _setMsgReply, (void*) "$JS.ACK.TEST.dur1.1.5.5.1624492520000000000.0"); - s = natsConnection_PublishString(nc, inbox, "msg5"); + PUBLISH_FAKE_JS_MSG_WITH_SEQ("$JS.ACK.TEST.dur1.1.5.5.1624492520000000000.0", "msg5 fake"); testCond(s == NATS_OK); test("NextMsg reports error: "); @@ -28112,6 +28155,7 @@ test_JetStreamSubscribeIdleHearbeat(void) s = natsSubscription_NextMsg(&msg, sub, 1000); testCond((s == NATS_MISMATCH) && (msg == NULL)); nats_clearLastError(); + PUBLISH_FAKE_RESET(); test("Check HB timer reports missed HB: "); s = NATS_OK;