opensips中db_default_url解析存在的bug

错误现象

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值