Skip to content

Instantly share code, notes, and snippets.

@mble
Created July 4, 2024 18:42
Show Gist options
  • Save mble/505225ff16fe3ce30a5f372a95057081 to your computer and use it in GitHub Desktop.
Save mble/505225ff16fe3ce30a5f372a95057081 to your computer and use it in GitHub Desktop.
Debugging how Redis CLI parses URIs
○ ~/dev/scratch/redis-cli-parse $ clang redis-cli-parser.c sds.c -o redis-cli-parser.out
○ ~/dev/scratch/redis-cli-parse $ ./redis-cli-parser.out
strlen(user): 0
scheme: rediss
tls_flag: 1
hostip: localhost
hostport: 6379
input_dbnum: 0
auth: password
user:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "sds.h"
typedef struct cliConnInfo {
char *hostip;
int hostport;
int input_dbnum;
char *auth;
char *user;
} cliConnInfo;
#define isHexChar(c) (isdigit(c) || ((c) >= 'a' && (c) <= 'f'))
#define decodeHexChar(c) (isdigit(c) ? (c) - '0' : (c) - 'a' + 10)
#define decodeHex(h, l) ((decodeHexChar(h) << 4) + decodeHexChar(l))
static sds percentDecode(const char *pe, size_t len) {
const char *end = pe + len;
sds ret = sdsempty();
const char *curr = pe;
while (curr < end) {
if (*curr == '%') {
if ((end - curr) < 2) {
fprintf(stderr, "Incomplete URI encoding\n");
exit(1);
}
char h = tolower(*(++curr));
char l = tolower(*(++curr));
if (!isHexChar(h) || !isHexChar(l)) {
fprintf(stderr, "Illegal character in URI encoding\n");
exit(1);
}
char c = decodeHex(h, l);
ret = sdscatlen(ret, &c, 1);
curr++;
} else {
ret = sdscatlen(ret, curr++, 1);
}
}
return ret;
}
void parseRedisUri(cliConnInfo *connInfo, const char *uri, int *tls_flag) {
const char *scheme = "redis://";
const char *tlsscheme = "rediss://";
const char *curr = uri;
const char *end = uri + strlen(uri);
const char *userinfo, *username, *port, *host, *path;
/* URI must start with a valid scheme. */
if (!strncasecmp(tlsscheme, curr, strlen(tlsscheme))) {
*tls_flag = 1;
curr += strlen(tlsscheme);
} else if (!strncasecmp(scheme, curr, strlen(scheme))) {
*tls_flag = 0;
curr += strlen(scheme);
} else {
fprintf(stderr, "Invalid URI scheme\n");
exit(1);
}
if (curr == end) return;
/* Extract user info. */
if ((userinfo = strchr(curr,'@'))) {
if ((username = strchr(curr, ':')) && username < userinfo) {
connInfo->user = percentDecode(curr, username - curr);
curr = username + 1;
}
connInfo->auth = percentDecode(curr, userinfo - curr);
curr = userinfo + 1;
}
if (curr == end) return;
/* Extract host and port. */
path = strchr(curr, '/');
if (*curr != '/') {
host = path ? path - 1 : end;
if (*curr == '[') {
curr += 1;
if ((port = strchr(curr, ']'))) {
if (*(port+1) == ':') {
connInfo->hostport = atoi(port + 2);
}
host = port - 1;
}
} else {
if ((port = strchr(curr, ':'))) {
connInfo->hostport = atoi(port + 1);
host = port - 1;
}
}
sdsfree(connInfo->hostip);
connInfo->hostip = sdsnewlen(curr, host - curr + 1);
}
curr = path ? path + 1 : end;
if (curr == end) return;
/* Extract database number. */
connInfo->input_dbnum = atoi(curr);
}
int main() {
cliConnInfo connInfo;
connInfo.hostip = sdsnew("127.0.0.1");
connInfo.hostport = 6379;
connInfo.input_dbnum = 0;
connInfo.auth = NULL;
connInfo.user = NULL;
int tls_flag;
const char *uri = "rediss://:password@localhost:6379/0";
parseRedisUri(&connInfo, uri, &tls_flag);
if (connInfo.user != NULL) {
printf("strlen(user): %lu\n", strlen(connInfo.user));
}
printf("scheme: %s\n", tls_flag ? "rediss" : "redis");
printf("tls_flag: %d\n", tls_flag);
printf("hostip: %s\n", connInfo.hostip);
printf("hostport: %d\n", connInfo.hostport);
printf("input_dbnum: %d\n", connInfo.input_dbnum);
printf("auth: %s\n", connInfo.auth);
printf("user: %s\n", connInfo.user);
}
@mble
Copy link
Author

mble commented Jul 4, 2024

TL;DR empty usernames are not treated as NULL, so triggers the AUTH/2 behaviour, instead of AUTH/1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment