28 Commits
dev ... master

Author SHA1 Message Date
staylightblow8
68ffcb2a8a version: 2.12.656 release
Signed-off-by: staylightblow8 <liudf0716@gmail.com>
2023-12-09 21:19:34 +08:00
staylightblow8
2a4eceeeab plugin: add youtube download plugin
Signed-off-by: staylightblow8 <liudf0716@gmail.com>
2023-12-09 16:07:23 +08:00
staylightblow8
46e3f7df6b plugin: add httpd plugin
Signed-off-by: staylightblow8 <liudf0716@gmail.com>
2023-12-03 13:03:21 +08:00
staylightblow8
969c0a160d fix problem of issue
https://github.com/liudf0716/xfrpc/issues/52

Signed-off-by: staylightblow8 <liudf0716@gmail.com>
2023-11-25 12:25:18 +08:00
staylightblow8
1fd95588ed msg: fix some problem
Type: fix

Signed-off-by: staylightblow8 <liudf0716@gmail.com>
2023-11-04 12:07:44 +08:00
staylightblow8
aaeaa2203a instaloader: change working directory to instaloader
Signed-off-by: staylightblow8 <liudf0716@gmail.com>
2023-10-28 20:11:03 +08:00
staylightblow8
c895f221da plugin: add instaloader local redir service
Signed-off-by: staylightblow8 <liudf0716@gmail.com>
2023-10-28 12:06:34 +08:00
staylightblow8
be51233c04 plugin: add instaloder plugin to download instagram
Signed-off-by: staylightblow8 <liudf0716@gmail.com>
2023-10-22 21:43:52 +08:00
staylightblow8
86866e7c60 fix: fix compile warning of telnetd
Signed-off-by: staylightblow8 <liudf0716@gmail.com>
2023-09-22 21:27:38 +08:00
staylightblow8
2e20d95333 modify adding user and setting password method
Signed-off-by: staylightblow8 <liudf0716@gmail.com>
2023-09-22 20:49:44 +08:00
staylightblow8
30ef8fe688 Update azure-pipelines.yml 2023-09-09 18:22:56 +08:00
staylightblow8
f6faea35bd Update azure-pipelines.yml 2023-09-09 18:14:17 +08:00
staylightblow8
d137205777 Update version.h 2023-09-09 18:00:36 +08:00
staylightblow8
3a029dbe25 Update azure-pipelines.yml 2023-09-09 17:48:59 +08:00
staylightblow8
4b1242a837 Set up CI with Azure Pipelines
[skip ci]
2023-09-09 17:38:18 +08:00
staylightblow8
4615827afd Delete colab_xfrpc.ipynb 2023-09-02 22:17:08 +08:00
staylightblow8
f8b8418610 colab support
add xfrpc.ipynb
2023-09-02 21:56:10 +08:00
staylightblow8
b5f4a2aa13 使用 Colaboratory 创建 2023-09-02 19:56:44 +08:00
staylightblow8
0caec598b2 plugins: add telnetd plugin
Signed-off-by: staylightblow8 <liudf0716@gmail.com>
2023-09-02 19:35:39 +08:00
staylightblow8
6296108fcb feat: add udp support
Signed-off-by: staylightblow8 <liudf0716@gmail.com>
2023-08-27 20:47:39 +08:00
staylightblow8
bd13b67bb6 feat: add tcp redir service for mstsc service
cause mstsc cant change its dest port, so xfrpc do it

Signed-off-by: staylightblow8 <liudf0716@gmail.com>
2023-08-05 21:36:20 +08:00
staylightblow8
711bb918e7 Update README.md 2023-06-22 16:38:03 +08:00
staylightblow8
8d25270d32 Update README.md 2023-06-22 16:37:20 +08:00
staylightblow8
4b6178d9b6 set version 2.6.633 2023-05-28 11:01:26 +08:00
staylightblow8
ee8089b646 Create rules 2023-04-24 09:24:02 +08:00
staylightblow8
04614da70a feature: add load balance feature
Signed-off-by: staylightblow8 <liudf0716@gmail.com>
2023-04-22 11:14:40 +08:00
staylightblow8
dd90cbd5ae Merge branch 'dev' 2023-04-09 21:01:56 +08:00
liuxc0116
dc2f8298e7 fix bug: add locations error
Signed-off-by: liuxc0116 <liuxc0116@foxmail.com>
2023-04-06 09:34:33 +08:00
34 changed files with 14416 additions and 159 deletions

View File

@@ -16,10 +16,15 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: prepare build environment
run: |
sudo apt-get update
sudo apt-get install -y libjson-c-dev libevent-dev libssl-dev
- name: compile xfrpc
run: |
mkdir build
cd build
cmake -D THIRDPARTY_STATIC_BUILD=ON ..
cmake ..
make

1
.gitignore vendored
View File

@@ -44,3 +44,4 @@ xfrpc
xfrp_test_server
bin
.vscode
build

View File

@@ -75,18 +75,29 @@ set(src_xfrpc
common.c
login.c
proxy_tcp.c
proxy_udp.c
proxy_ftp.c
proxy.c
tcpmux.c
tcp_redir.c
mongoose.c
)
set(src_xfrpc_plugins
plugins/telnetd.c
plugins/instaloader.c
plugins/httpd.c
plugins/youtubedl.c)
set(libs
ssl
crypto
event
z
m
json-c)
json-c
crypt
pthread)
set(test_libs
event
@@ -98,7 +109,7 @@ if (STATIC_BUILD STREQUAL "ON")
add_link_options(-static)
endif (STATIC_BUILD)
add_executable(xfrpc ${src_xfrpc})
add_executable(xfrpc ${src_xfrpc} ${src_xfrpc_plugins})
target_link_libraries(xfrpc ${libs} ${static_libs} ${asan_c_libs})
install(TARGETS xfrpc

View File

@@ -20,6 +20,7 @@ the following table is detail compatible feature:
| https | Yes | Yes |
| custom_domains | Yes | Yes |
| subdomain | Yes | Yes |
| socks5 | Yes | No |
| use_encryption | No | Yes |
| use_compression | No | Yes |
| udp | No | Yes |

View File

@@ -13,17 +13,8 @@ steps:
# install dependencies
sudo apt-get update
sudo apt-get install -y gcc cmake libevent-dev libjson-c-dev libssl-dev libz-dev
# install wget and tar
sudo apt-get install -y wget tar
# download the go-shadowsocks project
wget https://github.com/shadowsocks/go-shadowsocks2/releases/download/v0.1.5/shadowsocks2-linux.tgz
# unzip the project
tar -zxvf shadowsocks2-linux.tgz
# move the shadowsocks2-linux to /usr/bin/shadowsocks2-linux
sudo mv shadowsocks2-linux /usr/bin/shadowsocks2-linux
# create a build folder
mkdir build
# go to build folder
mkdir build
cd build
# run cmake
cmake ..
@@ -44,16 +35,12 @@ steps:
# the proxy will forward the traffic to the go-shadowsocks2
# the go-shadowsocks2 will forward the traffic to the internet
echo "[common]" > xfrpc.ini
echo "server_addr = $XFRPC_SERVER" >> xfrpc.ini
echo "server_port = $FRPS_PORT" >> xfrpc.ini
echo "token = $TOKEN" >> xfrpc.ini
echo "server_addr = $(XFRPC_SERVER)" >> xfrpc.ini
echo "server_port = $(FRPS_PORT)" >> xfrpc.ini
echo "token = $(TOKEN)" >> xfrpc.ini
echo "[chatgptd]" >> xfrpc.ini
echo "type = tcp" >> xfrpc.ini
echo "local_ip = 127.0.0.1" >> xfrpc.ini
echo "local_port = 1080" >> xfrpc.ini
echo "remote_port = $REMOTE_PORT" >> xfrpc.ini
# run go-shadowsocks2
/usr/bin/shadowsocks2-linux -s "ss://AEAD_CHACHA20_POLY1305:$SS_PASSWD@:1080" -verbose &
echo "type = socks5" >> xfrpc.ini
echo "remote_port = $(REMOTE_PORT)" >> xfrpc.ini
# run xfrpc
./xfrpc -c xfrpc.ini -f -d 7
displayName: 'build and run xfrpc'

View File

@@ -88,34 +88,6 @@ xfrp_proxy_event_cb(struct bufferevent *bev, short what, void *ctx)
debug(LOG_DEBUG, "send client data ...");
send_client_data_tail(client);
} else if (is_socks5_proxy(client->ps)) {
#if 0
debug(LOG_DEBUG, "send socks5 proxy response data ...");
client->state = SOCKS5_CONNECT;
uint8_t *socks5_response = NULL;
int offset = 0;
if (client->remote_addr.type == 0x01) {
offset = 10;
} else if (client->remote_addr.type == 0x03) {
uint8_t domain_len = strlen(client->remote_addr.addr);
offset = 7 + domain_len;
} else if (client->remote_addr.type == 0x04) {
offset = 22;
} else {
debug(LOG_ERR, "not support addr type: %d", client->remote_addr.type);
assert(0);
return;
}
socks5_response = (uint8_t *)malloc(offset);
memset(socks5_response, 0, offset);
socks5_response[0] = 0x05;
socks5_response[3] = client->remote_addr.type;
if (client->remote_addr.type == 0x03) {
uint8_t domain_len = strlen(client->remote_addr.addr);
socks5_response[4] = domain_len;
memcpy(socks5_response + 5, client->remote_addr.addr, domain_len);
}
tmux_stream_write(client->ctl_bev, socks5_response, offset, &client->stream);
#endif
// if rb is not empty, send data
// rb is client->stream.rx_ring
struct ring_buffer *rb = &client->stream.rx_ring;
@@ -151,6 +123,18 @@ is_socks5_proxy(const struct proxy_service *ps)
return 0;
}
int
is_udp_proxy (const struct proxy_service *ps)
{
if (! ps || ! ps->proxy_type)
return 0;
if (0 == strcmp(ps->proxy_type, "udp"))
return 1;
return 0;
}
// create frp tunnel for service
void
start_xfrp_tunnel(struct proxy_client *client)
@@ -179,14 +163,25 @@ start_xfrp_tunnel(struct proxy_client *client)
return;
}
// if client's proxy type is not socks5, then connect to local proxy server
if ( !is_socks5_proxy(client->ps) ) {
// if client's proxy type is udp
if ( is_udp_proxy(ps) ) {
debug(LOG_DEBUG, "start udp proxy tunnel for service [%s:%d]", ps->local_ip, ps->local_port);
client->local_proxy_bev = connect_udp_server(base);
if ( !client->local_proxy_bev ) {
debug(LOG_ERR, "frpc tunnel connect local proxy port [%d] failed!", ps->local_port);
del_proxy_client_by_stream_id(client->stream_id);
return;
}
} else if ( !is_socks5_proxy(ps) ) {
client->local_proxy_bev = connect_server(base, ps->local_ip, ps->local_port);
if ( !client->local_proxy_bev ) {
debug(LOG_ERR, "frpc tunnel connect local proxy port [%d] failed!", ps->local_port);
del_proxy_client_by_stream_id(client->stream_id);
return;
}
} else {
debug(LOG_DEBUG, "socks5 proxy client can't connect to remote server here ...");
return;
}
debug(LOG_DEBUG, "proxy server [%s:%d] <---> client [%s:%d]",
@@ -200,6 +195,9 @@ start_xfrp_tunnel(struct proxy_client *client)
if (PREDICT_FALSE(is_ftp_proxy(client->ps))) {
proxy_c2s_recv = ftp_proxy_c2s_cb;
proxy_s2c_recv = ftp_proxy_s2c_cb;
} else if ( is_udp_proxy(ps) ) {
proxy_c2s_recv = udp_proxy_c2s_cb;
proxy_s2c_recv = udp_proxy_s2c_cb;
} else {
proxy_c2s_recv = tcp_proxy_c2s_cb; // local service ---> xfrpc
proxy_s2c_recv = tcp_proxy_s2c_cb; // frps ---> xfrpc
@@ -214,10 +212,7 @@ start_xfrp_tunnel(struct proxy_client *client)
bufferevent_enable(client->ctl_bev, EV_READ|EV_WRITE);
}
if (is_socks5_proxy(client->ps)) {
debug(LOG_DEBUG, "socks5 proxy client can't connect to remote server here ...");
return;
}
bufferevent_setcb(client->local_proxy_bev,
proxy_c2s_recv,

View File

@@ -83,7 +83,7 @@ struct proxy_service {
int use_encryption;
int use_compression;
// tcp only
// tcp or udp
char *local_ip;
int remote_port;
int remote_data_port;
@@ -97,6 +97,17 @@ struct proxy_service {
char *http_user;
char *http_pwd;
// load balance
char *group;
char *group_key;
// plugin
char *plugin;
char *plugin_user;
char *plugin_pwd;
char *s_root_dir;
// private arguments
UT_hash_handle hh;
};
@@ -118,6 +129,8 @@ int is_ftp_proxy(const struct proxy_service *ps);
int is_socks5_proxy(const struct proxy_service *ps);
int is_udp_proxy(const struct proxy_service *ps);
struct proxy_client *new_proxy_client();
void clear_all_proxy_client();

118
colab/xfrpc.ipynb Normal file
View File

@@ -0,0 +1,118 @@
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"authorship_tag": "ABX9TyNuWIELWWDtoNBAYOHZfXRi",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/github/liudf0716/xfrpc/blob/master/colab_xfrpc.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "ErrkZjxXvVNe"
},
"outputs": [],
"source": [
"!git clone https://github.com/liudf0716/xfrpc.git\n",
"! cd xfrpc/ && mkdir build && cd build && cmake .. && make && cp xfrpc /usr/bin/"
]
},
{
"cell_type": "code",
"source": [
"! pip install configparser\n",
"\n",
"import configparser\n",
"import sys\n",
"\n",
"server_addr = \"colab.xfrpc.xyz\" #@param {type:\"string\"}\n",
"token = \"l00JJ6ea302lT\" #@param {type:\"string\"}\n",
"server_port = 8443 #@param {type:\"integer\"}\n",
"remote_port = 6003 #@param {type:\"integer\"}\n",
"plugin_user = \"xfrpc\" #@param {type:\"string\"}\n",
"plugin_pwd = \"123456\" #@param {type:\"string\"}\n",
"\n",
"def get_input(prompt):\n",
" \"\"\"Gets input from the user.\"\"\"\n",
" return input(prompt)\n",
"\n",
"def generate_ini(server_addr, server_port, token, remote_port, user, pwd):\n",
" \"\"\"Generates an INI file.\"\"\"\n",
" config = configparser.ConfigParser()\n",
"\n",
" config[\"common\"] = {\n",
" \"server_addr\": server_addr,\n",
" \"server_port\": server_port,\n",
" \"token\": token,\n",
" }\n",
"\n",
" config[\"telnetd\"] = {\n",
" \"type\": \"tcp\",\n",
" \"remote_port\": remote_port,\n",
" \"plugin\": \"telnetd\",\n",
" \"plugin_user\": user,\n",
" \"plugin_pwd\": pwd,\n",
" }\n",
"\n",
" with open(\"my.ini\", \"w\") as f:\n",
" config.write(f)\n",
"\n",
" print(\"Ini file generated successfully!\")\n",
"\n",
"if __name__ == \"__main__\":\n",
" generate_ini(server_addr, server_port, token, remote_port, plugin_user, plugin_pwd)\n",
"\n"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "V7n8azihcBUl",
"outputId": "b7d475f9-49af-4546-8381-b96a577258cb"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Requirement already satisfied: configparser in /usr/local/lib/python3.10/dist-packages (6.0.0)\n",
"Ini file generated successfully!\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"! xfrpc -c my.ini -f -d 6"
],
"metadata": {
"id": "KFLJaznZvrLG"
},
"execution_count": null,
"outputs": []
}
]
}

