433 lines
11 KiB
C
433 lines
11 KiB
C
/* vim: set et ts=4 sts=4 sw=4 : */
|
|
/********************************************************************\
|
|
* This program 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. *
|
|
* *
|
|
* This program 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, contact: *
|
|
* *
|
|
* Free Software Foundation Voice: +1-617-542-5942 *
|
|
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
|
|
* Boston, MA 02111-1307, USA gnu@gnu.org *
|
|
* *
|
|
\********************************************************************/
|
|
|
|
/** @file config.c
|
|
@brief xfrpc client config related
|
|
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <time.h>
|
|
|
|
#include <syslog.h>
|
|
#include <sys/utsname.h>
|
|
|
|
#include "ini.h"
|
|
#include "uthash.h"
|
|
#include "config.h"
|
|
#include "client.h"
|
|
#include "debug.h"
|
|
#include "msg.h"
|
|
#include "utils.h"
|
|
#include "version.h"
|
|
|
|
static struct common_conf *c_conf;
|
|
static struct proxy_service *all_ps;
|
|
|
|
static void new_ftp_data_proxy_service(struct proxy_service *ftp_ps);
|
|
|
|
struct common_conf *
|
|
get_common_config()
|
|
{
|
|
return c_conf;
|
|
};
|
|
|
|
void
|
|
free_common_config()
|
|
{
|
|
struct common_conf *c_conf = get_common_config();
|
|
|
|
if (c_conf->server_addr) free(c_conf->server_addr);
|
|
if (c_conf->auth_token) free(c_conf->auth_token);
|
|
};
|
|
|
|
static int
|
|
is_true(const char *val)
|
|
{
|
|
if (val && (strcmp(val, "true") == 0 || strcmp(val, "1") == 0))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const char *
|
|
get_valid_type(const char *val)
|
|
{
|
|
if (!val)
|
|
return NULL;
|
|
|
|
#define MATCH_VALUE(s) strcmp(val, s) == 0
|
|
if (MATCH_VALUE("tcp") ||
|
|
MATCH_VALUE("socks5") ||
|
|
MATCH_VALUE("http") ||
|
|
MATCH_VALUE("https")) {
|
|
|
|
return val;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
dump_common_conf()
|
|
{
|
|
if(! c_conf) {
|
|
debug(LOG_ERR, "Error: c_conf is NULL");
|
|
return;
|
|
}
|
|
|
|
debug(LOG_DEBUG, "Section[common]: {server_addr:%s, server_port:%d, auth_token:%s, interval:%d, timeout:%d}",
|
|
c_conf->server_addr, c_conf->server_port, c_conf->auth_token,
|
|
c_conf->heartbeat_interval, c_conf->heartbeat_timeout);
|
|
}
|
|
|
|
static void
|
|
dump_proxy_service(const int index, struct proxy_service *ps)
|
|
{
|
|
if (!ps)
|
|
return;
|
|
|
|
if (NULL == ps->proxy_type) {
|
|
ps->proxy_type = strdup("tcp");
|
|
assert(ps->proxy_type);
|
|
} else if (strcmp(ps->proxy_type, "ftp") == 0) {
|
|
new_ftp_data_proxy_service(ps);
|
|
}
|
|
|
|
if (!validate_proxy(ps)) {
|
|
debug(LOG_ERR, "Error: validate_proxy failed");
|
|
exit(-1);
|
|
}
|
|
|
|
debug(LOG_DEBUG,
|
|
"Proxy service %d: {name:%s, local_port:%d, type:%s, use_encryption:%d, use_compression:%d, custom_domains:%s, subdomain:%s, locations:%s, host_header_rewrite:%s, http_user:%s, http_pwd:%s}",
|
|
index,
|
|
ps->proxy_name,
|
|
ps->local_port,
|
|
ps->proxy_type,
|
|
ps->use_encryption,
|
|
ps->use_compression,
|
|
ps->custom_domains,
|
|
ps->subdomain,
|
|
ps->locations,
|
|
ps->host_header_rewrite,
|
|
ps->http_user,
|
|
ps->http_pwd);
|
|
}
|
|
|
|
static void
|
|
dump_all_ps()
|
|
{
|
|
struct proxy_service *ps = NULL, *tmp = NULL;
|
|
|
|
int index = 0;
|
|
HASH_ITER(hh, all_ps, ps, tmp) {
|
|
dump_proxy_service(index++, ps);
|
|
}
|
|
}
|
|
|
|
static struct proxy_service *
|
|
new_proxy_service(const char *name)
|
|
{
|
|
if (! name)
|
|
return NULL;
|
|
|
|
struct proxy_service *ps = (struct proxy_service *)calloc(sizeof(struct proxy_service), 1);
|
|
assert(ps);
|
|
assert(c_conf);
|
|
|
|
ps->proxy_name = strdup(name);
|
|
ps->ftp_cfg_proxy_name = NULL;
|
|
assert(ps->proxy_name);
|
|
|
|
ps->proxy_type = NULL;
|
|
ps->use_encryption = 0;
|
|
ps->local_port = -1;
|
|
ps->remote_port = -1;
|
|
ps->remote_data_port = -1;
|
|
ps->use_compression = 0;
|
|
ps->use_encryption = 0;
|
|
|
|
ps->custom_domains = NULL;
|
|
ps->subdomain = NULL;
|
|
ps->locations = NULL;
|
|
ps->host_header_rewrite = NULL;
|
|
ps->http_user = NULL;
|
|
ps->http_pwd = NULL;
|
|
|
|
return ps;
|
|
}
|
|
|
|
// create a new proxy service with suffix "_ftp_data_proxy"
|
|
static void
|
|
new_ftp_data_proxy_service(struct proxy_service *ftp_ps)
|
|
{
|
|
struct proxy_service *ps = NULL;
|
|
char *ftp_data_proxy_name = get_ftp_data_proxy_name((const char *)ftp_ps->proxy_name);
|
|
|
|
HASH_FIND_STR(all_ps, ftp_data_proxy_name, ps);
|
|
if (!ps) {
|
|
ps = new_proxy_service(ftp_data_proxy_name);
|
|
if (! ps) {
|
|
debug(LOG_ERR,
|
|
"cannot create ftp data proxy service, it should not happenned!");
|
|
exit(0);
|
|
}
|
|
|
|
ps->ftp_cfg_proxy_name = strdup(ftp_ps->proxy_name);
|
|
assert(ps->ftp_cfg_proxy_name);
|
|
|
|
ps->proxy_type = strdup("tcp");
|
|
ps->remote_port = ftp_ps->remote_data_port;
|
|
ps->local_ip = ftp_ps->local_ip;
|
|
ps->local_port = 0; //will be init in working tunnel connectting
|
|
|
|
HASH_ADD_KEYPTR(hh, all_ps, ps->proxy_name, strlen(ps->proxy_name), ps);
|
|
}
|
|
|
|
free(ftp_data_proxy_name);
|
|
}
|
|
|
|
int
|
|
validate_proxy(struct proxy_service *ps)
|
|
{
|
|
if (!ps || !ps->proxy_name || !ps->proxy_type)
|
|
return 0;
|
|
|
|
if (strcmp(ps->proxy_type, "socks5") == 0) {
|
|
if (ps->remote_port == 0) {
|
|
debug(LOG_ERR, "Proxy [%s] error: remote_port not found", ps->proxy_name);
|
|
return 0;
|
|
}
|
|
} else if (strcmp(ps->proxy_type, "tcp") == 0) {
|
|
if (ps->remote_port == 0 || ps->local_port == 0 || ps->local_ip == NULL) {
|
|
debug(LOG_ERR, "Proxy [%s] error: remote_port or local_port or local_ip not found", ps->proxy_name);
|
|
return 0;
|
|
}
|
|
} else if (strcmp(ps->proxy_type, "http") == 0 || strcmp(ps->proxy_type, "https") == 0) {
|
|
if (ps->local_port == 0 || ps->local_ip == NULL) {
|
|
debug(LOG_ERR, "Proxy [%s] error: local_port or local_ip not found", ps->proxy_name);
|
|
return 0;
|
|
}
|
|
// custom_domains and subdomain can not be set at the same time
|
|
// but one of them must be set
|
|
if (ps->custom_domains && ps->subdomain) {
|
|
debug(LOG_ERR, "Proxy [%s] error: custom_domains and subdomain can not be set at the same time", ps->proxy_name);
|
|
return 0;
|
|
} else if (!ps->custom_domains && !ps->subdomain) {
|
|
debug(LOG_ERR, "Proxy [%s] error: custom_domains or subdomain must be set", ps->proxy_name);
|
|
return 0;
|
|
}
|
|
} else {
|
|
debug(LOG_ERR, "Proxy [%s] error: proxy_type not found", ps->proxy_name);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
proxy_service_handler(void *user, const char *sect, const char *nm, const char *value)
|
|
{
|
|
struct proxy_service *ps = NULL;
|
|
|
|
char *section = NULL;
|
|
section = strdup(sect);
|
|
assert(section);
|
|
|
|
if (strcmp(section, "common") == 0) {
|
|
SAFE_FREE(section);
|
|
return 0;
|
|
}
|
|
|
|
HASH_FIND_STR(all_ps, section, ps);
|
|
if (!ps) {
|
|
ps = new_proxy_service(section);
|
|
if (! ps) {
|
|
debug(LOG_ERR, "cannot create proxy service, it should not happenned!");
|
|
exit(0);
|
|
}
|
|
|
|
HASH_ADD_KEYPTR(hh, all_ps, ps->proxy_name, strlen(ps->proxy_name), ps);
|
|
}
|
|
|
|
#define MATCH_NAME(s) strcmp(nm, s) == 0
|
|
#define TO_BOOL(v) strcmp(value, "true") ? 0:1
|
|
|
|
if (MATCH_NAME("type")) {
|
|
if (! get_valid_type(value)) {
|
|
debug(LOG_ERR, "proxy service type %s is not supportted", value);
|
|
SAFE_FREE(section);
|
|
exit(0);
|
|
}
|
|
ps->proxy_type = strdup(value);
|
|
assert(ps->proxy_type);
|
|
} else if (MATCH_NAME("local_ip")) {
|
|
ps->local_ip = strdup(value);
|
|
assert(ps->local_ip);
|
|
} else if (MATCH_NAME("local_port")) {
|
|
ps->local_port = atoi(value);
|
|
} else if (MATCH_NAME("use_encryption")) {
|
|
ps->use_encryption = is_true(value);
|
|
} else if (MATCH_NAME("remote_port")) {
|
|
ps->remote_port = atoi(value);
|
|
} else if (MATCH_NAME("remote_data_port")) {
|
|
ps->remote_data_port = atoi(value);
|
|
} else if (MATCH_NAME("http_user")) {
|
|
ps->http_user = strdup(value);
|
|
} else if (MATCH_NAME("http_pwd")) {
|
|
ps->http_pwd = strdup(value);
|
|
} else if (MATCH_NAME("subdomain")) {
|
|
ps->subdomain = strdup(value);
|
|
} else if (MATCH_NAME("custom_domains")) {
|
|
ps->custom_domains = strdup(value);
|
|
assert(ps->custom_domains);
|
|
} else if (MATCH_NAME("locations")) {
|
|
ps->locations = strdup(value);
|
|
} else if (MATCH_NAME("host_header_rewrite")) {
|
|
ps->host_header_rewrite = strdup(value);
|
|
} else if (MATCH_NAME("use_encryption")) {
|
|
ps->use_encryption = TO_BOOL(value);
|
|
} else if (MATCH_NAME("use_compression")) {
|
|
ps->use_compression = TO_BOOL(value);
|
|
}
|
|
|
|
// if ps->proxy_type is socks5, and ps->remote_port is not set, set it to 1980
|
|
if (ps->proxy_type && strcmp(ps->proxy_type, "socks5") == 0 && ps->remote_port == 0) {
|
|
ps->remote_port = 1980;
|
|
}
|
|
|
|
SAFE_FREE(section);
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
common_handler(void *user, const char *section, const char *name, const char *value)
|
|
{
|
|
struct common_conf *config = (struct common_conf *)user;
|
|
|
|
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
|
|
if (MATCH("common", "server_addr")) {
|
|
SAFE_FREE(config->server_addr);
|
|
config->server_addr = strdup(value);
|
|
assert(config->server_addr);
|
|
} else if (MATCH("common", "server_port")) {
|
|
config->server_port = atoi(value);
|
|
} else if (MATCH("common", "heartbeat_interval")) {
|
|
config->heartbeat_interval = atoi(value);
|
|
} else if (MATCH("common", "heartbeat_timeout")) {
|
|
config->heartbeat_timeout = atoi(value);
|
|
} else if (MATCH("common", "token")) {
|
|
SAFE_FREE(config->auth_token);
|
|
config->auth_token = strdup(value);
|
|
assert(config->auth_token);
|
|
} else if (MATCH("common", "tcp_mux")) {
|
|
config->tcp_mux = atoi(value);
|
|
config->tcp_mux = !!config->tcp_mux;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
init_common_conf(struct common_conf *config)
|
|
{
|
|
if (!config)
|
|
return;
|
|
|
|
config->server_addr = strdup("0.0.0.0");
|
|
assert(config->server_addr);
|
|
config->server_port = 7000;
|
|
config->heartbeat_interval = 30;
|
|
config->heartbeat_timeout = 90;
|
|
config->tcp_mux = 1;
|
|
config->is_router = 0;
|
|
}
|
|
|
|
// it should be free after using
|
|
// because of assert it will never return NULL
|
|
char *get_ftp_data_proxy_name(const char *ftp_proxy_name)
|
|
{
|
|
char *ftp_tail_data_name = FTP_RMT_CTL_PROXY_SUFFIX;
|
|
char *ftp_data_proxy_name = (char *)calloc(1,
|
|
strlen(ftp_proxy_name)+strlen(ftp_tail_data_name)+1);
|
|
assert(ftp_data_proxy_name);
|
|
|
|
snprintf(ftp_data_proxy_name,
|
|
strlen(ftp_proxy_name) + strlen(ftp_tail_data_name) + 1,
|
|
"%s%s",
|
|
ftp_proxy_name,
|
|
ftp_tail_data_name);
|
|
|
|
return ftp_data_proxy_name;
|
|
}
|
|
|
|
void load_config(const char *confile)
|
|
{
|
|
c_conf = (struct common_conf *)calloc(sizeof(struct common_conf), 1);
|
|
assert(c_conf);
|
|
|
|
init_common_conf(c_conf);
|
|
|
|
debug(LOG_DEBUG, "Reading configuration file '%s'", confile);
|
|
|
|
if (ini_parse(confile, common_handler, c_conf) < 0) {
|
|
debug(LOG_ERR, "Config file parse failed");
|
|
exit(0);
|
|
}
|
|
|
|
dump_common_conf();
|
|
|
|
if (c_conf->heartbeat_interval <= 0) {
|
|
debug(LOG_ERR, "Error: heartbeat_interval <= 0");
|
|
exit(0);
|
|
}
|
|
|
|
if (c_conf->heartbeat_timeout < c_conf->heartbeat_interval) {
|
|
debug(LOG_ERR, "Error: heartbeat_timeout < heartbeat_interval");
|
|
exit(0);
|
|
}
|
|
|
|
ini_parse(confile, proxy_service_handler, NULL);
|
|
|
|
dump_all_ps();
|
|
}
|
|
|
|
int is_running_in_router()
|
|
{
|
|
return c_conf->is_router;
|
|
}
|
|
|
|
struct proxy_service *
|
|
get_proxy_service(const char *proxy_name)
|
|
{
|
|
struct proxy_service *ps = NULL;
|
|
HASH_FIND_STR(all_ps, proxy_name, ps);
|
|
return ps;
|
|
}
|
|
|
|
struct proxy_service *
|
|
get_all_proxy_services()
|
|
{
|
|
return all_ps;
|
|
}
|