Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68ffcb2a8a | ||
|
|
2a4eceeeab | ||
|
|
46e3f7df6b | ||
|
|
969c0a160d | ||
|
|
1fd95588ed | ||
|
|
aaeaa2203a | ||
|
|
c895f221da | ||
|
|
be51233c04 | ||
|
|
86866e7c60 | ||
|
|
2e20d95333 | ||
|
|
30ef8fe688 | ||
|
|
f6faea35bd | ||
|
|
d137205777 | ||
|
|
3a029dbe25 | ||
|
|
4b1242a837 | ||
|
|
4615827afd | ||
|
|
f8b8418610 | ||
|
|
b5f4a2aa13 | ||
|
|
0caec598b2 | ||
|
|
6296108fcb | ||
|
|
bd13b67bb6 | ||
|
|
711bb918e7 | ||
|
|
8d25270d32 |
9
.github/workflows/linux.yml
vendored
9
.github/workflows/linux.yml
vendored
@@ -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
1
.gitignore
vendored
@@ -44,3 +44,4 @@ xfrpc
|
||||
xfrp_test_server
|
||||
bin
|
||||
.vscode
|
||||
build
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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'
|
||||
|
||||
63
client.c
63
client.c
@@ -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,
|
||||
|
||||
11
client.h
11
client.h
@@ -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;
|
||||
@@ -100,6 +100,13 @@ struct proxy_service {
|
||||
// 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;
|
||||
@@ -122,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
118
colab/xfrpc.ipynb
Normal 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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
146
config.c
146
config.c
@@ -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)
|
||||
{
|
||||
@@ -314,6 +424,14 @@ proxy_service_handler(void *user, const char *sect, const char *nm, const char *
|
||||
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);
|
||||
@@ -326,6 +444,8 @@ proxy_service_handler(void *user, const char *sect, const char *nm, const char *
|
||||
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);
|
||||
|
||||
9
config.h
9
config.h
@@ -29,7 +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
|
||||
|
||||
48
control.c
48
control.c
@@ -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;
|
||||
|
||||
@@ -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_
|
||||
|
||||
3
login.c
3
login.c
@@ -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);
|
||||
|
||||
3
login.h
3
login.h
@@ -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
10039
mongoose.c
Normal file
File diff suppressed because it is too large
Load Diff
1915
mongoose.h
Normal file
1915
mongoose.h
Normal file
File diff suppressed because it is too large
Load Diff
356
msg.c
356
msg.c
@@ -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);
|
||||
@@ -191,14 +199,14 @@ new_proxy_service_marshal(const struct proxy_service *np_req, char **msg)
|
||||
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 ||
|
||||
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);
|
||||
}
|
||||
@@ -233,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);
|
||||
@@ -249,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));
|
||||
@@ -278,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);
|
||||
|
||||
@@ -287,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) {
|
||||
@@ -297,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);
|
||||
|
||||
@@ -320,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);
|
||||
|
||||
@@ -358,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);
|
||||
@@ -377,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);
|
||||
@@ -404,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
21
msg.h
@@ -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_
|
||||
|
||||
81
plugins/httpd.c
Normal file
81
plugins/httpd.c
Normal 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
8
plugins/httpd.h
Normal 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
247
plugins/instaloader.c
Normal 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
6
plugins/instaloader.h
Normal 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
615
plugins/telnetd.c
Normal 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
14
plugins/telnetd.h
Normal 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
289
plugins/youtubedl.c
Normal 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
6
plugins/youtubedl.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef _YOUTUBE_DL_H_
|
||||
#define _YOUTUBE_DL_H_
|
||||
|
||||
int start_youtubedl_service(uint16_t local_port);
|
||||
|
||||
#endif
|
||||
6
proxy.h
6
proxy.h
@@ -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
220
proxy_udp.c
Normal 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
211
tcp_redir.c
Normal 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
8
tcp_redir.h
Normal 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
|
||||
2
utils.c
2
utils.c
@@ -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;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef _VERSION_H_
|
||||
#define _VERSION_H_
|
||||
|
||||
#define VERSION "2.5.633"
|
||||
#define VERSION "2.12.656"
|
||||
#define PROTOCOL_VERESION "0.43.0"
|
||||
#define CLIENT_V 1
|
||||
|
||||
|
||||
37
xfrpc.c
37
xfrpc.c
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user