161
config.c
View File

@@ -28,9 +28,11 @@
#include <string.h>
#include <assert.h>
#include <time.h>
#include <syslog.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <shadow.h>
#include <crypt.h>
#include "ini.h"
#include "uthash.h"
@@ -41,6 +43,17 @@
#include "utils.h"
#include "version.h"
// define a list of type in array
static const char *valid_types[] = {
"tcp",
"udp",
"socks5",
"http",
"https",
NULL
};
static struct common_conf *c_conf;
static struct proxy_service *all_ps;
@@ -77,12 +90,11 @@ get_valid_type(const char *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;
// iterate the valid_types array
for (int i = 0; valid_types[i]; i++) {
if (MATCH_VALUE(valid_types[i])) {
return valid_types[i];
}
}
return NULL;
@@ -162,9 +174,9 @@ new_proxy_service(const char *name)
ps->proxy_type = NULL;
ps->use_encryption = 0;
ps->local_port = -1;
ps->remote_port = -1;
ps->remote_data_port = -1;
ps->local_port = 0;
ps->remote_port = 0;
ps->remote_data_port = 0;
ps->use_compression = 0;
ps->use_encryption = 0;
@@ -175,6 +187,15 @@ new_proxy_service(const char *name)
ps->http_user = NULL;
ps->http_pwd = NULL;
ps->group = NULL;
ps->group_key = NULL;
ps->plugin = NULL;
ps->plugin_user = NULL;
ps->plugin_pwd = NULL;
ps->s_root_dir = NULL;
return ps;
}
@@ -219,7 +240,7 @@ validate_proxy(struct proxy_service *ps)
debug(LOG_ERR, "Proxy [%s] error: remote_port not found", ps->proxy_name);
return 0;
}
} else if (strcmp(ps->proxy_type, "tcp") == 0) {
} else if (strcmp(ps->proxy_type, "tcp") == 0 || strcmp(ps->proxy_type, "udp") == 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;
@@ -246,6 +267,95 @@ validate_proxy(struct proxy_service *ps)
return 1;
}
static int
add_user_and_set_password(const char *username, const char *password)
{
// Check if the user already exists
struct passwd *pw = getpwnam(username);
if (pw != NULL) {
debug (LOG_ERR, "User %s already exists\n", username);
return -1;
}
// Create the new user with useradd command
char cmd[256];
snprintf(cmd, sizeof(cmd), "sudo useradd -m -s /bin/bash %s", username);
int ret = system(cmd);
if (ret != 0) {
debug (LOG_ERR, "Failed to create user %s\n", username);
return -1;
}
// Set the user's password with passwd command
snprintf(cmd, sizeof(cmd), "echo '%s:%s' | sudo chpasswd", username, password);
ret = system(cmd);
if (ret != 0) {
debug (LOG_ERR, "Failed to set password for user %s\n", username);
return -1;
}
// Add the user to the sudo group with usermod command
snprintf(cmd, sizeof(cmd), "sudo usermod -aG sudo %s", username);
ret = system(cmd);
if (ret != 0) {
debug (LOG_ERR, "Failed to add user %s to sudo group\n", username);
return -1;
}
debug (LOG_DEBUG, "User %s added successfully\n", username);
return 0;
}
static void
process_plugin_conf(struct proxy_service *ps)
{
if (!ps || !ps->plugin)
return;
if (strcmp(ps->plugin, "telnetd") == 0) {
if (ps->local_port == 0)
ps->local_port = XFRPC_PLUGIN_TELNETD_PORT;
if (ps->local_ip == NULL)
ps->local_ip = strdup("127.0.0.1");
if (ps->plugin_user !=NULL && ps->plugin_pwd != NULL) {
add_user_and_set_password (ps->plugin_user, ps->plugin_pwd);
}
} else if (strcmp(ps->plugin, "instaloader") == 0) {
if (ps->local_port == 0)
ps->local_port = XFRPC_PLUGIN_INSTALOADER_PORT;
if (ps->remote_port == 0)
ps->remote_port = XFRPC_PLUGIN_INSTALOADER_REMOTE_PORT;
if (ps->local_ip == NULL)
ps->local_ip = strdup("127.0.0.1");
} else if (strcmp(ps->plugin, "instaloader_client") == 0) {
if (ps->local_port == 0)
ps->local_port = XFRPC_PLUGIN_INSTALOADER_PORT;
if (ps->remote_port == 0)
ps->remote_port == XFRPC_PLUGIN_INSTALOADER_REMOTE_PORT;
if (ps->local_ip == NULL)
ps->local_ip = strdup("0.0.0.0");
} else if (strcmp(ps->plugin, "youtubedl") == 0) {
if (ps->local_port == 0)
ps->local_port = XFRPC_PLUGIN_YOUTUBEDL_PORT;
if (ps->remote_port == 0)
ps->remote_port = XFRPC_PLUGIN_YOUTUBEDL_REMOTE_PORT;
if (ps->local_ip == NULL)
ps->local_ip = strdup("127.0.0.1");
} else if (strcmp(ps->plugin, "httpd") == 0) {
if (ps->local_port == 0)
ps->local_port = XFRPC_PLUGIN_HTTPD_PORT;
if (ps->local_ip == NULL)
ps->local_ip = strdup("127.0.0.1");
if (ps->remote_port == 0)
ps->remote_port = XFRPC_PLUGIN_HTTPD_REMOTE_PORT;
if (ps->s_root_dir == NULL)
ps->s_root_dir = strdup("/var/www/html");
} else {
debug(LOG_INFO, "plugin %s is not supportted", ps->plugin);
}
}
static int
proxy_service_handler(void *user, const char *sect, const char *nm, const char *value)
{
@@ -310,11 +420,32 @@ proxy_service_handler(void *user, const char *sect, const char *nm, const char *
ps->use_encryption = TO_BOOL(value);
} else if (MATCH_NAME("use_compression")) {
ps->use_compression = TO_BOOL(value);
} else if (MATCH_NAME("group")) {
ps->group = strdup(value);
} else if (MATCH_NAME("group_key")) {
ps->group_key = strdup(value);
} else if (MATCH_NAME("plugin")) {
ps->plugin = strdup(value);
} else if (MATCH_NAME("plugin_user")) {
ps->plugin_user = strdup(value);
} else if (MATCH_NAME("plugin_pwd")) {
ps->plugin_pwd = strdup(value);
} else if (MATCH_NAME("root_dir")) {
ps->s_root_dir = strdup(value);
} else {
debug(LOG_ERR, "unknown option %s in section %s", nm, section);
SAFE_FREE(section);
return 0;
}
// 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;
if (ps->proxy_type && strcmp(ps->proxy_type, "socks5") == 0) {
if (ps->remote_port == 0)
ps->remote_port = DEFAULT_SOCKS5_PORT;
if (ps->group == NULL)
ps->group = strdup("chatgptd");
} else if (ps->proxy_type && strcmp(ps->proxy_type, "tcp") == 0) {
process_plugin_conf(ps);
}
SAFE_FREE(section);

View File

@@ -29,6 +29,16 @@
#include "client.h"
#include "common.h"
#define DEFAULT_MSTSC_PORT 3389
#define DEFAULT_SOCKS5_PORT 1980
#define XFRPC_PLUGIN_TELNETD_PORT 23
#define XFRPC_PLUGIN_INSTALOADER_PORT 10000
#define XFRPC_PLUGIN_INSTALOADER_REMOTE_PORT 10001
#define XFRPC_PLUGIN_YOUTUBEDL_PORT 20002
#define XFRPC_PLUGIN_YOUTUBEDL_REMOTE_PORT 20003
#define XFRPC_PLUGIN_HTTPD_PORT 8000
#define XFRPC_PLUGIN_HTTPD_REMOTE_PORT 8001
#define FTP_RMT_CTL_PROXY_SUFFIX "_ftp_remote_ctl_proxy"
//client common config

View File

@@ -49,6 +49,7 @@
#include "common.h"
#include "login.h"
#include "tcpmux.h"
#include "proxy.h"
static struct control *main_ctl;
static int client_connected = 0;
@@ -162,6 +163,10 @@ start_proxy_services()
debug(LOG_ERR, "proxy service is invalid!");
return;
}
if (strcmp(ps->proxy_type, "mstsc") == 0) {
debug(LOG_ERR, "no need to send mstsc service!");
continue;
}
send_new_proxy(ps);
}
}
@@ -220,6 +225,33 @@ connect_server(struct event_base *base, const char *name, const int port)
return bev;
}
struct bufferevent *
connect_udp_server(struct event_base *base)
{
evutil_socket_t fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (fd < 0) {
debug(LOG_ERR, "create udp socket failed!");
return NULL;
}
if (evutil_make_socket_nonblocking(fd) < 0) {
debug(LOG_ERR, "make udp socket nonblocking failed!");
evutil_closesocket(fd);
return NULL;
}
struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
assert(bev);
if (!bev) {
evutil_closesocket(fd);
debug(LOG_ERR, "create udp bufferevent failed!");
return NULL;
}
return bev;
}
static void
set_ticker_ping_timer(struct event *timeout)
{
@@ -405,6 +437,22 @@ handle_control_work(const uint8_t *buf, int len, void *ctx)
break;
}
case TypeUDPPacket:
{
struct udp_packet *udp = udp_packet_unmarshal((const char *)msg->data);
if (!udp) {
debug(LOG_ERR, "TypeUDPPacket unmarshal failed!");
break;
}
debug(LOG_DEBUG, "recv udp packet from server, content is %s",
udp->content);
assert(ctx);
struct proxy_client *client = ctx;
assert(client->ps);
handle_udp_packet(udp, client);
SAFE_FREE(udp);
break;
}
case TypePong:
pong_time = time(NULL);
break;

