mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
718 lines
22 KiB
718 lines
22 KiB
/*
|
|
* Copyright (C) 2015 Olle E. Johansson, Edvina AB
|
|
*
|
|
* Based on code from sqlops and htable by Elena-Ramona:
|
|
* Copyright (C) 2008 Elena-Ramona Modroiu (asipto.com)
|
|
*
|
|
* This file is part of kamailio, a free SIP server.
|
|
*
|
|
* Kamailio is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* Kamailio is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/*! \file
|
|
* \brief Kamailio http_client :: Connection handling
|
|
* \ingroup http_client
|
|
*/
|
|
|
|
#include <curl/curl.h>
|
|
|
|
#include "../../hashes.h"
|
|
#include "../../dprint.h"
|
|
#include "../../parser/parse_param.h"
|
|
#include "../../usr_avp.h"
|
|
#include "../../cfg_parser.h"
|
|
#include "http_client.h"
|
|
#include "curlcon.h"
|
|
|
|
#define KEYVALUE_TYPE_NONE 0
|
|
#define KEYVALUE_TYPE_PARAMS 1
|
|
|
|
|
|
curl_con_t *_curl_con_root = NULL;
|
|
|
|
/* Forward declaration */
|
|
curl_con_t *curl_init_con(str *name);
|
|
|
|
/* Temporary structure for holding info parsed from cfg file */
|
|
typedef struct raw_http_client_conn
|
|
{
|
|
str name;
|
|
|
|
str url;
|
|
str username;
|
|
str password;
|
|
str failover;
|
|
str useragent;
|
|
str clientcert;
|
|
str clientkey;
|
|
str ciphersuites;
|
|
str http_proxy;
|
|
int http_proxy_port;
|
|
int verify_peer;
|
|
int verify_host;
|
|
int tlsversion;
|
|
int timeout;
|
|
int maxdatasize;
|
|
int http_follow_redirect;
|
|
int authmethod;
|
|
|
|
struct raw_http_client_conn *next;
|
|
} raw_http_client_conn_t;
|
|
|
|
static raw_http_client_conn_t *raw_conn_list = NULL;
|
|
|
|
static cfg_option_t tls_versions[] = {
|
|
{"DEFAULT", .val = 0}, /* CURL_SSLVERSION_DEFAULT */
|
|
{"TLSv1", .val = 1}, /* CURL_SSLVERSION_TLSv1 */
|
|
{"SSLv2", .val = 2}, /* CURL_SSLVERSION_SSLv2 */
|
|
{"SSLv3", .val = 3}, /* CURL_SSLVERSION_SSLv3 */
|
|
{"TLSv1.0", .val = 4}, /* CURL_SSLVERSION_TLSv1_0 - support after libcurl 7.34.0 */
|
|
{"TLSv1.1", .val = 5}, /* CURL_SSLVERSION_TLSv1_1 - support after libcurl 7.34.0 */
|
|
{"TLSv1.2", .val = 6}, /* CURL_SSLVERSION_TLSv1_2 - support after libcurl 7.34.0 */
|
|
{0}
|
|
};
|
|
|
|
static cfg_option_t http_client_options[] = {
|
|
{"url", .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM}, /* 0 */
|
|
{"username", .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM}, /* 1 */
|
|
{"password", .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM}, /* 2 */
|
|
{"failover", .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM}, /* 3 */
|
|
{"useragent", .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM}, /* 4 */
|
|
{"verify_peer", .f = cfg_parse_bool_opt}, /* 5 */
|
|
{"verify_host", .f = cfg_parse_bool_opt}, /* 6 */
|
|
{"client_cert", .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM}, /* 7 */
|
|
{"client_key", .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM}, /* 8 */
|
|
{"cipher_suites", .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM}, /* 9 */
|
|
{"tlsversion", .f = cfg_parse_enum_opt, .param = tls_versions}, /* 10 */
|
|
{"timeout", .f = cfg_parse_int_opt}, /* 11 */
|
|
{"maxdatasize", .f = cfg_parse_int_opt}, /* 12 */
|
|
{"httpredirect", .f = cfg_parse_bool_opt}, /* 13 */
|
|
{"httpproxy", .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM}, /* 14 */
|
|
{"httpproxyport", .f = cfg_parse_int_opt}, /* 15 */
|
|
{"authmethod", .f = cfg_parse_int_opt}, /* 16 */
|
|
{0}
|
|
};
|
|
|
|
/*! Count the number of connections
|
|
*/
|
|
unsigned int curl_connection_count()
|
|
{
|
|
unsigned int i = 0;
|
|
curl_con_t *cc;
|
|
cc = _curl_con_root;
|
|
while(cc)
|
|
{
|
|
i++;
|
|
cc = cc->next;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
/*! Find CURL connection by name
|
|
*/
|
|
curl_con_t* curl_get_connection(str *name)
|
|
{
|
|
curl_con_t *cc;
|
|
unsigned int conid;
|
|
|
|
conid = core_case_hash(name, 0, 0);
|
|
LM_DBG("curl_get_connection looking for httpcon: [%.*s] ID %u\n", name->len, name->s, conid);
|
|
|
|
cc = _curl_con_root;
|
|
while(cc)
|
|
{
|
|
if(conid==cc->conid && cc->name.len==name->len && strncmp(cc->name.s, name->s, name->len)==0) {
|
|
return cc;
|
|
}
|
|
cc = cc->next;
|
|
}
|
|
LM_DBG("curl_get_connection no success in looking for httpcon: [%.*s]\n", name->len, name->s);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*! Parse the httpcon module parameter
|
|
*
|
|
* Syntax:
|
|
* name => proto://user:password@server/url/url
|
|
* name => proto://server/url/url
|
|
* name => proto://server/url/url;param=value;param=value
|
|
*
|
|
* the url is very much like CURLs syntax
|
|
* the url is a base url where you can add local address
|
|
* Parameters
|
|
* httpredirect
|
|
* timeout
|
|
* useragent
|
|
* failover
|
|
* maxdatasize
|
|
* verifypeer
|
|
* verifyhost
|
|
*
|
|
*/
|
|
int curl_parse_param(char *val)
|
|
{
|
|
str name = STR_NULL;
|
|
str schema = STR_NULL;
|
|
str url = STR_NULL;
|
|
str username = STR_NULL;
|
|
str password = STR_NULL;
|
|
str params = STR_NULL;
|
|
str failover = STR_NULL;
|
|
|
|
str client_cert = default_tls_clientcert;
|
|
str client_key = default_tls_clientkey;
|
|
str ciphersuites = default_cipher_suite_list;
|
|
str useragent = default_useragent;
|
|
str http_proxy = default_http_proxy;
|
|
|
|
unsigned int http_proxy_port = default_http_proxy_port;
|
|
unsigned int maxdatasize = default_maxdatasize;
|
|
unsigned int timeout = default_connection_timeout;
|
|
unsigned int http_follow_redirect = default_http_follow_redirect;
|
|
unsigned int verify_peer = default_tls_verify_peer;
|
|
unsigned int verify_host = default_tls_verify_host;
|
|
unsigned int tlsversion = default_tls_version;
|
|
unsigned int authmethod = default_authmethod;
|
|
|
|
str in;
|
|
char *p;
|
|
char *u;
|
|
param_t *conparams = NULL;
|
|
curl_con_t *cc = NULL;
|
|
|
|
LM_INFO("http_client modparam parsing starting\n");
|
|
LM_DBG("modparam httpcon: %s\n", val);
|
|
LM_DBG(" *** Default httproxy: %s\n", http_proxy.s);
|
|
|
|
/* parse: name=>http_url*/
|
|
in.s = val;
|
|
in.len = strlen(in.s);
|
|
p = in.s;
|
|
|
|
/* Skip white space */
|
|
while(p < in.s+in.len && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r')) {
|
|
p++;
|
|
}
|
|
if(p > in.s+in.len || *p=='\0') {
|
|
goto error;
|
|
}
|
|
|
|
/* This is the connection name */
|
|
name.s = p;
|
|
/* Skip to whitespace */
|
|
while(p < in.s + in.len)
|
|
{
|
|
if(*p=='=' || *p==' ' || *p=='\t' || *p=='\n' || *p=='\r') {
|
|
break;
|
|
}
|
|
p++;
|
|
}
|
|
if(p > in.s+in.len || *p=='\0') {
|
|
goto error;
|
|
}
|
|
name.len = p - name.s;
|
|
if(*p != '=')
|
|
{
|
|
/* Skip whitespace */
|
|
while(p<in.s+in.len && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r')) {
|
|
p++;
|
|
}
|
|
if(p>in.s+in.len || *p=='\0' || *p!='=') {
|
|
goto error;
|
|
}
|
|
}
|
|
p++;
|
|
if(*p != '>') {
|
|
goto error;
|
|
}
|
|
p++;
|
|
/* Skip white space again */
|
|
while(p < in.s+in.len && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r')) {
|
|
p++;
|
|
}
|
|
schema.s = p;
|
|
/* Skip to colon ':' */
|
|
while(p < in.s + in.len)
|
|
{
|
|
if(*p == ':') {
|
|
break;
|
|
}
|
|
p++;
|
|
}
|
|
if(*p != ':') {
|
|
goto error;
|
|
}
|
|
schema.len = p - schema.s;
|
|
p++; /* Skip the colon */
|
|
/* Skip two slashes */
|
|
if(*p != '/') {
|
|
goto error;
|
|
}
|
|
p++;
|
|
if(*p != '/') {
|
|
goto error;
|
|
}
|
|
p++;
|
|
/* We are now at the first character after :// */
|
|
url.s = p;
|
|
url.len = in.len + (int)(in.s - p);
|
|
u = p;
|
|
|
|
/* Now check if there is a @ character. If so, we need to parse the username
|
|
and password */
|
|
/* Skip to at-sign '@' */
|
|
while(p < in.s + in.len)
|
|
{
|
|
if(*p == '@') {
|
|
break;
|
|
}
|
|
p++;
|
|
}
|
|
if (*p == '@') {
|
|
/* We have a username and possibly password - parse them out */
|
|
username.s = u;
|
|
while (u < p) {
|
|
if (*u == ':') {
|
|
break;
|
|
}
|
|
u++;
|
|
}
|
|
username.len = u - username.s;
|
|
|
|
/* We either have a : or a @ */
|
|
if (*u == ':') {
|
|
u++;
|
|
/* Go look for password */
|
|
password.s = u;
|
|
while (u < p) {
|
|
u++;
|
|
}
|
|
password.len = u - password.s;
|
|
}
|
|
p++; /* Skip the at sign */
|
|
url.s = p;
|
|
url.len = in.len + (int)(in.s - p);
|
|
}
|
|
/* Reset P to beginning of URL and look for parameters - starting with ; */
|
|
p = url.s;
|
|
/* Skip to ';' or end of string */
|
|
while(p < url.s + url.len)
|
|
{
|
|
if(*p == ';') {
|
|
/* Cut off URL at the ; */
|
|
url.len = (int)(p - url.s);
|
|
break;
|
|
}
|
|
p++;
|
|
}
|
|
if (*p == ';') {
|
|
/* We have parameters */
|
|
str tok;
|
|
param_t *pit = NULL;
|
|
|
|
/* Adjust the URL length */
|
|
|
|
p++; /* Skip the ; */
|
|
params.s = p;
|
|
params.len = in.len + (int) (in.s - p);
|
|
param_hooks_t phooks;
|
|
|
|
if (parse_params(¶ms, CLASS_ANY, &phooks, &conparams) < 0)
|
|
{
|
|
LM_ERR("CURL failed parsing httpcon parameters value\n");
|
|
goto error;
|
|
}
|
|
|
|
/* Have parameters */
|
|
for (pit = conparams; pit; pit=pit->next)
|
|
{
|
|
tok = pit->body;
|
|
if(pit->name.len==12 && strncmp(pit->name.s, "httpredirect", 12)==0) {
|
|
if(str2int(&tok, &http_follow_redirect) != 0) {
|
|
/* Bad value */
|
|
LM_WARN("curl connection [%.*s]: httpredirect bad value. Using default\n", name.len, name.s);
|
|
http_follow_redirect = default_http_follow_redirect;
|
|
}
|
|
if (http_follow_redirect != 0 && http_follow_redirect != 1) {
|
|
LM_WARN("curl connection [%.*s]: httpredirect bad value. Using default\n", name.len, name.s);
|
|
http_follow_redirect = default_http_follow_redirect;
|
|
}
|
|
LM_DBG("curl [%.*s] - httpredirect [%d]\n", pit->name.len, pit->name.s, http_follow_redirect);
|
|
} else if(pit->name.len==7 && strncmp(pit->name.s, "timeout", 7)==0) {
|
|
if(str2int(&tok, &timeout)!=0) {
|
|
/* Bad timeout */
|
|
LM_WARN("curl connection [%.*s]: timeout bad value. Using default\n", name.len, name.s);
|
|
timeout = default_connection_timeout;
|
|
}
|
|
LM_DBG("curl [%.*s] - timeout [%d]\n", pit->name.len, pit->name.s, timeout);
|
|
} else if(pit->name.len==9 && strncmp(pit->name.s, "useragent", 9)==0) {
|
|
useragent = tok;
|
|
LM_DBG("curl [%.*s] - useragent [%.*s]\n", pit->name.len, pit->name.s,
|
|
useragent.len, useragent.s);
|
|
} else if(pit->name.len==8 && strncmp(pit->name.s, "failover", 8)==0) {
|
|
failover = tok;
|
|
LM_DBG("curl [%.*s] - failover [%.*s]\n", pit->name.len, pit->name.s,
|
|
failover.len, failover.s);
|
|
} else if(pit->name.len==11 && strncmp(pit->name.s, "maxdatasize", 11)==0) {
|
|
if(str2int(&tok, &maxdatasize)!=0) {
|
|
/* Bad timeout */
|
|
LM_WARN("curl connection [%.*s]: maxdatasize bad value. Using default\n", name.len, name.s);
|
|
maxdatasize = default_maxdatasize;
|
|
}
|
|
LM_DBG("curl [%.*s] - maxdatasize [%d]\n", pit->name.len, pit->name.s, maxdatasize);
|
|
} else if(pit->name.len==11 && strncmp(pit->name.s, "verify_peer", 11)==0) {
|
|
if(str2int(&tok, &verify_peer)!=0) {
|
|
/* Bad integer */
|
|
LM_WARN("curl connection [%.*s]: verify_peer bad value. Using default\n", name.len, name.s);
|
|
verify_peer = default_tls_verify_peer;
|
|
}
|
|
if (verify_peer != 0 && verify_peer != 1) {
|
|
LM_WARN("curl connection [%.*s]: verify_peer bad value. Using default\n", name.len, name.s);
|
|
verify_peer = default_tls_verify_peer;
|
|
}
|
|
LM_DBG("curl [%.*s] - verify_peer [%d]\n", pit->name.len, pit->name.s, verify_peer);
|
|
} else if(pit->name.len==11 && strncmp(pit->name.s, "verify_host", 11)==0) {
|
|
if(str2int(&tok, &verify_host)!=0) {
|
|
/* Bad integer */
|
|
LM_WARN("curl connection [%.*s]: verify_host bad value. Using default\n", name.len, name.s);
|
|
verify_host = default_tls_verify_host;
|
|
}
|
|
LM_DBG("curl [%.*s] - verify_host [%d]\n", pit->name.len, pit->name.s, verify_host);
|
|
} else if(pit->name.len==10 && strncmp(pit->name.s, "tlsversion", 10)==0) {
|
|
if(str2int(&tok, &tlsversion)!=0) {
|
|
/* Bad integer */
|
|
LM_WARN("curl connection [%.*s]: tlsversion bad value. Using default\n", name.len, name.s);
|
|
tlsversion = default_tls_version;
|
|
}
|
|
if (tlsversion >= CURL_SSLVERSION_LAST) {
|
|
LM_WARN("curl connection [%.*s]: tlsversion unsupported value. Using default\n", name.len, name.s);
|
|
tlsversion = default_tls_version;
|
|
}
|
|
LM_DBG("curl [%.*s] - tlsversion [%d]\n", pit->name.len, pit->name.s, tlsversion);
|
|
} else if(pit->name.len==11 && strncmp(pit->name.s, "client_cert", 11)==0) {
|
|
client_cert = tok;
|
|
LM_DBG("curl [%.*s] - client_cert [%.*s]\n", pit->name.len, pit->name.s,
|
|
client_cert.len, client_cert.s);
|
|
} else if(pit->name.len==10 && strncmp(pit->name.s, "client_key", 10)==0) {
|
|
client_key = tok;
|
|
LM_DBG("curl [%.*s] - client_key [%.*s]\n", pit->name.len, pit->name.s,
|
|
client_key.len, client_key.s);
|
|
} else if(pit->name.len==13 && strncmp(pit->name.s, "cipher_suites", 13)==0) {
|
|
ciphersuites = tok;
|
|
LM_DBG("curl [%.*s] - cipher_suites [%.*s]\n", pit->name.len, pit->name.s,
|
|
ciphersuites.len, ciphersuites.s);
|
|
} else {
|
|
LM_ERR("curl Unknown parameter [%.*s] \n", pit->name.len, pit->name.s);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* The URL ends either with nothing or parameters. Parameters start with ; */
|
|
|
|
if(conparams != NULL) {
|
|
free_params(conparams);
|
|
}
|
|
|
|
cc = curl_init_con(&name);
|
|
if (cc == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
cc->username = username.s ? as_asciiz(&username) : NULL;
|
|
cc->password = password.s ? as_asciiz(&password) : NULL;
|
|
cc->schema = schema;
|
|
cc->authmethod = authmethod;
|
|
cc->failover = failover;
|
|
cc->useragent = as_asciiz(&useragent);
|
|
cc->url = url;
|
|
cc->clientcert = client_cert.s ? as_asciiz(&client_cert) : NULL;
|
|
cc->clientkey = client_key.s ? as_asciiz(&client_key) : NULL;
|
|
cc->ciphersuites = ciphersuites.s ? as_asciiz(&ciphersuites) : NULL;
|
|
cc->tlsversion = tlsversion;
|
|
cc->verify_peer = verify_peer;
|
|
cc->verify_host = verify_host;
|
|
cc->timeout = timeout;
|
|
cc->maxdatasize = maxdatasize;
|
|
if (http_proxy_port > 0) {
|
|
cc->http_proxy_port = http_proxy_port;
|
|
cc->http_proxy = http_proxy.s ? as_asciiz(&http_proxy) : NULL;
|
|
LM_DBG("*** Setting HTTP proxy for connection to %s \n", cc->http_proxy);
|
|
}
|
|
cc->http_follow_redirect = http_follow_redirect;
|
|
|
|
LM_DBG("cname: [%.*s] url: [%.*s] username [%s] password [%s] failover [%.*s] timeout [%d] useragent [%s] maxdatasize [%d]\n",
|
|
cc->name.len, cc->name.s, cc->url.len, cc->url.s, cc->username ? cc->username : "", cc->password ? cc->password : "",
|
|
cc->failover.len, cc->failover.s, cc->timeout, cc->useragent, cc->maxdatasize);
|
|
LM_DBG("cname: [%.*s] client_cert [%s] client_key [%s] ciphersuites [%s] tlsversion [%d] verify_peer [%d] verify_host [%d] authmethod [%d]\n",
|
|
cc->name.len, cc->name.s, cc->clientcert, cc->clientkey, cc->ciphersuites, cc->tlsversion, cc->verify_peer, cc->verify_host, cc->authmethod);
|
|
if (cc->http_proxy_port > 0) {
|
|
LM_DBG("cname: [%.*s] http_proxy [%s] http_proxy_port [%d]\n",
|
|
cc->name.len, cc->name.s, cc->http_proxy, cc->http_proxy_port);
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
LM_ERR("invalid curl parameter [%.*s] at [%d]\n", in.len, in.s, (int)(p-in.s));
|
|
|
|
if(conparams != NULL) {
|
|
free_params(conparams);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int curl_parse_conn(void *param, cfg_parser_t *parser, unsigned int flags)
|
|
{
|
|
str name = STR_NULL;
|
|
|
|
raw_http_client_conn_t *raw_cc = NULL;
|
|
int i, ret;
|
|
cfg_token_t t;
|
|
|
|
/* Get the name from the section header */
|
|
|
|
ret = cfg_get_token(&t, parser, 0);
|
|
if (ret < 0) return -1;
|
|
if ((ret > 0) || (t.type != CFG_TOKEN_ALPHA))
|
|
{
|
|
LM_ERR("%s:%d:%d: Invalid or missing connection name\n",
|
|
parser->file, t.start.line, t.start.col);
|
|
return -1;
|
|
}
|
|
pkg_str_dup(&name, &t.val);
|
|
ret = cfg_get_token(&t, parser, 0);
|
|
if (ret < 0) return -1;
|
|
if ((ret > 0) || (t.type != ']'))
|
|
{
|
|
LM_ERR("%s:%d:%d: Syntax error, ']' expected\n",
|
|
parser->file, t.start.line, t.start.col);
|
|
return -1;
|
|
}
|
|
|
|
if (cfg_eat_eol(parser, flags)) return -1;
|
|
|
|
raw_cc = pkg_malloc(sizeof(raw_http_client_conn_t));
|
|
if (raw_cc == NULL) {
|
|
return -1;
|
|
}
|
|
memset(raw_cc, 0, sizeof(raw_http_client_conn_t));
|
|
raw_cc->next = raw_conn_list;
|
|
raw_conn_list = raw_cc;
|
|
raw_cc->name = name;
|
|
/* Set default values - memory freed if overridden */
|
|
if (default_tls_clientcert.s != NULL)
|
|
pkg_str_dup(&raw_cc->clientcert, &default_tls_clientcert);
|
|
if (default_tls_clientkey.s != NULL)
|
|
pkg_str_dup(&raw_cc->clientkey, &default_tls_clientkey);
|
|
if (default_cipher_suite_list.s != NULL)
|
|
pkg_str_dup(&raw_cc->ciphersuites, &default_cipher_suite_list);
|
|
pkg_str_dup(&raw_cc->useragent, &default_useragent);
|
|
if (default_http_proxy_port > 0) {
|
|
raw_cc->http_proxy_port = default_http_proxy_port;
|
|
if (default_http_proxy.s != NULL) {
|
|
pkg_str_dup(&raw_cc->http_proxy, &default_http_proxy);
|
|
}
|
|
}
|
|
raw_cc->verify_peer = default_tls_verify_peer;
|
|
raw_cc->verify_host = default_tls_verify_host;
|
|
raw_cc->maxdatasize = default_maxdatasize;
|
|
raw_cc->timeout = default_connection_timeout;
|
|
raw_cc->http_follow_redirect = default_http_follow_redirect;
|
|
raw_cc->tlsversion = default_tls_version;
|
|
raw_cc->authmethod = default_authmethod;
|
|
|
|
for(i = 0; tls_versions[i].name; i++) {
|
|
tls_versions[i].param = &raw_cc->tlsversion;
|
|
}
|
|
/* Index from above structure (see top of file) */
|
|
http_client_options[0].param = &raw_cc->url;
|
|
http_client_options[1].param = &raw_cc->username;
|
|
http_client_options[2].param = &raw_cc->password;
|
|
http_client_options[3].param = &raw_cc->failover;
|
|
http_client_options[4].param = &raw_cc->useragent;
|
|
http_client_options[5].param = &raw_cc->verify_peer;
|
|
http_client_options[6].param = &raw_cc->verify_host;
|
|
http_client_options[7].param = &raw_cc->clientcert;
|
|
http_client_options[8].param = &raw_cc->clientkey;
|
|
http_client_options[9].param = &raw_cc->ciphersuites;
|
|
/* tlsversion is set using enum types */
|
|
http_client_options[11].param = &raw_cc->timeout;
|
|
http_client_options[12].param = &raw_cc->maxdatasize;
|
|
http_client_options[13].param = &raw_cc->http_follow_redirect;
|
|
http_client_options[14].param = &raw_cc->http_proxy;
|
|
http_client_options[15].param = &raw_cc->http_proxy_port;
|
|
http_client_options[16].param = &raw_cc->authmethod;
|
|
|
|
cfg_set_options(parser, http_client_options);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int fixup_raw_http_client_conn_list(void)
|
|
{
|
|
raw_http_client_conn_t *raw_cc = NULL;
|
|
curl_con_t *cc = NULL;
|
|
str schema, url;
|
|
char *pos, *end;
|
|
int ret = 1;
|
|
|
|
for (raw_cc = raw_conn_list; raw_cc != NULL; raw_cc = raw_cc->next)
|
|
{
|
|
cc = curl_init_con(&raw_cc->name);
|
|
if (cc == NULL) {
|
|
ret = -1;
|
|
goto done;
|
|
}
|
|
/* Parse raw URL into schema + hostname/url */
|
|
schema.s = raw_cc->url.s;
|
|
pos = schema.s;
|
|
end = raw_cc->url.s + raw_cc->url.len;
|
|
while ((pos < end) && (*pos != '\0'))
|
|
{
|
|
if (*pos == ':') break;
|
|
pos++;
|
|
}
|
|
if (pos[0] != ':' || pos[1] != '/' || pos[2] != '/' || (end-pos < 4))
|
|
{
|
|
LM_ERR("Invalid schema://url definition [%.*s]\n", raw_cc->url.len, raw_cc->url.s);
|
|
ret = -1;
|
|
goto done;
|
|
}
|
|
schema.len = (int)(pos - schema.s);
|
|
|
|
url.s = pos+3;
|
|
url.len = end - url.s;
|
|
|
|
pkg_str_dup(&cc->schema, &schema);
|
|
pkg_str_dup(&cc->url, &url);
|
|
|
|
cc->username = raw_cc->username.s ? as_asciiz(&raw_cc->username) : NULL;
|
|
cc->password = raw_cc->password.s ? as_asciiz(&raw_cc->password) : NULL;
|
|
cc->authmethod = raw_cc->authmethod;
|
|
if (raw_cc->failover.s != NULL)
|
|
pkg_str_dup(&cc->failover, &raw_cc->failover);
|
|
cc->useragent = as_asciiz(&raw_cc->useragent);
|
|
cc->clientcert = raw_cc->clientcert.s ? as_asciiz(&raw_cc->clientcert) : NULL;
|
|
cc->clientkey = raw_cc->clientkey.s ? as_asciiz(&raw_cc->clientkey) : NULL;
|
|
cc->ciphersuites = raw_cc->ciphersuites.s ? as_asciiz(&raw_cc->ciphersuites) : NULL;
|
|
cc->tlsversion = raw_cc->tlsversion;
|
|
if (cc->tlsversion >= CURL_SSLVERSION_LAST) {
|
|
LM_WARN("curl connection [%.*s]: tlsversion %d unsupported value. Using default\n", cc->name.len, cc->name.s, cc->tlsversion);
|
|
cc->tlsversion = default_tls_version;
|
|
}
|
|
cc->http_proxy_port = raw_cc->http_proxy_port;
|
|
if (cc->http_proxy_port > 0 && raw_cc->http_proxy.s != NULL) {
|
|
cc->http_proxy = raw_cc->http_proxy.s ? as_asciiz(&raw_cc->http_proxy) : NULL;
|
|
}
|
|
|
|
cc->verify_peer = raw_cc->verify_peer;
|
|
cc->verify_host = raw_cc->verify_host;
|
|
cc->timeout = raw_cc->timeout;
|
|
cc->maxdatasize = raw_cc->maxdatasize;
|
|
cc->http_follow_redirect = raw_cc->http_follow_redirect;
|
|
|
|
LM_DBG("cname: [%.*s] url: [%.*s] username [%s] password [%s] failover [%.*s] timeout [%d] useragent [%s] maxdatasize [%d]\n",
|
|
cc->name.len, cc->name.s, cc->url.len, cc->url.s, cc->username ? cc->username : "", cc->password ? cc->password : "",
|
|
cc->failover.len, cc->failover.s, cc->timeout, cc->useragent, cc->maxdatasize);
|
|
LM_DBG("cname: [%.*s] client_cert [%s] client_key [%s] ciphersuites [%s] tlsversion [%d] verify_peer [%d] verify_host [%d]\n",
|
|
cc->name.len, cc->name.s, cc->clientcert, cc->clientkey, cc->ciphersuites, cc->tlsversion, cc->verify_peer, cc->verify_host);
|
|
if (cc->http_proxy_port > 0) {
|
|
LM_DBG("cname: [%.*s] http_proxy [%s] http_proxy_port [%d]\n",
|
|
cc->name.len, cc->name.s, cc->http_proxy, cc->http_proxy_port);
|
|
}
|
|
|
|
}
|
|
done:
|
|
while (raw_conn_list != NULL)
|
|
{
|
|
raw_cc = raw_conn_list;
|
|
if (raw_cc->name.s) pkg_free(raw_cc->name.s);
|
|
if (raw_cc->url.s) pkg_free(raw_cc->url.s);
|
|
if (raw_cc->username.s) pkg_free(raw_cc->username.s);
|
|
if (raw_cc->password.s) pkg_free(raw_cc->password.s);
|
|
if (raw_cc->failover.s) pkg_free(raw_cc->failover.s);
|
|
if (raw_cc->useragent.s) pkg_free(raw_cc->useragent.s);
|
|
if (raw_cc->clientcert.s) pkg_free(raw_cc->clientcert.s);
|
|
if (raw_cc->clientkey.s) pkg_free(raw_cc->clientkey.s);
|
|
if (raw_cc->ciphersuites.s) pkg_free(raw_cc->ciphersuites.s);
|
|
if (raw_cc->http_proxy.s) pkg_free(raw_cc->http_proxy.s);
|
|
pkg_free(raw_cc);
|
|
raw_conn_list = raw_conn_list->next;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int http_client_load_config(str *config_file)
|
|
{
|
|
cfg_parser_t *parser;
|
|
str empty = STR_NULL;
|
|
|
|
if ((parser = cfg_parser_init(&empty, config_file)) == NULL)
|
|
{
|
|
LM_ERR("Failed to init http_client config file parser\n");
|
|
goto error;
|
|
}
|
|
|
|
cfg_section_parser(parser, curl_parse_conn, NULL);
|
|
if (sr_cfg_parse(parser))
|
|
goto error;
|
|
cfg_parser_close(parser);
|
|
|
|
fixup_raw_http_client_conn_list();
|
|
return 0;
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
/*! Init connection structure and place it in structure
|
|
*/
|
|
curl_con_t *curl_init_con(str *name)
|
|
{
|
|
curl_con_t *cc;
|
|
unsigned int conid;
|
|
|
|
conid = core_case_hash(name, 0, 0);
|
|
LM_DBG("curl_init_con httpcon: [%.*s] ID %u\n", name->len, name->s, conid);
|
|
|
|
cc = _curl_con_root;
|
|
while(cc)
|
|
{
|
|
if(conid==cc->conid && cc->name.len == name->len
|
|
&& strncmp(cc->name.s, name->s, name->len)==0)
|
|
{
|
|
LM_ERR("duplicate Curl connection name\n");
|
|
return NULL;
|
|
}
|
|
cc = cc->next;
|
|
}
|
|
|
|
cc = (curl_con_t*) pkg_malloc(sizeof(curl_con_t));
|
|
if(cc == NULL)
|
|
{
|
|
LM_ERR("no pkg memory\n");
|
|
return NULL;
|
|
}
|
|
memset(cc, 0, sizeof(curl_con_t));
|
|
cc->next = _curl_con_root;
|
|
cc->conid = conid;
|
|
_curl_con_root = cc;
|
|
cc->name = *name;
|
|
|
|
LM_INFO("CURL: Added connection [%.*s]\n", name->len, name->s);
|
|
return cc;
|
|
}
|