错误现象
22:29:55 [86] DBG:db_mysql:db_mysql_connect: opening connection: mysql://xxxx:xxxx@:gsf@1202]@10.10.10.10/opensips
22:29:55 [86] ERROR:db_mysql:db_mysql_connect: driver error(2005): Unknown MySQL server host ':gsf@1202]@10.10.10.10' (2)
22:29:55 [86] ERROR:db_mysql:db_mysql_new_connection: initial connect failed
22:29:55 [86] ERROR:core:db_do_init: could not add connection to the pool
22:29:55 [86] ERROR:uri:mod_init: Could not connect to database
原因分析
opensips中的db_default_url的解析是自己实现的,2.x源码在db/db_id.c
如果你的密码存在特殊字符,如@,!等,这个解析就存在bug
根据它源码的注释,它是可以通过[] 来实现转义的,
scheme://[username[:password]@]hostname[:port]/database
但是实际上功能上存在bug。
我把它的源码部分贴出来,如果遇到问题的,可以自己试着跑一下
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
typedef struct __str {
char* s; /**< string as char array */
int len; /**< string length, not including null-termination */
} str;
/** Structure representing a database ID */
struct db_id {
char* scheme; /**< URL scheme */
char* username; /**< Username, case sensitive */
char* password; /**< Password, case sensitive */
char* host; /**< Host or IP, case insensitive */
unsigned short port; /**< Port number */
char* database; /**< Database, case sensitive */
str url; /**< Pointer to the original url> */
};
unsigned short str2s(const char* s, unsigned int len, int *err)
{
unsigned short ret;
int i;
unsigned char *limit;
unsigned char *str;
/*init*/
str=(unsigned char*)s;
ret=i=0;
limit=str+len;
for(;str<limit ;str++){
if ( (*str <= '9' ) && (*str >= '0') ){
ret=ret*10+*str-'0';
i++;
if (i>5) goto error_digits;
}else{
//error unknown char
goto error_char;
}
}
if (err) *err=0;
return ret;
error_digits:
printf("too many letters in [%.*s]\n", (int)len, s);
if (err) *err=1;
return 0;
error_char:
printf("unexpected char %c in %.*s\n", *str, (int)len, s);
if (err) *err=1;
return 0;
}
static int dupl_string(char** dst, const char* begin, const char* end)
{
if (*dst) free(*dst);
*dst = malloc(end - begin + 1);
if ((*dst) == NULL) {
return -1;
}
memcpy(*dst, begin, end - begin);
(*dst)[end - begin] = '\0';
return 0;
}
/**
* Parse a database URL of form
* scheme://[username[:password]@]hostname[:port]/database
*
* \param id filled id struct
* \param url parsed URL
* \return 0 if parsing was successful and -1 otherwise
*/
int parse_db_url(struct db_id* id, const str* url)
{
#define SHORTEST_DB_URL "s://a/b"
#define SHORTEST_DB_URL_LEN (sizeof(SHORTEST_DB_URL) - 1)
enum state {
ST_SCHEME, /* Scheme part */
ST_SLASH1, /* First slash */
ST_SLASH2, /* Second slash */
ST_USER_HOST, /* Username or hostname */
ST_PASS_PORT, /* Password or port part */
ST_HOST, /* Hostname part */
ST_HOST6, /* Hostname part IPv6 */
ST_PORT, /* Port part */
ST_DB /* Database part */
};
enum state st;
unsigned int len, i, ipv6_flag=0;
const char* begin;
char* prev_token = NULL;
if (!id || !url || !url->s) {
return -1;
}
len = url->len;
if (len < SHORTEST_DB_URL_LEN) {
return -1;
}
/* Initialize all attributes to 0 */
memset(id, 0, sizeof(struct db_id));
st = ST_SCHEME;
begin = url->s;
for(i = 0; i < len; i++) {
switch(st) {
case ST_SCHEME:
switch(url->s[i]) {
case ':':
st = ST_SLASH1;
if (dupl_string(&id->scheme, begin, url->s + i) < 0) goto err;
break;
}
break;
case ST_SLASH1:
switch(url->s[i]) {
case '/':
st = ST_SLASH2;
break;
default:
goto err;
}
break;
case ST_SLASH2:
switch(url->s[i]) {
case '/':
st = ST_USER_HOST;
begin = url->s + i + 1;
break;
default:
goto err;
}
break;
case ST_USER_HOST:
switch(url->s[i]) {
case '@':
st = ST_HOST;
if (dupl_string(&id->username, begin, url->s + i) < 0) goto err;
begin = url->s + i + 1;
break;
case ':':
st = ST_PASS_PORT;
if (dupl_string(&prev_token, begin, url->s + i) < 0) goto err;
begin = url->s + i + 1;
break;
case '[':
st = ST_HOST6;
begin = url->s + i + 1;
break;
case '/':
if (dupl_string(&id->host, begin, url->s + i) < 0) goto err;
if (dupl_string(&id->database, url->s + i + 1, url->s + len) < 0) goto err;
return 0;
}
break;
case ST_PASS_PORT:
switch(url->s[i]) {
case '@':
st = ST_HOST;
id->username = prev_token; prev_token = NULL;
if (dupl_string(&id->password, begin, url->s + i) < 0) goto err;
begin = url->s + i + 1;
break;
case '/':
id->host = prev_token; prev_token = NULL;
id->port = str2s(begin, url->s + i - begin, 0);
if (dupl_string(&id->database, url->s + i + 1, url->s + len) < 0) goto err;
return 0;
}
break;
case ST_HOST:
switch(url->s[i]) {
case '[':
st = ST_HOST6;
begin = url->s + i + 1;
break;
case ':':
st = ST_PORT;
if (dupl_string(&id->host, begin, url->s + i - ipv6_flag) < 0) goto err;
begin = url->s + i + 1;
break;
case '/':
if (dupl_string(&id->host, begin, url->s + i - ipv6_flag) < 0) goto err;
if (dupl_string(&id->database, url->s + i + 1, url->s + len) < 0) goto err;
return 0;
}
break;
case ST_HOST6:
switch(url->s[i]) {
case ']':
ipv6_flag = 1;
st = ST_HOST;
break;
}
break;
case ST_PORT:
switch(url->s[i]) {
case '/':
id->port = str2s(begin, url->s + i - begin, 0);
if (dupl_string(&id->database, url->s + i + 1, url->s + len) < 0) goto err;
return 0;
}
break;
case ST_DB:
break;
}
}
if (st != ST_DB) goto err;
return 0;
err:
if (id->scheme) free(id->scheme);
if (id->username) free(id->username);
if (id->password) free(id->password);
if (id->host) free(id->host);
if (id->database) free(id->database);
if (prev_token) free(prev_token);
return -1;
}
int main() {
struct db_id id;
str url;
url.s = "mysql://root:gsf1202@10.10.10.10/opensips";
url.len = strlen(url.s);
parse_db_url(&id, &url);
printf("scheme: %s \n"
"usename: %s \n"
"password: %s \n"
"host : %s \n"
"database: %s \n"
"port : %d",
id.scheme, id.username, id.password, id.host,id.database, id.port);
return 0;
}