View File

@@ -80,4 +80,6 @@ void send_new_proxy(struct proxy_service *ps);
struct bufferevent *connect_server(struct event_base *base, const char *name, const int port);
struct bufferevent *connect_udp_server(struct event_base *base);
#endif //_CONTROL_H_

View File

@@ -72,7 +72,8 @@ void init_login()
struct utsname uname_buf;
if (uname(&uname_buf)) {
return;
debug(LOG_ERR, "error: get system info failed!");
exit(0);
}
c_login->version = strdup(PROTOCOL_VERESION);

View File

@@ -31,6 +31,7 @@
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <time.h>
#include "uthash.h"
@@ -41,7 +42,7 @@ struct login {
char *arch;
char *user;
char *privilege_key;
long int timestamp;
time_t timestamp;
char *run_id;
char *metas;
int pool_count;

10039
mongoose.c Normal file

File diff suppressed because it is too large Load Diff

1915
mongoose.h Normal file

File diff suppressed because it is too large Load Diff

360
msg.c
View File

@@ -20,8 +20,8 @@
\********************************************************************/
/** @file msg.c
@brief xfrpc client msg related
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
@brief xfrpc client msg related
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
#include <string.h>
@@ -41,44 +41,45 @@
#include "client.h"
#include "utils.h"
#define JSON_MARSHAL_TYPE(jobj,key,jtype,item) \
json_object_object_add(jobj, key, json_object_new_##jtype((item)));
#define JSON_MARSHAL_TYPE(jobj, key, jtype, item) \
json_object_object_add(jobj, key, json_object_new_##jtype((item)));
#define SAFE_JSON_STRING(str_target) \
str_target?str_target:"\0"
str_target ? str_target : "\0"
const char msg_types[] = {TypeLogin,
TypeLoginResp,
TypeNewProxy,
TypeNewProxyResp,
TypeNewWorkConn,
TypeReqWorkConn,
TypeStartWorkConn,
TypePing,
TypePong,
TypeUDPPacket};
const char msg_types[] = {TypeLogin,
TypeLoginResp,
TypeNewProxy,
TypeNewProxyResp,
TypeNewWorkConn,
TypeReqWorkConn,
TypeStartWorkConn,
TypePing,
TypePong,
TypeUDPPacket};
char *
calc_md5(const char *data, int datalen)
{
unsigned char digest[16] = {0};
char *out = (char*)malloc(33);
char *out = (char *)malloc(33);
assert(out);
MD5_CTX md5;
MD5_Init(&md5);
MD5_Update(&md5, data, datalen);
MD5_Final(digest, &md5);
for (int n = 0; n < 16; ++n) {
snprintf(&(out[n*2]), 3, "%02x", (unsigned int)digest[n]);
}
return out;
for (int n = 0; n < 16; ++n)
{
snprintf(&(out[n * 2]), 3, "%02x", (unsigned int)digest[n]);
}
return out;
}
static void
static void
fill_custom_domains(struct json_object *j_ctl_req, const char *custom_domains)
{
struct json_object *jarray_cdomains = json_object_new_array();
@@ -86,7 +87,8 @@ fill_custom_domains(struct json_object *j_ctl_req, const char *custom_domains)
char *tmp = strdup(custom_domains);
assert(tmp);
char *tok = tmp, *end = tmp;
while (tok != NULL) {
while (tok != NULL)
{
strsep(&end, ",");
int dname_len = strlen(tok) + 1;
@@ -99,15 +101,16 @@ fill_custom_domains(struct json_object *j_ctl_req, const char *custom_domains)
tok = end;
}
SAFE_FREE(tmp);
json_object_object_add(j_ctl_req, "custom_domains", jarray_cdomains);
}
struct work_conn *
new_work_conn() {
new_work_conn()
{
struct work_conn *work_c = calloc(1, sizeof(struct work_conn));
assert(work_c);
if (work_c)
if (work_c)
work_c->run_id = NULL;
return work_c;
@@ -122,28 +125,30 @@ get_auth_key(const char *token, long int *timestamp)
snprintf(seed, 128, "%s%ld", token, *timestamp);
else
snprintf(seed, 128, "%ld", *timestamp);
return calc_md5(seed, strlen(seed));
}
size_t
size_t
login_request_marshal(char **msg)
{
size_t nret = 0;
struct json_object *j_login_req = json_object_new_object();
if (j_login_req == NULL)
return 0;
struct login *lg = get_common_login_config();
if (!lg)
if (!lg) {
json_object_put(j_login_req);
return 0;
}
SAFE_FREE(lg->privilege_key);
struct common_conf *cf = get_common_config();
char *auth_key = get_auth_key(cf->auth_token, &lg->timestamp);
lg->privilege_key = strdup(auth_key);
assert(lg->privilege_key);
JSON_MARSHAL_TYPE(j_login_req, "version", string, lg->version);
JSON_MARSHAL_TYPE(j_login_req, "hostname", string, SAFE_JSON_STRING(lg->hostname));
JSON_MARSHAL_TYPE(j_login_req, "os", string, lg->os);
@@ -151,11 +156,15 @@ login_request_marshal(char **msg)
JSON_MARSHAL_TYPE(j_login_req, "user", string, SAFE_JSON_STRING(lg->user));
JSON_MARSHAL_TYPE(j_login_req, "privilege_key", string, SAFE_JSON_STRING(lg->privilege_key));
JSON_MARSHAL_TYPE(j_login_req, "timestamp", int64, lg->timestamp);
if (sizeof(time_t) == 4) {
JSON_MARSHAL_TYPE(j_login_req, "timestamp", int, lg->timestamp);
} else {
JSON_MARSHAL_TYPE(j_login_req, "timestamp", int64, lg->timestamp);
}
JSON_MARSHAL_TYPE(j_login_req, "run_id", string, SAFE_JSON_STRING(lg->run_id));
JSON_MARSHAL_TYPE(j_login_req, "pool_count", int, lg->pool_count);
json_object_object_add(j_login_req, "metas", NULL);
const char *tmp = NULL;
tmp = json_object_to_json_string(j_login_req);
if (tmp && strlen(tmp) > 0) {
@@ -168,21 +177,20 @@ login_request_marshal(char **msg)
return nret;
}
int
new_proxy_service_marshal(const struct proxy_service *np_req, char **msg)
int new_proxy_service_marshal(const struct proxy_service *np_req, char **msg)
{
const char *tmp = NULL;
int nret = 0;
int nret = 0;
char *path = NULL;
char *delimiter = ",";
char *save = NULL;
struct json_object *j_np_req = json_object_new_object();
if ( ! j_np_req)
if (!j_np_req)
return 0;
JSON_MARSHAL_TYPE(j_np_req, "proxy_name", string, np_req->proxy_name);
// if proxy_type is socks5, set the proxy_type to tcp
if (strcmp(np_req->proxy_type, "socks5") == 0) {
if (strcmp(np_req->proxy_type, "socks5") == 0 || strcmp(np_req->proxy_type, "mstsc") == 0) {
JSON_MARSHAL_TYPE(j_np_req, "proxy_type", string, "tcp");
} else {
JSON_MARSHAL_TYPE(j_np_req, "proxy_type", string, np_req->proxy_type);
@@ -190,6 +198,20 @@ new_proxy_service_marshal(const struct proxy_service *np_req, char **msg)
JSON_MARSHAL_TYPE(j_np_req, "use_encryption", boolean, np_req->use_encryption);
JSON_MARSHAL_TYPE(j_np_req, "use_compression", boolean, np_req->use_compression);
// if proxy_type is tcp, http, https and socks5, set group and group_key to j_np_req
if (strcmp(np_req->proxy_type, "tcp") == 0 ||
strcmp(np_req->proxy_type, "http") == 0 ||
strcmp(np_req->proxy_type, "https") == 0 ||
strcmp(np_req->proxy_type, "socks5") == 0) {
if (np_req->group) {
JSON_MARSHAL_TYPE(j_np_req, "group", string, np_req->group);
}
if (np_req->group_key) {
JSON_MARSHAL_TYPE(j_np_req, "group_key", string, np_req->group_key);
}
}
if (is_ftp_proxy(np_req)) {
JSON_MARSHAL_TYPE(j_np_req, "remote_data_port", int, np_req->remote_data_port);
}
@@ -219,11 +241,11 @@ new_proxy_service_marshal(const struct proxy_service *np_req, char **msg)
} else {
json_object_object_add(j_np_req, "locations", NULL);
}
JSON_MARSHAL_TYPE(j_np_req, "host_header_rewrite", string, SAFE_JSON_STRING(np_req->host_header_rewrite));
JSON_MARSHAL_TYPE(j_np_req, "http_user", string, SAFE_JSON_STRING(np_req->http_user));
JSON_MARSHAL_TYPE(j_np_req, "http_pwd", string, SAFE_JSON_STRING(np_req->http_pwd));
tmp = json_object_to_json_string(j_np_req);
if (tmp && strlen(tmp) > 0) {
nret = strlen(tmp);
@@ -235,13 +257,12 @@ new_proxy_service_marshal(const struct proxy_service *np_req, char **msg)
return nret;
}
int
new_work_conn_marshal(const struct work_conn *work_c, char **msg)
int new_work_conn_marshal(const struct work_conn *work_c, char **msg)
{
const char *tmp = NULL;
int nret = 0;
struct json_object *j_new_work_conn = json_object_new_object();
if (! j_new_work_conn)
if (!j_new_work_conn)
return 0;
JSON_MARSHAL_TYPE(j_new_work_conn, "run_id", string, SAFE_JSON_STRING(work_c->run_id));
@@ -264,7 +285,7 @@ new_proxy_resp_unmarshal(const char *jres)
struct json_object *j_np_res = json_tokener_parse(jres);
if (j_np_res == NULL)
return NULL;
struct new_proxy_response *npr = calloc(1, sizeof(struct new_proxy_response));
assert(npr);
@@ -273,8 +294,13 @@ new_proxy_resp_unmarshal(const char *jres)
npr->run_id = strdup(json_object_get_string(npr_run_id));
struct json_object *npr_proxy_remote_addr = NULL;
if (! json_object_object_get_ex(j_np_res, "remote_addr", &npr_proxy_remote_addr))
if (!json_object_object_get_ex(j_np_res, "remote_addr", &npr_proxy_remote_addr)) {
free(npr->run_id);
free(npr);
npr = NULL;
goto END_ERROR;
}
const char *remote_addr = json_object_get_string(npr_proxy_remote_addr);
char *port = strrchr(remote_addr, ':');
if (port) {
@@ -283,14 +309,24 @@ new_proxy_resp_unmarshal(const char *jres)
}
struct json_object *npr_proxy_name = NULL;
if (! json_object_object_get_ex(j_np_res, "proxy_name", &npr_proxy_name))
if (!json_object_object_get_ex(j_np_res, "proxy_name", &npr_proxy_name)) {
free(npr->run_id);
free(npr);
npr = NULL;
goto END_ERROR;
}
npr->proxy_name = strdup(json_object_get_string(npr_proxy_name));
assert(npr->proxy_name);
struct json_object *npr_error = NULL;
if(! json_object_object_get_ex(j_np_res, "error", &npr_error))
if (!json_object_object_get_ex(j_np_res, "error", &npr_error)) {
free(npr->run_id);
free(npr->proxy_name);
free(npr);
npr = NULL;
goto END_ERROR;
}
npr->error = strdup(json_object_get_string(npr_error));
assert(npr->error);
@@ -306,25 +342,40 @@ login_resp_unmarshal(const char *jres)
struct json_object *j_lg_res = json_tokener_parse(jres);
if (j_lg_res == NULL)
return NULL;
struct login_resp *lr = calloc(1, sizeof(struct login_resp));
assert(lr);
struct json_object *l_version = NULL;
if (! json_object_object_get_ex(j_lg_res, "version", &l_version))
if (!json_object_object_get_ex(j_lg_res, "version", &l_version)) {
free(lr);
lr = NULL;
goto END_ERROR;
}
lr->version = strdup(json_object_get_string(l_version));
assert(lr->version);
struct json_object *l_run_id = NULL;
if (! json_object_object_get_ex(j_lg_res, "run_id", &l_run_id))
if (!json_object_object_get_ex(j_lg_res, "run_id", &l_run_id)) {
free(lr->version);
free(lr);
lr = NULL;
goto END_ERROR;
}
lr->run_id = strdup(json_object_get_string(l_run_id));
assert(lr->run_id);
struct json_object *l_error = NULL;
if(! json_object_object_get_ex(j_lg_res, "error", &l_error))
if (!json_object_object_get_ex(j_lg_res, "error", &l_error)) {
free(lr->version);
free(lr->run_id);
free(lr);
lr = NULL;
goto END_ERROR;
}
lr->error = strdup(json_object_get_string(l_error));
assert(lr->error);
@@ -344,8 +395,11 @@ start_work_conn_resp_unmarshal(const char *resp_msg)
assert(sr);
struct json_object *pn = NULL;
if(! json_object_object_get_ex(j_start_w_res, "proxy_name", &pn))
if (!json_object_object_get_ex(j_start_w_res, "proxy_name", &pn)) {
free(sr);
sr = NULL;
goto START_W_C_R_END;
}
sr->proxy_name = strdup(json_object_get_string(pn));
assert(sr->proxy_name);
@@ -363,22 +417,32 @@ control_response_unmarshal(const char *jres)
return NULL;
struct control_response *ctl_res = calloc(sizeof(struct control_response), 1);
assert(ctl_res);
struct json_object *jtype = NULL;
if(! json_object_object_get_ex(j_ctl_res, "type", &jtype))
if (!json_object_object_get_ex(j_ctl_res, "type", &jtype)) {
free(ctl_res);
ctl_res = NULL;
goto END_ERROR;
ctl_res->type = json_object_get_int(jtype);
struct json_object *jcode = NULL;
if(! json_object_object_get_ex(j_ctl_res, "code", &jcode))
goto END_ERROR;
ctl_res->code = json_object_get_int(jcode);
struct json_object *jmsg = NULL;
if(json_object_object_get_ex(j_ctl_res, "msg", &jmsg)) {
ctl_res->msg = strdup(json_object_get_string(jmsg));
assert(ctl_res->msg);
}
ctl_res->type = json_object_get_int(jtype);
struct json_object *jcode = NULL;
if (!json_object_object_get_ex(j_ctl_res, "code", &jcode)) {
free(ctl_res);
ctl_res = NULL;
goto END_ERROR;
}
ctl_res->code = json_object_get_int(jcode);
struct json_object *jmsg = NULL;
if (!json_object_object_get_ex(j_ctl_res, "msg", &jmsg)) {
free(ctl_res);
ctl_res = NULL;
goto END_ERROR;
}
ctl_res->msg = strdup(json_object_get_string(jmsg));
assert(ctl_res->msg);
END_ERROR:
json_object_put(j_ctl_res);
@@ -390,26 +454,168 @@ control_response_free(struct control_response *res)
{
if (!res)
return;
SAFE_FREE(res->msg);
SAFE_FREE(res);
}
// marshal udp packet msg
int
msg_type_valid_check(char msg_type)
new_udp_packet_marshal(const struct udp_packet *udp, char **msg)
{
int i = 0;
for(i = 0; i<(sizeof(msg_types) / sizeof(*msg_types)); i++) {
if (msg_types[i] == msg_type)
return 1;
// parse struct udp_packet to json
struct json_object *j_udp = json_object_new_object();
assert(j_udp);
struct json_object *content = json_object_new_string(udp->content);
assert(content);
json_object_object_add(j_udp, "c", content);
if (udp->laddr) {
// laddr is a struct, parse it to json object and add to j_udp
struct json_object *j_laddr = json_object_new_object();
assert(j_laddr);
struct json_object *j_laddr_addr = json_object_new_string(udp->laddr->addr);
assert(j_laddr_addr);
json_object_object_add(j_laddr, "IP", j_laddr_addr);
struct json_object *j_laddr_port = json_object_new_int(udp->laddr->port);
assert(j_laddr_port);
json_object_object_add(j_laddr, "Port", j_laddr_port);
json_object_object_add(j_udp, "l", j_laddr);
json_object *j_laddr_zone = json_object_new_string("");
assert(j_laddr_zone);
json_object_object_add(j_laddr, "Zone", j_laddr_zone);
} else {
// laddr is NULL, add null to j_udp
struct json_object *j_laddr = json_object_new_object();
assert(j_laddr);
json_object_object_add(j_udp, "l", j_laddr);
}
if (udp->raddr) {
// raddr is a struct, parse it to json object and add to j_udp
struct json_object *j_raddr = json_object_new_object();
assert(j_raddr);
struct json_object *j_raddr_addr = json_object_new_string(udp->raddr->addr);
assert(j_raddr_addr);
json_object_object_add(j_raddr, "IP", j_raddr_addr);
struct json_object *j_raddr_port = json_object_new_int(udp->raddr->port);
assert(j_raddr_port);
json_object_object_add(j_raddr, "Port", j_raddr_port);
json_object_object_add(j_udp, "r", j_raddr);
json_object *j_raddr_zone = json_object_new_string("");
assert(j_raddr_zone);
json_object_object_add(j_raddr, "Zone", j_raddr_zone);
} else {
// raddr is NULL, add null to j_udp
struct json_object *j_raddr = json_object_new_object();
assert(j_raddr);
json_object_object_add(j_udp, "r", j_raddr);
}
// convert json to string msg
*msg = strdup(json_object_to_json_string(j_udp));
assert(*msg);
json_object_put(j_udp);
return 0;
}
char *
get_msg_type(uint8_t type)
void
udp_packet_free(struct udp_packet *udp)
{
return NULL;
if (!udp)
return;
SAFE_FREE(udp->content);
SAFE_FREE(udp->laddr->addr);
SAFE_FREE(udp->laddr->zone);
SAFE_FREE(udp->laddr);
SAFE_FREE(udp->raddr->addr);
SAFE_FREE(udp->raddr->zone);
SAFE_FREE(udp->raddr);
SAFE_FREE(udp);
}
// unmarshal udp packet msg
struct udp_packet *
udp_packet_unmarshal(const char *msg)
{
struct json_object *j_udp = json_tokener_parse(msg);
if (j_udp == NULL)
return NULL;
struct udp_packet *udp = calloc(sizeof(struct udp_packet), 1);
assert(udp);
struct json_object *j_content = NULL;
if (!json_object_object_get_ex(j_udp, "c", &j_content)) {
goto END_ERROR;
}
udp->content = strdup(json_object_get_string(j_content));
assert(udp->content);
struct json_object *j_laddr = NULL;
if (!json_object_object_get_ex(j_udp, "l", &j_laddr)) {
goto END_ERROR;
}
struct json_object *j_laddr_ip = NULL;
if (!json_object_object_get_ex(j_laddr, "IP", &j_laddr_ip)) {
goto END_ERROR;
}
struct json_object *j_laddr_port = NULL;
if (!json_object_object_get_ex(j_laddr, "Port", &j_laddr_port)) {
goto END_ERROR;
}
struct json_object *j_laddr_zone = NULL;
if (!json_object_object_get_ex(j_laddr, "Zone", &j_laddr_zone)) {
goto END_ERROR;
}
udp->laddr = calloc(sizeof(struct udp_addr), 1);
assert(udp->laddr);
udp->laddr->addr = strdup(json_object_get_string(j_laddr_ip));
assert(udp->laddr->addr);
udp->laddr->port = json_object_get_int(j_laddr_port);
udp->laddr->zone = strdup(json_object_get_string(j_laddr_zone));
assert(udp->laddr->zone);
struct json_object *j_raddr = NULL;
if (!json_object_object_get_ex(j_udp, "r", &j_raddr)) {
goto END_ERROR;
}
struct json_object *j_raddr_ip = NULL;
if (!json_object_object_get_ex(j_raddr, "IP", &j_raddr_ip)) {
goto END_ERROR;
}
struct json_object *j_raddr_port = NULL;
if (!json_object_object_get_ex(j_raddr, "Port", &j_raddr_port)) {
goto END_ERROR;
}
struct json_object *j_raddr_zone = NULL;
if (!json_object_object_get_ex(j_raddr, "Zone", &j_raddr_zone)) {
goto END_ERROR;
}
udp->raddr = calloc(sizeof(struct udp_addr), 1);
assert(udp->raddr);
udp->raddr->addr = strdup(json_object_get_string(j_raddr_ip));
assert(udp->raddr->addr);
udp->raddr->port = json_object_get_int(j_raddr_port);
udp->raddr->zone = strdup(json_object_get_string(j_raddr_zone));
assert(udp->raddr->zone);
json_object_put(j_udp);
return udp;
END_ERROR:
json_object_put(j_udp);
udp_packet_free(udp);
return NULL;
}

21
msg.h
View File

@@ -86,6 +86,18 @@ struct work_conn {
char *run_id;
};
struct udp_addr {
char *addr;
int port;
char *zone;
};
struct udp_packet {
char *content; // base64
struct udp_addr *laddr;
struct udp_addr *raddr;
};
struct __attribute__((__packed__)) msg_hdr {
char type;
uint64_t length;
@@ -96,8 +108,8 @@ struct start_work_conn_resp {
char *proxy_name;
};
int new_udp_packet_marshal(const struct udp_packet *udp, char **msg);
int new_proxy_service_marshal(const struct proxy_service *np_req, char **msg);
int msg_type_valid_check(char msg_type);
char *calc_md5(const char *data, int datalen);
char *get_auth_key(const char *token, long int *timestamp);
size_t login_request_marshal(char **msg);
@@ -112,8 +124,11 @@ struct control_response *control_response_unmarshal(const char *jres);
struct work_conn *new_work_conn();
int new_work_conn_marshal(const struct work_conn *work_c, char **msg);
// parse json string to udp packet
struct udp_packet *udp_packet_unmarshal(const char *jres);
void udp_packet_free(struct udp_packet *udp);
void control_response_free(struct control_response *res);
char *get_msg_type(uint8_t type);
#endif //_MSG_H_

12
pkg/debian/rules Normal file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/make -f
export DEB_LDFLAGS_MAINT_APPEND=-Wl,-O1
export DEB_BUILD_MAINT_OPTIONS=hardening=+all
# get the various DEB_BUILD/DEB_HOST variables
include /usr/share/dpkg/architecture.mk
override_dh_install:
dh_install --autodest
%:
dh $@

81
plugins/httpd.c Normal file
View File

@@ -0,0 +1,81 @@
#include <pthread.h>
#include "../debug.h"
#include "../mongoose.h"
#include "httpd.h"
static const char *s_root_dir = ".";
static const char *s_listening_address = "http://0.0.0.0:8000";
static void
httpd_handler(struct mg_connection *c, int ev, void *ev_data, void *fn_data)
{
if (ev == MG_EV_HTTP_MSG)
{
struct mg_http_message *hm = ev_data, tmp = {0};
struct mg_str unknown = mg_str_n("?", 1), *cl;
struct mg_http_serve_opts opts = {0};
opts.root_dir = s_root_dir;
mg_http_serve_dir(c, hm, &opts);
mg_http_parse((char *)c->send.buf, c->send.len, &tmp);
cl = mg_http_get_header(&tmp, "Content-Length");
if (cl == NULL)
cl = &unknown;
debug(LOG_INFO, "HTTP: %.*s %.*s %.*s %.*s\n",
(int)hm->method.len, hm->method.ptr,
(int)hm->uri.len, hm->uri.ptr,
(int)tmp.uri.len, tmp.uri.ptr,
(int)cl->len, cl->ptr);
}
(void)fn_data;
}
static void *
httpd_thread(void *arg)
{
char path[MG_PATH_MAX] = ".";
struct mg_mgr mgr;
struct mg_connection *c;
struct proxy_service *ps = (struct proxy_service *)arg;
mg_mgr_init(&mgr);
if ((c = mg_http_listen(&mgr, s_listening_address, httpd_handler, &mgr)) == NULL)
{
debug(LOG_ERR, "Cannot listen on %s. Use http://ADDR:PORT or :PORT",
s_listening_address);
exit(EXIT_FAILURE);
}
// Root directory must not contain double dots. Make it absolute
// Do the conversion only if the root dir spec does not contain overrides
if (strchr(ps->s_root_dir, ',') == NULL)
{
realpath(ps->s_root_dir, path);
s_root_dir = path;
}
debug(LOG_INFO, "Listening on : %s", s_listening_address);
debug(LOG_INFO, "Web root : [%s]", s_root_dir);
while (1)
mg_mgr_poll(&mgr, 1000);
mg_mgr_free(&mgr);
return NULL;
}
void start_httpd_service(struct proxy_service *ps)
{
// start a httpd service in a new thread
pthread_t thread;
if (pthread_create(&thread, NULL, httpd_thread, ps) != 0)
{
debug(LOG_ERR, "Failed to create thread\n");
exit(-1);
}
//detach thread
pthread_detach(thread);
return;
}

8
plugins/httpd.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef _HTTPD_H
#define _HTTPD_H
#include "../client.h"
void start_httpd_service(struct proxy_service *ps);
#endif

247
plugins/instaloader.c Normal file
View File

@@ -0,0 +1,247 @@
#include <json-c/json.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <event2/http.h>
#include "../common.h"
#include "../debug.h"
#include "../config.h"
#include "instaloader.h"
struct instaloader_param {
char action[10];
char profile[100];
};
// define instaloader worker function
static void *
instaloader_worker(void *param)
{
struct instaloader_param *p = (struct instaloader_param *)param;
debug(LOG_DEBUG, "instaloader: action: %s, profile: %s\n", p->action, p->profile);
char cmd[512] = {0};
// create directory instaloader and change current directory to it
snprintf(cmd, sizeof(cmd), "mkdir -p instaloader && cd instaloader");
debug(LOG_DEBUG, "instaloader: cmd: %s\n", cmd);
system(cmd);
if (strcmp(p->action, "download") == 0) {
// download profile
snprintf(cmd, sizeof(cmd), "instaloader --no-captions --no-metadata-json --no-compress-json --no-pictures %s", p->profile);
debug(LOG_DEBUG, "instaloader: cmd: %s\n", cmd);
// use popen to execute cmd and get its output
FILE *fp = popen(cmd, "r");
if (fp == NULL) {
debug(LOG_ERR, "instaloader: popen failed\n");
free(param);
return NULL;
}
char buf[512] = {0};
while (fgets(buf, sizeof(buf), fp) != NULL) {
debug(LOG_DEBUG, "instaloader: %s", buf);
memset(buf, 0, sizeof(buf));
}
pclose(fp);
} else if (strcmp(p->action, "stop") == 0) {
// stop instaloader
debug(LOG_DEBUG, "instaloader: exit the program \n");
exit(0);
} else {
debug(LOG_ERR, "instaloader: unknown action: %s\n", p->action);
}
// free param
free(param);
return 0;
}
static int
parse_instaloader_command(char *json_data, struct instaloader_param *param)
{
// parse json data with json-c to param
json_object *jobj = json_tokener_parse(json_data);
if (jobj == NULL) {
debug(LOG_ERR, "instaloader: json_tokener_parse failed\n");
return -1;
}
// get action
json_object *jaction = NULL;
if (!json_object_object_get_ex(jobj, "action", &jaction)) {
debug(LOG_ERR, "instaloader: json_object_object_get_ex failed\n");
json_object_put(jobj);
return -1;
}
strcpy(param->action, json_object_get_string(jaction));
if (strcmp(param->action, "stop") == 0) {
json_object_put(jobj);
return 0;
}
// get profile
json_object *jprofile = NULL;
if (!json_object_object_get_ex(jobj, "profile", &jprofile)) {
debug(LOG_ERR, "instaloader: json_object_object_get_ex failed\n");
json_object_put(jobj);
return -1;
}
strcpy(param->profile, json_object_get_string(jprofile));
// free json object
json_object_put(jobj);
return 0;
}
static void
instaloader_response(struct evhttp_request *req, char *result)
{
struct evbuffer *resp = evbuffer_new();
evbuffer_add_printf(resp, "{\"status\": \"%s\"}", result);
evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "application/json");
evhttp_send_reply(req, HTTP_OK, "OK", resp);
}
// define instaloader read callback function
static void
instaloader_read_cb(struct evhttp_request *req, void *args)
{
#define BUFF_LEN 4096
// read data from bufferevent
char data[BUFF_LEN] = {0};
struct evbuffer *input = evhttp_request_get_input_buffer(req);
size_t len = evbuffer_get_length(input);
assert(len < BUFF_LEN);
if (len >= BUFF_LEN) {
debug(LOG_ERR, "instaloader: data length is too long\n");
instaloader_response(req, "data length is too long");
return;
}
debug(LOG_DEBUG, "instaloader: data: %s\n", data);
// parse http post and get its json data
evbuffer_copyout(input, data, len);
debug(LOG_DEBUG, "instaloader: data: %s\n", data);
struct instaloader_param *param = (struct instaloader_param *)malloc(sizeof(struct instaloader_param));
assert(param != NULL);
memset(param, 0, sizeof(struct instaloader_param));
int nret = parse_instaloader_command(data, param);
if (nret != 0) {
debug(LOG_ERR, "instaloader: parse_command failed\n");
free(param);
instaloader_response(req, "failed to parse command");
return;
}
// create a thread
pthread_t thread;
// create a thread attribute
pthread_attr_t attr;
// initialize thread attribute
pthread_attr_init(&attr);
// set thread attribute to detach
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// create a thread
pthread_create(&thread, &attr, instaloader_worker, param);
// destroy thread attribute
pthread_attr_destroy(&attr);
instaloader_response(req, "ok");
}
// define instaloader http post callback function
static void
http_post_cb(struct evhttp_request *req, void *arg)
{
// check http request method
if (evhttp_request_get_command(req) != EVHTTP_REQ_POST) {
debug(LOG_ERR, "instaloader: http request method is not POST\n");
evhttp_send_error(req, HTTP_BADMETHOD, "Method Not Allowed");
return;
}
// Check the HTTP request content type
const char *content_type = evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type");
if (content_type == NULL || strcmp(content_type, "application/json") != 0) {
debug(LOG_ERR, "instaloader: http request content type is not application/json\n");
evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request");
return;
}
// get json data from http request
instaloader_read_cb(req, arg);
}
// define instaloader service
static void *
instaloader_service(void *local_port)
{
uint16_t port = *(uint16_t *)local_port;
free(local_port);
// Initialize libevent
struct event_base *base = event_base_new();
if (!base) {
debug(LOG_ERR, "instaloader: Failed to initialize libevent\n");
return NULL;
}
// Create a new HTTP server
struct evhttp *http = evhttp_new(base);
if (!http) {
debug(LOG_ERR, "Failed to create HTTP server\n");
return NULL;
}
if (evhttp_bind_socket(http, "0.0.0.0", port) != 0) {
debug(LOG_ERR, "Failed to bind HTTP server to port %d\n", port);
return NULL;
}
debug(LOG_DEBUG, "instaloader: start instaloader service on port %d\n", port);
// Set up a callback function for handling HTTP requests
evhttp_set_cb(http, "/", http_post_cb, NULL);
// Start the event loop
event_base_dispatch(base);
// Clean up
evhttp_free(http);
event_base_free(base);
return NULL;
}
int
start_instaloader_service(uint16_t local_port)
{
uint16_t *p = (uint16_t *)malloc(sizeof(uint16_t));
assert(p != NULL);
*p = local_port;
// create a thread
pthread_t thread;
// create a thread attribute
pthread_attr_t attr;
// initialize thread attribute
pthread_attr_init(&attr);
// set thread attribute to detach
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// create a thread
pthread_create(&thread, &attr, instaloader_service, (void *)p);
// destroy thread attribute
pthread_attr_destroy(&attr);
return 0;
}

6
plugins/instaloader.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef _INSTALOADER_H_
#define _INSTALOADER_H_
int start_instaloader_service(uint16_t local_port);
#endif

615
plugins/telnetd.c Normal file
View File

@@ -0,0 +1,615 @@
/*
* Simple telnet server
* Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
*
* This file is distributed under the GNU Public License (GPL),
* please see the file LICENSE for further information.
*
* ---------------------------------------------------------------------------
* (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
****************************************************************************
*
* The telnetd manpage says it all:
*
* Telnetd operates by allocating a pseudo-terminal device (see pty(4)) for
* a client, then creating a login process which has the slave side of the
* pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the
* master side of the pseudo-terminal, implementing the telnet protocol and
* passing characters between the remote client and the login process.
*
* Vladimir Oleynik <dzo@simtreas.ru> 2001
* Set process group corrections, initial busybox port
*
* BusyBox is distributed under version 2 of the General Public License please
* see the file LICENSE for further information. Version 2 is the only version
* of this license which this version of BusyBox
* (or modified versions derived from this one) may be
* distributed under.
* https://busybox.net/downloads/busybox-0.60.5.tar.bz2
*/
#define _GNU_SOURCE
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <stdio.h>
#include <signal.h>
#include <termios.h>
#include <arpa/telnet.h>
#include <ctype.h>
#include <sys/syslog.h>
#include <pthread.h>
#include "telnetd.h"
#include "../debug.h"
typedef struct sockaddr_in sockaddr_type;
static const char *loginpath = "/bin/login";
/* shell name and arguments */
static const char *argv_init[] = {NULL, NULL};
/* structure that describes a session */
struct tsession
{
struct tsession *next;
int sockfd, ptyfd;
int shell_pid;
/* two circular buffers */
char *buf1, *buf2;
int rdidx1, wridx1, size1;
int rdidx2, wridx2, size2;
};
/*
This is how the buffers are used. The arrows indicate the movement
of data.
+-------+ wridx1++ +------+ rdidx1++ +----------+
| | <-------------- | buf1 | <-------------- | |
| | size1-- +------+ size1++ | |
| pty | | socket |
| | rdidx2++ +------+ wridx2++ | |
| | --------------> | buf2 | --------------> | |
+-------+ size2++ +------+ size2-- +----------+
Each session has got two buffers.
*/
static int maxfd;
static struct tsession *sessions;
/*
Remove all IAC's from the buffer pointed to by bf (recieved IACs are ignored
and must be removed so as to not be interpreted by the terminal). Make an
uninterrupted string of characters fit for the terminal. Do this by packing
all characters meant for the terminal sequentially towards the end of bf.
Return a pointer to the beginning of the characters meant for the terminal.
and make *num_totty the number of characters that should be sent to
the terminal.
Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
past (bf + len) then that IAC will be left unprocessed and *processed will be
less than len.
FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
what is the escape character? We aren't handling that situation here.
CR-LF ->'s CR mapping is also done here, for convenience
*/
static char *
remove_iacs(struct tsession *ts, int *pnum_totty)
{
unsigned char *ptr0 = ts->buf1 + ts->wridx1;
unsigned char *ptr = ptr0;
unsigned char *totty = ptr;
unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
int processed;
int num_totty;
while (ptr < end)
{
if (*ptr != IAC)
{
int c = *ptr;
*totty++ = *ptr++;
/* We now map \r\n ==> \r for pragmatic reasons.
* Many client implementations send \r\n when
* the user hits the CarriageReturn key.
*/
if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
ptr++;
}
else
{
/*
* TELOPT_NAWS support!
*/
if ((ptr + 2) >= end)
{
/* only the beginning of the IAC is in the
buffer we were asked to process, we can't
process this char. */
break;
}
/*
* IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
*/
else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS)
{
struct winsize ws;
if ((ptr + 8) >= end)
break; /* incomplete, can't process */
ws.ws_col = (ptr[3] << 8) | ptr[4];
ws.ws_row = (ptr[5] << 8) | ptr[6];
(void)ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
ptr += 9;
}
else
{
/* skip 3-byte IAC non-SB cmd */
ptr += 3;
}
}
}
processed = ptr - ptr0;
num_totty = totty - ptr0;
/* the difference between processed and num_to tty
is all the iacs we removed from the stream.
Adjust buf1 accordingly. */
ts->wridx1 += processed - num_totty;
ts->size1 -= processed - num_totty;
*pnum_totty = num_totty;
/* move the chars meant for the terminal towards the end of the
buffer. */
return memmove(ptr - num_totty, ptr0, num_totty);
}
static int
getpty(char *line)
{
#ifdef OLD_GETPTY
int p;
p = open("/dev/ptmx", 2);
if (p > 0)
{
grantpt(p);
unlockpt(p);
strcpy(line, ptsname(p));
return (p);
}
return -1;
#else
int p;
p = open("/dev/ptmx", O_RDWR);
if (p >= 0)
{
grantpt(p);
unlockpt(p);
if (ptsname_r(p, line, GETPTY_BUFSIZE - 1) != 0)
{
debug(LOG_ERR, "ptsname error (is /dev/pts mounted?)");
return -1;
}
line[GETPTY_BUFSIZE - 1] = '\0';
return p;
}
struct stat stb;
int i;
int j;
strcpy(line, "/dev/ptyXX");
for (i = 0; i < 16; i++)
{
line[8] = "pqrstuvwxyzabcde"[i];
line[9] = '0';
if (stat(line, &stb) < 0)
{
continue;
}
for (j = 0; j < 16; j++)
{
line[9] = j < 10 ? j + '0' : j - 10 + 'a';
p = open(line, O_RDWR | O_NOCTTY);
if (p >= 0)
{
line[5] = 't';
return p;
}
}
}
#endif
}
static void
send_iac(struct tsession *ts, unsigned char command, int option)
{
/* We rely on that there is space in the buffer for now. */
char *b = ts->buf2 + ts->rdidx2;
*b++ = IAC;
*b++ = command;
*b++ = option;
ts->rdidx2 += 3;
ts->size2 += 3;
}
static struct tsession *
make_new_session(int sockfd)
{
struct termios termbuf;
int pty, pid;
char tty_name[32];
struct tsession *ts = malloc(sizeof(struct tsession) + BUFSIZE * 2);
ts->buf1 = (char *)(&ts[1]);
ts->buf2 = ts->buf1 + BUFSIZE;
ts->sockfd = sockfd;
ts->rdidx1 = ts->wridx1 = ts->size1 = 0;
ts->rdidx2 = ts->wridx2 = ts->size2 = 0;
/* Got a new connection, set up a tty and spawn a shell. */
pty = getpty(tty_name);
if (pty < 0)
{
debug(LOG_ERR, "All network ports in use!");
return 0;
}
if (pty > maxfd)
maxfd = pty;
ts->ptyfd = pty;
/* Make the telnet client understand we will echo characters so it
* should not do it locally. We don't tell the client to run linemode,
* because we want to handle line editing and tab completion and other
* stuff that requires char-by-char support.
*/
send_iac(ts, DO, TELOPT_ECHO);
send_iac(ts, DO, TELOPT_NAWS);
send_iac(ts, DO, TELOPT_LFLOW);
send_iac(ts, WILL, TELOPT_ECHO);
send_iac(ts, WILL, TELOPT_SGA);
if ((pid = fork()) < 0)
{
syslog(LOG_ERR, "Can`t forking");
}
if (pid == 0)
{
/* In child, open the child's side of the tty. */
int i;
for (i = 0; i <= maxfd; i++)
close(i);
/* make new process group */
setsid();
if (open(tty_name, O_RDWR) < 0)
{
syslog(LOG_ERR, "Could not open tty");
exit(1);
}
dup(0);
dup(0);
tcsetpgrp(0, getpid());
/* The pseudo-terminal allocated to the client is configured to operate in
* cooked mode, and with XTABS CRMOD enabled (see tty(4)).
*/
tcgetattr(0, &termbuf);
termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
termbuf.c_oflag |= ONLCR | XTABS;
termbuf.c_iflag |= ICRNL;
termbuf.c_iflag &= ~IXOFF;
/*termbuf.c_lflag &= ~ICANON;*/
tcsetattr(0, TCSANOW, &termbuf);
/* exec shell, with correct argv and env */
execv(loginpath, (char *const *)argv_init);
/* NOT REACHED */
syslog(LOG_ERR, "execv error");
exit(1);
}
ts->shell_pid = pid;
return ts;
}
static void
free_session(struct tsession *ts)
{
struct tsession *t = sessions;
/* Unlink this telnet session from the session list. */
if (t == ts)
sessions = ts->next;
else
{
while (t->next != ts)
t = t->next;
t->next = ts->next;
}
kill(ts->shell_pid, SIGKILL);
wait4(ts->shell_pid, NULL, 0, NULL);
close(ts->ptyfd);
close(ts->sockfd);
if (ts->ptyfd == maxfd || ts->sockfd == maxfd)
maxfd--;
if (ts->ptyfd == maxfd || ts->sockfd == maxfd)
maxfd--;
free(ts);
}
// create a function for a thread, so we can use it in the main function
static void *
simple_telnetd_thread(void *arg)
{
sockaddr_type sa;
int master_fd;
fd_set rdfdset, wrfdset;
int selret;
int on = 1;
uint16_t portnbr = arg ? *(uint16_t *)arg : 2323;
int maxlen, w, r;
free(arg);
debug(LOG_INFO, "Starting telnetd on port %d\n", portnbr);
if (access(loginpath, X_OK) < 0)
{
debug(LOG_ERR, "No login shell found at %s\n", loginpath);
return NULL;
}
argv_init[0] = loginpath;
sessions = 0;
/* Grab a TCP socket. */
master_fd = socket(SOCKET_TYPE, SOCK_STREAM, 0);
if (master_fd < 0)
{
debug(LOG_ERR, "Unable to create socket\n");
return NULL;
}
(void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
/* Set it to listen to specified port. */
memset((void *)&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(portnbr);
if (bind(master_fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
{
debug(LOG_ERR, "Failed to bind socket: %s\n", strerror(errno));
close(master_fd);
return NULL;
}
if (listen(master_fd, 1) < 0)
{
debug(LOG_ERR, "Socket failed to listen\n");
close(master_fd);
return NULL;
}
maxfd = master_fd;
do
{
struct tsession *ts;
FD_ZERO(&rdfdset);
FD_ZERO(&wrfdset);
/* select on the master socket, all telnet sockets and their
* ptys if there is room in their respective session buffers.
*/
FD_SET(master_fd, &rdfdset);
ts = sessions;
while (ts)
{
/* buf1 is used from socket to pty
* buf2 is used from pty to socket
*/
if (ts->size1 > 0)
{
FD_SET(ts->ptyfd, &wrfdset); /* can write to pty */
}
if (ts->size1 < BUFSIZE)
{
FD_SET(ts->sockfd, &rdfdset); /* can read from socket */
}
if (ts->size2 > 0)
{
FD_SET(ts->sockfd, &wrfdset); /* can write to socket */
}
if (ts->size2 < BUFSIZE)
{
FD_SET(ts->ptyfd, &rdfdset); /* can read from pty */
}
ts = ts->next;
}
selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
if (!selret)
break;
/* First check for and accept new sessions. */
if (FD_ISSET(master_fd, &rdfdset))
{
int fd, salen;
salen = sizeof(sa);
if ((fd = accept(master_fd, (struct sockaddr *)&sa,
&salen)) < 0)
{
continue;
}
else
{
/* Create a new session and link it into
our active list. */
struct tsession *new_ts = make_new_session(fd);
if (new_ts)
{
new_ts->next = sessions;
sessions = new_ts;
if (fd > maxfd)
maxfd = fd;
}
else
{
close(fd);
}
}
}
/* Then check for data tunneling. */
ts = sessions;
while (ts)
{ /* For all sessions... */
struct tsession *next = ts->next; /* in case we free ts. */
if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset))
{
int num_totty;
char *ptr;
/* Write to pty from buffer 1. */
ptr = remove_iacs(ts, &num_totty);
w = write(ts->ptyfd, ptr, num_totty);
if (w < 0)
{
free_session(ts);
ts = next;
continue;
}
ts->wridx1 += w;
ts->size1 -= w;
if (ts->wridx1 == BUFSIZE)
ts->wridx1 = 0;
}
if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset))
{
/* Write to socket from buffer 2. */
maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen);
if (w < 0)
{
free_session(ts);
ts = next;
continue;
}
ts->wridx2 += w;
ts->size2 -= w;
if (ts->wridx2 == BUFSIZE)
ts->wridx2 = 0;
}
if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset))
{
/* Read from socket to buffer 1. */
maxlen = MIN(BUFSIZE - ts->rdidx1,
BUFSIZE - ts->size1);
r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen);
if (!r || (r < 0 && errno != EINTR))
{
free_session(ts);
ts = next;
continue;
}
if (!*(ts->buf1 + ts->rdidx1 + r - 1))
{
r--;
if (!r)
continue;
}
ts->rdidx1 += r;
ts->size1 += r;
if (ts->rdidx1 == BUFSIZE)
ts->rdidx1 = 0;
}
if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset))
{
/* Read from pty to buffer 2. */
maxlen = MIN(BUFSIZE - ts->rdidx2,
BUFSIZE - ts->size2);
r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
if (!r || (r < 0 && errno != EINTR))
{
free_session(ts);
ts = next;
continue;
}
ts->rdidx2 += r;
ts->size2 += r;
if (ts->rdidx2 == BUFSIZE)
ts->rdidx2 = 0;
}
if (ts->size1 == 0)
{
ts->rdidx1 = 0;
ts->wridx1 = 0;
}
if (ts->size2 == 0)
{
ts->rdidx2 = 0;
ts->wridx2 = 0;
}
ts = next;
}
} while (1);
return 0;
}
int
simple_telnetd_start(uint16_t port)
{
pthread_t thread;
uint16_t *port_ptr = malloc(sizeof(uint16_t));
*port_ptr = port;
pthread_create(&thread, NULL, simple_telnetd_thread, port_ptr);
return 0;
}

