Skip to content

Commit

Permalink
CacheDB: Fix URL parser bugs with multiple hosts
Browse files Browse the repository at this point in the history
Although support for CACHEDB_ID_MULTIPLE_HOSTS has been in there for a
long time, URLs such as "redis:ha://h1,h2,h3:6379" were not correctly
parsed.

(cherry picked from commit bacb776)
  • Loading branch information
liviuchircu committed Nov 16, 2022
1 parent 3e85179 commit 2ebd84e
Show file tree
Hide file tree
Showing 2 changed files with 212 additions and 19 deletions.
55 changes: 47 additions & 8 deletions cachedb/cachedb_id.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ static int dupl_string(char** dst, const char* begin, const char* end)

*dst = pkg_malloc(end - begin + 1);
if ((*dst) == NULL) {
LM_ERR("pkg malloc failed on %p/%p\n", begin, end);
return -1;
}

Expand Down Expand Up @@ -81,7 +82,7 @@ static int parse_cachedb_url(struct cachedb_id* id, const str* url)
};

enum state st;
unsigned int len, i, ipv6_flag=0;
unsigned int len, i, ipv6_flag=0, multi_hosts=0;
char* begin;
char* prev_token,*start_host=NULL,*start_prev=NULL,*ptr;

Expand Down Expand Up @@ -159,11 +160,15 @@ static int parse_cachedb_url(struct cachedb_id* id, const str* url)
switch(url->s[i]) {
case '@':
st = ST_HOST;
multi_hosts = 0;
if (dupl_string(&id->username, begin, url->s + i) < 0) goto err;
begin = url->s + i + 1;
break;

case ':':
if (multi_hosts)
continue;

st = ST_PASS_PORT;
if (dupl_string(&prev_token, begin, url->s + i) < 0) goto err;
start_prev = begin;
Expand All @@ -180,6 +185,10 @@ static int parse_cachedb_url(struct cachedb_id* id, const str* url)
begin = url->s + i + 1;
st = ST_DB;
break;

case ',':
multi_hosts = 1;
break;
}
break;

Expand All @@ -190,6 +199,7 @@ static int parse_cachedb_url(struct cachedb_id* id, const str* url)
id->username = prev_token;
if (dupl_string(&id->password, begin, url->s + i) < 0) goto err;
begin = url->s + i + 1;
start_host = begin;
break;

case '/':
Expand Down Expand Up @@ -237,6 +247,10 @@ static int parse_cachedb_url(struct cachedb_id* id, const str* url)
begin = url->s + i + 1;
st = ST_DB;
break;

case ',':
id->flags |= CACHEDB_ID_MULTIPLE_HOSTS;
break;
}
break;

Expand Down Expand Up @@ -286,11 +300,23 @@ static int parse_cachedb_url(struct cachedb_id* id, const str* url)
}
}

if (st == ST_PORT) {
LM_DBG("final st: %d, begin: %s, start_host: %s\n", st, begin, start_host);

if (multi_hosts)
id->flags |= CACHEDB_ID_MULTIPLE_HOSTS;

if (st == ST_PORT || st == ST_PASS_PORT) {
int rc;
if (url->s + i - begin == 0)
goto err;

id->port = str2s(begin, url->s + i - begin, 0);
id->port = str2s(begin, url->s + i - begin, &rc);
if (rc != 0)
goto err;

if (prev_token && !id->host)
id->host = prev_token;

return 0;
}

Expand All @@ -300,11 +326,24 @@ static int parse_cachedb_url(struct cachedb_id* id, const str* url)
return 0;
}