14
plugins/telnetd.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef _TELNETD_H
#define _TELNETD_H
#define BUFSIZE 4000
#define SOCKET_TYPE AF_INET
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
enum
{
GETPTY_BUFSIZE = 16
};
int simple_telnetd_start(uint16_t port);
#endif

289
plugins/youtubedl.c Normal file
View File

@@ -0,0 +1,289 @@
#include <json-c/json.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
#include <event2/http.h>
#include "../common.h"
#include "../debug.h"
#include "../config.h"
#include "youtubedl.h"
struct yt_dlp_param {
char action[10];
char profile[100];
};
// define yt-dlp worker function
static void *
yt_dlp_worker(void *param)
{
struct yt_dlp_param *p = (struct yt_dlp_param *)param;
debug(LOG_DEBUG, "yt-dlp: action: %s, url: %s\n", p->action, p->profile);
char cmd[512] = {0};
// create directory yt-dlp and change current directory to it
snprintf(cmd, sizeof(cmd), "mkdir -p yt-dlp && cd yt-dlp");
debug(LOG_DEBUG, "yt-dlp: cmd: %s\n", cmd);
system(cmd);
if (strcmp(p->action, "download") == 0) {
// download profile
snprintf(cmd, sizeof(cmd), "yt-dlp %s", p->profile);
debug(LOG_DEBUG, "yt-dlp: cmd: %s\n", cmd);
// use popen to execute cmd and get its output
FILE *fp = popen(cmd, "r");
if (fp == NULL) {
debug(LOG_ERR, "yt-dlp: popen failed\n");
free(param);
return NULL;
}
char buf[512] = {0};
while (fgets(buf, sizeof(buf), fp) != NULL) {
debug(LOG_DEBUG, "yt-dlp: %s", buf);
memset(buf, 0, sizeof(buf));
}
pclose(fp);
} else {
debug(LOG_ERR, "yt-dlp: unknown action: %s\n", p->action);
}
// free param
free(param);
return 0;
}
static int
parse_yt_dlp_command(char *json_data, struct yt_dlp_param *param)
{
// parse json data with json-c to param
json_object *jobj = json_tokener_parse(json_data);
if (jobj == NULL) {
debug(LOG_ERR, "yt-dlp: json_tokener_parse failed\n");
return -1;
}
// get action
json_object *jaction = NULL;
if (!json_object_object_get_ex(jobj, "action", &jaction)) {
debug(LOG_ERR, "yt-dlp: json_object_object_get_ex failed\n");
json_object_put(jobj);
return -1;
}
strcpy(param->action, json_object_get_string(jaction));
if (strcmp(param->action, "stop") == 0) {
json_object_put(jobj);
return 0;
}
// get profile
json_object *jprofile = NULL;
if (!json_object_object_get_ex(jobj, "profile", &jprofile)) {
debug(LOG_ERR, "yt-dlp: json_object_object_get_ex failed\n");
json_object_put(jobj);
return -1;
}
strcpy(param->profile, json_object_get_string(jprofile));
// free json object
json_object_put(jobj);
return 0;
}
static void
yt_dlp_response(struct evhttp_request *req, char *result)
{
struct evbuffer *resp = evbuffer_new();
evbuffer_add_printf(resp, "{\"status\": \"%s\"}", result);
evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "application/json");
evhttp_send_reply(req, HTTP_OK, "OK", resp);
}
// define yt-dlp read callback function
static void
yt_dlp_read_cb(struct evhttp_request *req, void *args)
{
#define BUFF_LEN 4096
// read data from bufferevent
char data[BUFF_LEN] = {0};
struct evbuffer *input = evhttp_request_get_input_buffer(req);
size_t len = evbuffer_get_length(input);
assert(len < BUFF_LEN);
if (len >= BUFF_LEN) {
debug(LOG_ERR, "yt-dlp: data length is too long\n");
yt_dlp_response(req, "data length is too long");
return;
}
debug(LOG_DEBUG, "yt-dlp: data: %s\n", data);
// parse http post and get its json data
evbuffer_copyout(input, data, len);
debug(LOG_DEBUG, "yt-dlp: data: %s\n", data);
struct yt_dlp_param *param = (struct yt_dlp_param *)malloc(sizeof(struct yt_dlp_param));
assert(param != NULL);
memset(param, 0, sizeof(struct yt_dlp_param));
int nret = parse_yt_dlp_command (data, param);
if (nret != 0) {
debug(LOG_ERR, "yt-dlp: parse_command failed\n");
free(param);
yt_dlp_response(req, "failed to parse command");
return;
}
// create a thread
pthread_t thread;
// create a thread attribute
pthread_attr_t attr;
// initialize thread attribute
pthread_attr_init(&attr);
// set thread attribute to detach
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// create a thread
pthread_create(&thread, &attr, yt_dlp_worker, param);
// destroy thread attribute
pthread_attr_destroy(&attr);
yt_dlp_response(req, "ok");
}
// define yt-dlp http post callback function
static void
http_post_cb(struct evhttp_request *req, void *arg)
{
// check http request method
if (evhttp_request_get_command(req) != EVHTTP_REQ_POST) {
debug(LOG_ERR, "yt-dlp: http request method is not POST\n");
evhttp_send_error(req, HTTP_BADMETHOD, "Method Not Allowed");
return;
}
// Check the HTTP request content type
const char *content_type = evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type");
if (content_type == NULL || strcmp(content_type, "application/json") != 0) {
debug(LOG_ERR, "yt-dlp: http request content type is not application/json\n");
evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request");
return;
}
// get json data from http request
yt_dlp_read_cb(req, arg);
}
static int
install_yt_dlp()
{
// if yt-dlp exists, return
if (access("/usr/local/bin/yt-dlp", F_OK) == 0) {
debug(LOG_DEBUG, "yt-dlp: yt-dlp exists\n");
return 0;
}
// install yt-dlp to /usr/local/bin
// download yt-dlp through curl or wget if any of them exists
char cmd[512] = {0};
if (access("/usr/bin/curl", F_OK) == 0) {
snprintf(cmd, sizeof(cmd), "sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp");
} else if (access("/usr/bin/wget", F_OK) == 0) {
snprintf(cmd, sizeof(cmd), "sudo wget https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -O /usr/local/bin/yt-dlp");
} else {
debug(LOG_ERR, "yt-dlp: curl and wget are not installed\n");
return -1;
}
debug(LOG_DEBUG, "yt-dlp: cmd: %s\n", cmd);
int nret = system(cmd);
if (nret != 0) {
debug(LOG_ERR, "yt-dlp: system failed\n");
return -1;
}
// change yt-dlp to executable
snprintf(cmd, sizeof(cmd), "sudo chmod a+rx /usr/local/bin/yt-dlp");
debug(LOG_DEBUG, "yt-dlp: cmd: %s\n", cmd);
nret = system(cmd);
if (nret != 0) {
debug(LOG_ERR, "yt-dlp: system failed\n");
return -1;
}
return 0;
}
// define yt-dlp service
static void *
yt_dlp_service(void *local_port)
{
// install yt-dlp
int nret = install_yt_dlp();
if (nret != 0) {
debug(LOG_ERR, "yt-dlp: install_yt_dlp failed\n");
return NULL;
}
uint16_t port = *(uint16_t *)local_port;
free(local_port);
// Initialize libevent
struct event_base *base = event_base_new();
if (!base) {
debug(LOG_ERR, "yt-dlp: Failed to initialize libevent\n");
return NULL;
}
// Create a new HTTP server
struct evhttp *http = evhttp_new(base);
if (!http) {
debug(LOG_ERR, "yt-dlp: Failed to create HTTP server\n");
return NULL;
}
if (evhttp_bind_socket(http, "0.0.0.0", port) != 0) {
debug(LOG_ERR, "yt-dlp: Failed to bind HTTP server to port %d\n", port);
return NULL;
}
debug(LOG_DEBUG, "yt-dlp: start youtube download service on port %d\n", port);
// Set up a callback function for handling HTTP requests
evhttp_set_cb(http, "/", http_post_cb, NULL);
// Start the event loop
event_base_dispatch(base);
// Clean up
evhttp_free(http);
event_base_free(base);
return NULL;
}
int
start_youtubedl_service(uint16_t local_port)
{
uint16_t *p = (uint16_t *)malloc(sizeof(uint16_t));
assert(p != NULL);
*p = local_port;
// create a thread
pthread_t thread;
// create a thread attribute
pthread_attr_t attr;
// initialize thread attribute
pthread_attr_init(&attr);
// set thread attribute to detach
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// create a thread
pthread_create(&thread, &attr, yt_dlp_service, (void *)p);
// destroy thread attribute
pthread_attr_destroy(&attr);
return 0;
}