if (st == ST_USER_HOST && begin == url->s+url->len) {
/* Not considered an error - to cope with modules that
* offer cacheDB functionality backed up by OpenSIPS mem */
id->flags |= CACHEDB_ID_NO_URL;
LM_DBG("Just scheme, no actual url\n");
if (st == ST_HOST || st == ST_USER_HOST) {
if (begin == url->s+url->len) {
if (st == ST_USER_HOST) {
/* Not considered an error - to cope with modules that
* offer cacheDB functionality backed up by OpenSIPS mem */
id->flags |= CACHEDB_ID_NO_URL;
LM_DBG("Just scheme, no actual url\n");
return 0;
} else {
goto err;
}
}

if (start_host)
begin = start_host;

if (begin < url->s + len &&
dupl_string(&id->host, begin, url->s + len) < 0) goto err;
return 0;
}

Expand Down
176 changes: 165 additions & 11 deletions cachedb/test/test_cachedb.c
Original file line number Diff line number Diff line change
Expand Up @@ -519,37 +519,191 @@ static void test_cachedb_backends(void)

static void test_cachedb_url(void)
{
#define CDB_PARSE(__url) db = new_cachedb_id(_str(__url)); if (!ok(db != NULL)) return;
struct cachedb_id *db;

/* invalid URLs */
ok(!new_cachedb_id(_str("d:g://@")));
ok(!new_cachedb_id(_str("d:g://u:@")));
ok(!new_cachedb_id(_str("d:g://u:p@")));
ok(!new_cachedb_id(_str("d:g://u:p@h")));
ok(!new_cachedb_id(_str("d:g://u:p@h:")));

db = new_cachedb_id(_str("redis:group1://:devxxxxxx@172.31.180.127:6379"));
if (!ok(db != NULL))
return;
CDB_PARSE("redis:group1://");
ok(db->flags == CACHEDB_ID_NO_URL);
ok(!strcmp(db->scheme, "redis"));
ok(!strcmp(db->group_name, "group1"));
ok(!db->username);
ok(!db->password);
ok(!db->host);
ok(db->port == 0);
ok(!db->database);
ok(!db->extra_options);

CDB_PARSE("redis:group1://172.31.180.127");
ok(db->flags == 0);
ok(!strcmp(db->scheme, "redis"));
ok(!strcmp(db->group_name, "group1"));
ok(!db->username);
ok(!db->password);
ok(!strcmp(db->host, "172.31.180.127"));
ok(db->port == 0);
ok(!db->database);
ok(!db->extra_options);

CDB_PARSE("redis:group1://172.31.180.127:6379");
ok(db->flags == 0);
ok(!strcmp(db->scheme, "redis"));
ok(!strcmp(db->group_name, "group1"));
ok(!db->username);
ok(!db->password);
ok(!strcmp(db->host, "172.31.180.127"));
ok(db->port == 6379);
ok(!db->database);
ok(!db->extra_options);

CDB_PARSE("redis:group1://user@172.31.180.127:6379");
ok(db->flags == 0);
ok(!strcmp(db->username, "user"));
ok(!db->password);
ok(!strcmp(db->host, "172.31.180.127"));
ok(db->port == 6379);
ok(!db->database);
ok(!db->extra_options);

CDB_PARSE("redis:group1://:pwd@172.31.180.127:6379");
ok(db->flags == 0);
ok(!strcmp(db->scheme, "redis"));
ok(!strcmp(db->group_name, "group1"));
ok(!strcmp(db->username, ""));
ok(!strcmp(db->password, "devxxxxxx"));
ok(!strcmp(db->password, "pwd"));
ok(!strcmp(db->host, "172.31.180.127"));
ok(db->port == 6379);
ok(!db->database);
ok(!db->extra_options);

db = new_cachedb_id(_str("redis:group1://:devxxxxxx@172.31.180.127:6379/"));
if (!ok(db != NULL))
return;
CDB_PARSE("redis:group1://user:@172.31.180.127:6379");
ok(!strcmp(db->username, "user"));
ok(!strcmp(db->password, ""));
ok(!strcmp(db->host, "172.31.180.127"));
ok(db->port == 6379);

CDB_PARSE("redis:group1://:pwd@172.31.180.127:6379/");
ok(db->flags == 0);
ok(!strcmp(db->username, ""));
ok(!strcmp(db->password, "pwd"));
ok(db->port == 6379);
ok(!db->database);
ok(!db->extra_options);

db = new_cachedb_id(_str("redis:group1://:devxxxxxx@172.31.180.127:6379/d?x=1&q=2"));
if (!ok(db != NULL))
return;
CDB_PARSE("redis:group1://:pwd@172.31.180.127:6379/d?x=1&q=2");
ok(db->flags == 0);
ok(!strcmp(db->username, ""));
ok(!strcmp(db->password, "pwd"));
ok(db->port == 6379);
ok(!strcmp(db->database, "d"));
ok(!strcmp(db->extra_options, "x=1&q=2"));

/* multiple hosts tests */

CDB_PARSE("redis:group1://h1,h2,h3");
ok(db->flags == CACHEDB_ID_MULTIPLE_HOSTS);
ok(!db->username);
ok(!db->password);
ok(!strcmp(db->host, "h1,h2,h3"));
ok(db->port == 0);
ok(!db->database);

CDB_PARSE("redis:group1://h1:1,h2:22,h3:333");
ok(db->flags == CACHEDB_ID_MULTIPLE_HOSTS);
ok(!db->username);
ok(!db->password);
ok(!strcmp(db->host, "h1:1,h2:22,h3:333"));
ok(db->port == 0);
ok(!db->database);

CDB_PARSE("redis:group1://h1,h2:22,h3:333");
ok(db->flags == CACHEDB_ID_MULTIPLE_HOSTS);
ok(!db->username);
ok(!db->password);
ok(!strcmp(db->host, "h1,h2:22,h3:333"));
ok(db->port == 0);
ok(!db->database);

CDB_PARSE("redis:group1://h1,h2:22,h3");
ok(db->flags == CACHEDB_ID_MULTIPLE_HOSTS);
ok(!db->username);
ok(!db->password);
ok(!strcmp(db->host, "h1,h2:22,h3"));
ok(db->port == 0);
ok(!db->database);

CDB_PARSE("redis:group1://h1,h2,h3:333");
ok(db->flags == CACHEDB_ID_MULTIPLE_HOSTS);
ok(!db->username);
ok(!db->password);
ok(!strcmp(db->host, "h1,h2,h3:333"));
ok(db->port == 0);
ok(!db->database);

CDB_PARSE("redis:group1://h1,h2,h3:333/");
ok(db->flags == CACHEDB_ID_MULTIPLE_HOSTS);
ok(!db->username);
ok(!db->password);
ok(!strcmp(db->host, "h1,h2,h3:333"));
ok(db->port == 0);
ok(!db->database);

CDB_PARSE("redis:group1://user@h1,h2,h3");
ok(db->flags == CACHEDB_ID_MULTIPLE_HOSTS);
ok(!strcmp(db->username, "user"));
ok(!db->password);
ok(!strcmp(db->host, "h1,h2,h3"));
ok(db->port == 0);
ok(!db->database);

CDB_PARSE("redis:group1://user:pwd@h1,h2,h3");
ok(db->flags == CACHEDB_ID_MULTIPLE_HOSTS);
ok(!strcmp(db->username, "user"));
ok(!strcmp(db->password, "pwd"));
ok(!strcmp(db->host, "h1,h2,h3"));
ok(db->port == 0);
ok(!db->database);

CDB_PARSE("redis:group1://:pwd@h1,h2,h3");
ok(db->flags == CACHEDB_ID_MULTIPLE_HOSTS);
ok(!strcmp(db->username, ""));
ok(!strcmp(db->password, "pwd"));
ok(!strcmp(db->host, "h1,h2,h3"));
ok(db->port == 0);
ok(!db->database);

CDB_PARSE("redis:group1://h1,h2,h3/db");
ok(db->flags == CACHEDB_ID_MULTIPLE_HOSTS);
ok(!db->username);
ok(!db->password);
ok(!strcmp(db->host, "h1,h2,h3"));
ok(db->port == 0);
ok(!strcmp(db->database, "db"));

CDB_PARSE("redis:group1://user:pwd@h1,h2,h3/db");
ok(db->flags == CACHEDB_ID_MULTIPLE_HOSTS);
ok(!strcmp(db->username, "user"));
ok(!strcmp(db->password, "pwd"));
ok(!strcmp(db->host, "h1,h2,h3"));
ok(db->port == 0);
ok(!strcmp(db->database, "db"));

CDB_PARSE("redis:ha://localhost,host_a:6380,host_b:6381,host_c/db");
ok(db->flags == CACHEDB_ID_MULTIPLE_HOSTS);
ok(!strcmp(db->host, "localhost,host_a:6380,host_b:6381,host_c"));
ok(!strcmp(db->database, "db"));
ok(db->port == 0);

CDB_PARSE("redis:group1://:pwd@h1,h2,h3:6379/d");
ok(db->flags == CACHEDB_ID_MULTIPLE_HOSTS);
ok(!strcmp(db->username, ""));
ok(!strcmp(db->password, "pwd"));
ok(!strcmp(db->host, "h1,h2,h3:6379"));
ok(!strcmp(db->database, "d"));
ok(db->port == 0);
}

0 comments on commit 2ebd84e

Please sign in to comment.