6
plugins/youtubedl.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef _YOUTUBE_DL_H_
#define _YOUTUBE_DL_H_
int start_youtubedl_service(uint16_t local_port);
#endif

View File

@@ -37,6 +37,7 @@
#include "client.h"
#include "common.h"
#include "tcpmux.h"
#include "msg.h"
#define IP_LEN 16
@@ -56,6 +57,9 @@ void tcp_proxy_c2s_cb(struct bufferevent *bev, void *ctx);
void tcp_proxy_s2c_cb(struct bufferevent *bev, void *ctx);
void ftp_proxy_c2s_cb(struct bufferevent *bev, void *ctx);
void ftp_proxy_s2c_cb(struct bufferevent *bev, void *ctx);
void udp_proxy_c2s_cb(struct bufferevent *bev, void *ctx);
void udp_proxy_s2c_cb(struct bufferevent *bev, void *ctx);
struct proxy *new_proxy_obj(struct bufferevent *bev);
void free_proxy_obj(struct proxy *p);
void set_ftp_data_proxy_tunnel(const char *ftp_proxy_name,
@@ -65,4 +69,6 @@ void set_ftp_data_proxy_tunnel(const char *ftp_proxy_name,
uint32_t handle_socks5(struct proxy_client *client, struct ring_buffer *rb, int len);
uint32_t handle_ss5(struct proxy_client *client, struct ring_buffer *rb, int len);
void handle_udp_packet(struct udp_packet *udp_pkt, struct proxy_client *client);
#endif //_PROXY_H_

220
proxy_udp.c Normal file
View File

@@ -0,0 +1,220 @@
/* 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 proxy_tcp.c
@brief xfrp proxy udp implemented
@author Copyright (C) 2016 Dengfeng Liu <liudf0716@gmail.com>
*/
#include <arpa/inet.h>
#include "debug.h"
#include "uthash.h"
#include "common.h"
#include "proxy.h"
#include "config.h"
#include "tcpmux.h"
#include "control.h"
static const char base64_table[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static int
base64_encode(const uint8_t *src, int srclen, char *dst)
{
uint32_t ac = 0;
int bits = 0;
int i;
char *cp = dst;
for (i = 0; i < srclen; i++) {
ac = (ac << 8) | src[i];
bits += 8;
do {
bits -= 6;
*cp++ = base64_table[(ac >> bits) & 0x3f];
} while (bits >= 6);
}
if (bits) {
*cp++ = base64_table[(ac << (6 - bits)) & 0x3f];
bits -= 6;
}
while (bits < 0) {
*cp++ = '=';
bits += 2;
}
return cp - dst;
}
static int
base64_decode(const char *src, int srclen, uint8_t *dst)
{
uint32_t ac = 0;
int bits = 0;
int i;
uint8_t *bp = dst;
for (i = 0; i < srclen; i++) {
const char *p = strchr(base64_table, src[i]);
if (src[i] == '=') {
ac = (ac << 6);
bits += 6;
if (bits >= 8)
bits -= 8;
continue;
}
if (p == NULL || src[i] == 0)
return -1;
ac = (ac << 6) | (p - base64_table);
bits += 6;
if (bits >= 8) {
bits -= 8;
*bp++ = (uint8_t)(ac >> bits);
}
}
if (ac & ((1 << bits) - 1))
return -1;
return bp - dst;
}
static void
evutil_base64_decode(struct evbuffer *src, struct evbuffer *dst)
{
uint8_t dbuff[1500] = {0};
size_t len = evbuffer_get_length(src);
char *buf = (char *)malloc(len);
assert(buf != NULL);
memset(buf, 0, len);
evbuffer_remove(src, buf, len);
int decode_len = base64_decode(buf, len, dbuff);
assert(decode_len > 0 && decode_len < 1500);
evbuffer_add(dst, dbuff, decode_len);
free(buf);
}
static void
evutil_base64_encode(struct evbuffer *src, struct evbuffer *dst)
{
char ebuff[2048] = {0}; // 2048 is enough for base64 encode
size_t len = evbuffer_get_length(src);
uint8_t *buf = (uint8_t *)malloc(len);
assert(buf != NULL);
memset(buf, 0, len);
evbuffer_remove(src, buf, len);
int encode_len = base64_encode(buf, len, ebuff);
assert(encode_len > 0 && encode_len < 2048);
evbuffer_add(dst, ebuff, encode_len);
free(buf);
}
void
handle_udp_packet(struct udp_packet *udp_pkt, struct proxy_client *client)
{
// debase64 of udp_pkt->content
struct evbuffer *base64_input = evbuffer_new();
size_t content_len = strlen(udp_pkt->content);
evbuffer_add(base64_input, udp_pkt->content, content_len);
struct evbuffer *base64_output = evbuffer_new();
evutil_base64_decode(base64_input, base64_output);
evbuffer_free(base64_input);
// send buf content to local_proxy_bev
struct bufferevent *local_proxy_bev = client->local_proxy_bev;
assert(local_proxy_bev != NULL);
// according to client proxy service's local address and port, send buf to local_proxy_bev
assert(client->ps);
// if client->ps->local_addr is domain, need to resolve it
struct sockaddr_in local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(client->ps->local_port);
if (inet_pton(AF_INET, client->ps->local_ip, &local_addr.sin_addr) <= 0) {
// domain
struct hostent *host = gethostbyname(client->ps->local_ip);
assert(host != NULL);
if (host == NULL) {
debug(LOG_ERR, "gethostbyname %s failed", client->ps->local_ip);
evbuffer_free(base64_output);
return;
}
memcpy(&local_addr.sin_addr, host->h_addr, host->h_length);
}
// send buf to local_proxy_bev
struct evbuffer *dst = bufferevent_get_output(local_proxy_bev);
evbuffer_add_buffer(dst, base64_output);
evbuffer_free(base64_output);
}
void
udp_proxy_c2s_cb(struct bufferevent *bev, void *ctx)
{
struct common_conf *c_conf = get_common_config();
struct proxy_client *client = (struct proxy_client *)ctx;
assert(client);
struct bufferevent *partner = client->ctl_bev;
assert(partner);
struct evbuffer *src = bufferevent_get_input(bev);
// encode src to base64
struct evbuffer *base64_output = evbuffer_new();
evutil_base64_encode(src, base64_output);
evbuffer_free(src);
// convert base64_output to udp_packet and json marshal
struct udp_packet *udp_pkt = (struct udp_packet *)malloc(sizeof(struct udp_packet));
assert(udp_pkt != NULL);
memset(udp_pkt, 0, sizeof(struct udp_packet));
udp_pkt->content = evbuffer_pullup(base64_output, -1);
udp_pkt->raddr = (struct udp_addr *)malloc(sizeof(struct udp_addr));
assert(udp_pkt->raddr != NULL);
memset(udp_pkt->raddr, 0, sizeof(struct udp_addr));
udp_pkt->raddr->addr = client->ps->local_ip;
udp_pkt->raddr->port = client->ps->local_port;
char *buf = NULL;
new_udp_packet_marshal(udp_pkt, &buf);
size_t len = strlen(buf);
free(udp_pkt->raddr);
free(udp_pkt);
if (!c_conf->tcp_mux) {
struct evbuffer *dst = bufferevent_get_output(partner);
evbuffer_add(dst, buf, len);
free(buf);
return;
}
uint32_t nr = tmux_stream_write(partner, buf, len, &client->stream);
if (nr < len) {
debug(LOG_DEBUG, "stream_id [%d] len is %d tmux_stream_write %d data, disable read", client->stream.id, len, nr);
bufferevent_disable(bev, EV_READ);
}
free(buf);
}
void
udp_proxy_s2c_cb(struct bufferevent *bev, void *ctx)
{
struct proxy_client *client = (struct proxy_client *)ctx;
assert(client);
}

211
tcp_redir.c Normal file
View File

@@ -0,0 +1,211 @@
/* 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 tcp_redir.c
@brief xfrp tcp redirect service implemented
@author Copyright (C) 2023 Dengfeng Liu <liu_df@qq.com>
*/
#include <pthread.h>
#include <arpa/inet.h>
#include "common.h"
#include "debug.h"
#include "config.h"
#include "utils.h"
#include "tcp_redir.h"
// define a struct for tcp_redir which include proxy_service and event_base
struct tcp_redir_service {
struct event_base *base;
struct proxy_service *ps;
struct sockaddr_in server_addr;
};
static struct bufferevent *current_bev = NULL; // Global variable to hold the current connection
// define a callback function for read event
static void read_cb(struct bufferevent *bev, void *arg)
{
struct bufferevent *bev_out = (struct bufferevent *)arg;
struct evbuffer *input = bufferevent_get_input(bev);
struct evbuffer *output = bufferevent_get_output(bev_out);
evbuffer_add_buffer(output, input);
}
// define a callback function for event event
static void event_cb(struct bufferevent *bev, short events, void *arg)
{
struct bufferevent *partner = (struct bufferevent *)arg;
if (events & BEV_EVENT_CONNECTED) {
debug(LOG_INFO, "connected");
} else if (events & BEV_EVENT_ERROR) {
debug(LOG_ERR, "connection error");
bufferevent_free(bev);
bufferevent_free(partner);
current_bev = NULL;
} else if (events & BEV_EVENT_EOF) {
debug(LOG_INFO, "connection closed");
bufferevent_free(bev);
bufferevent_free(partner);
current_bev = NULL;
}
}
// define a callback function for accept event
static void accept_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *address, int socklen, void *arg)
{
if (current_bev) {
// Already have a connection, reject new connection
debug(LOG_INFO, "Rejecting new connection. Only one connection allowed at a time.");
evutil_closesocket(fd);
return;
}
// the argument is the proxy_service
struct tcp_redir_service *trs = (struct tcp_redir_service *)arg;
struct event_base *base = trs->base;
// read the data from the local port
struct bufferevent *bev_in = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
if (!bev_in) {
debug(LOG_ERR, "create bufferevent for local port failed!");
evutil_closesocket(fd);
return;
}
// connect to the remote xfrpc service
struct bufferevent *bev_out = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
if (!bev_out) {
debug(LOG_ERR, "create bufferevent for remote xfrps service failed!");
bufferevent_free(bev_in);
return;
}
// connect to the remote port
if (bufferevent_socket_connect(bev_out, (struct sockaddr *)&(trs->server_addr), sizeof(trs->server_addr)) < 0) {
debug(LOG_ERR, "connect to remote port failed! %s", strerror(errno));
bufferevent_free(bev_in);
bufferevent_free(bev_out);
return;
}
debug(LOG_INFO, "connect to remote xfrps service [%s:%d] success!",
get_common_config()->server_addr, trs->ps->remote_port);
bufferevent_setcb(bev_in, read_cb, NULL, event_cb, (void *)bev_out);
bufferevent_setcb(bev_out, read_cb, NULL, event_cb, (void *)bev_in);
bufferevent_enable(bev_in, EV_READ|EV_WRITE);
bufferevent_enable(bev_out, EV_READ|EV_WRITE);
current_bev = bev_in;
debug(LOG_INFO, "connect to remote port success!");
return;
}
// define a thread worker function for tcp_redir
static void *tcp_redir_worker(void *arg)
{
struct proxy_service *ps = (struct proxy_service *)arg;
struct common_conf *c_conf = get_common_config();
// the worker is based on libevent and bufferevent
// it listens on the local port and forward the data to the remote port
// the local port and remote port are defined in the proxy_service
// the proxy_service as argument is passed to the worker function
// create a event_base
struct evconnlistener *listener;
struct event_base *base = event_base_new();
if (!base) {
debug(LOG_ERR, "create event base failed!");
exit(1);
}
// define listen address and port
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(ps->local_port);
sin.sin_addr.s_addr = htonl(INADDR_ANY);
struct tcp_redir_service trs;
trs.base = base;
trs.ps = ps;
trs.server_addr.sin_family = AF_INET;
trs.server_addr.sin_port = htons(ps->remote_port);
// if c_conf->server_addr is ip address, use inet_addr to convert it
// if c_conf->server_addr is domain name, use gethostbyname to convert it
if (is_valid_ip_address(c_conf->server_addr))
trs.server_addr.sin_addr.s_addr = inet_addr(c_conf->server_addr);
else {
struct hostent *host = gethostbyname(c_conf->server_addr);
if (!host) {
debug(LOG_ERR, "gethostbyname failed!");
exit(1);
}
// only support ipv4
if (host->h_addrtype != AF_INET) {
debug(LOG_ERR, "only support ipv4!");
exit(1);
}
trs.server_addr.sin_addr.s_addr = *(unsigned long *)host->h_addr_list[0];
}
// create a listener
listener = evconnlistener_new_bind(base, accept_cb, (void *)&trs,
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1, (struct sockaddr *)&sin, sizeof(sin));
if (!listener) {
debug(LOG_ERR, "create listener failed!");
exit(1);
}
// start the event loop
event_base_dispatch(base);
// free the listener
evconnlistener_free(listener);
// free the event base
event_base_free(base);
return NULL;
}
void start_tcp_redir_service(struct proxy_service *ps)
{
// create a thread
pthread_t tid;
if (pthread_create(&tid, NULL, tcp_redir_worker, (void *)ps) != 0) {
debug(LOG_ERR, "create tcp_redir worker thread failed!");
exit(1);
}
debug(LOG_INFO, "create tcp_redir worker thread success!");
// detach the thread
if (pthread_detach(tid) != 0) {
debug(LOG_ERR, "detach tcp_redir worker thread failed!");
exit(1);
}
debug(LOG_INFO, "detach tcp_redir worker thread success!");
return;
}

8
tcp_redir.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef _TCP_REDIR_H_
#define _TCP_REDIR_H_
#include "proxy.h"
void start_tcp_redir_service(struct proxy_service *proxy);
#endif

View File

@@ -31,7 +31,7 @@ void s_sleep(unsigned int s, unsigned int u)
}
// is_valid_ip_address:
// return 0:ipaddress unlegal
// return 0:ip address illegal
int is_valid_ip_address(const char *ip_address)
{
struct sockaddr_in sa;

View File

@@ -1,7 +1,7 @@
#ifndef _VERSION_H_
#define _VERSION_H_
#define VERSION "2.1.606"
#define VERSION "2.12.656"
#define PROTOCOL_VERESION "0.43.0"
#define CLIENT_V 1

37
xfrpc.c
View File

@@ -45,11 +45,46 @@
#include "crypto.h"
#include "msg.h"
#include "utils.h"
#include "tcp_redir.h"
#include "config.h"
#include "plugins/telnetd.h"
#include "plugins/instaloader.h"
#include "plugins/httpd.h"
static void start_xfrpc_local_service()
{
// iterate all proxy service to find mstsc service
// if found, start tcp_redir for it
struct proxy_service *ps, *ps_tmp;
struct proxy_service *all_ps = get_all_proxy_services();
HASH_ITER(hh, all_ps, ps, ps_tmp) {
if (ps->plugin) {
if (strcmp(ps->plugin, "telnetd") == 0) {
simple_telnetd_start(ps->local_port);
} else if (strcmp(ps->plugin, "instaloader") == 0) {
// start instaloader service
start_instaloader_service(ps->local_port);
} else if (strcmp(ps->plugin, "youtubedl") == 0) {
// start youtubedl service
start_youtubedl_service(ps->local_port);
} else if (strcmp(ps->plugin, "instaloader_redir") == 0) {
start_tcp_redir_service(ps);
} else if (strcmp(ps->plugin, "httpd") == 0) {
start_httpd_service(ps);
} else {
debug(LOG_ERR, "start_xfrpc_local_service: unknown plugin %s\n", ps->plugin);
}
}
}
}
void xfrpc_loop()
{
start_xfrpc_local_service();
init_main_control();
run_control();
close_main_control();
}