Compare commits
19 Commits
2.6.633
...
instaloade
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da2830c31e | ||
|
|
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,26 @@ set(src_xfrpc
|
||||
common.c
|
||||
login.c
|
||||
proxy_tcp.c
|
||||
proxy_udp.c
|
||||
proxy_ftp.c
|
||||
proxy.c
|
||||
tcpmux.c
|
||||
tcp_redir.c
|
||||
)
|
||||
|
||||
|
||||
set(src_xfrpc_plugins
|
||||
plugins/telnetd.c
|
||||
plugins/instaloader.c)
|
||||
|
||||
set(libs
|
||||
ssl
|
||||
crypto
|
||||
event
|
||||
z
|
||||
m
|
||||
json-c)
|
||||
json-c
|
||||
crypt
|
||||
pthread)
|
||||
|
||||
set(test_libs
|
||||
event
|
||||
@@ -98,7 +106,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,
|
||||
|
||||
9
client.h
9
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,11 @@ struct proxy_service {
|
||||
// load balance
|
||||
char *group;
|
||||
char *group_key;
|
||||
|
||||
// plugin
|
||||
char *plugin;
|
||||
char *plugin_user;
|
||||
char *plugin_pwd;
|
||||
|
||||
// private arguments
|
||||
UT_hash_handle hh;
|
||||
@@ -122,6 +127,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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
126
config.c
126
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,13 @@ 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;
|
||||
|
||||
return ps;
|
||||
}
|
||||
|
||||
@@ -219,7 +238,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 +265,79 @@ 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_ROMOTE_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_ROMOTE_PORT;
|
||||
if (ps->local_ip == NULL)
|
||||
ps->local_ip = strdup("0.0.0.0");
|
||||
} 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 +406,12 @@ 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 {
|
||||
debug(LOG_ERR, "unknown option %s in section %s", nm, section);
|
||||
SAFE_FREE(section);
|
||||
@@ -326,6 +424,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);
|
||||
|
||||
5
config.h
5
config.h
@@ -29,7 +29,12 @@
|
||||
#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_ROMOTE_PORT 10001
|
||||
|
||||
#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_
|
||||
|
||||
425
msg.c
425
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,31 @@ 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)
|
||||
{
|
||||
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);
|
||||
@@ -155,10 +161,11 @@ login_request_marshal(char **msg)
|
||||
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) {
|
||||
if (tmp && strlen(tmp) > 0)
|
||||
{
|
||||
nret = strlen(tmp);
|
||||
*msg = strdup(tmp);
|
||||
assert(*msg);
|
||||
@@ -168,54 +175,66 @@ 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 {
|
||||
}
|
||||
else
|
||||
{
|
||||
JSON_MARSHAL_TYPE(j_np_req, "proxy_type", string, np_req->proxy_type);
|
||||
}
|
||||
JSON_MARSHAL_TYPE(j_np_req, "use_encryption", boolean, np_req->use_encryption);
|
||||
JSON_MARSHAL_TYPE(j_np_req, "use_compression", boolean, np_req->use_compression);
|
||||
|
||||
// if proxy_type is tcp, http, https and socks5, set group and group_key to j_np_req
|
||||
if (strcmp(np_req->proxy_type, "tcp") == 0 ||
|
||||
strcmp(np_req->proxy_type, "http") == 0 ||
|
||||
strcmp(np_req->proxy_type, "https") == 0 ||
|
||||
strcmp(np_req->proxy_type, "socks5") == 0) {
|
||||
|
||||
if (np_req->group) {
|
||||
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) {
|
||||
if (np_req->group_key)
|
||||
{
|
||||
JSON_MARSHAL_TYPE(j_np_req, "group_key", string, np_req->group_key);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_ftp_proxy(np_req)) {
|
||||
if (is_ftp_proxy(np_req))
|
||||
{
|
||||
JSON_MARSHAL_TYPE(j_np_req, "remote_data_port", int, np_req->remote_data_port);
|
||||
}
|
||||
|
||||
if (np_req->custom_domains) {
|
||||
if (np_req->custom_domains)
|
||||
{
|
||||
fill_custom_domains(j_np_req, np_req->custom_domains);
|
||||
json_object_object_add(j_np_req, "remote_port", NULL);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
json_object_object_add(j_np_req, "custom_domains", NULL);
|
||||
if (np_req->remote_port != -1) {
|
||||
if (np_req->remote_port != -1)
|
||||
{
|
||||
JSON_MARSHAL_TYPE(j_np_req, "remote_port", int, np_req->remote_port);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
json_object_object_add(j_np_req, "remote_port", NULL);
|
||||
}
|
||||
}
|
||||
@@ -223,23 +242,28 @@ new_proxy_service_marshal(const struct proxy_service *np_req, char **msg)
|
||||
JSON_MARSHAL_TYPE(j_np_req, "subdomain", string, SAFE_JSON_STRING(np_req->subdomain));
|
||||
|
||||
json_object *j_location_array = json_object_new_array();
|
||||
if (np_req->locations) {
|
||||
if (np_req->locations)
|
||||
{
|
||||
json_object_object_add(j_np_req, "locations", j_location_array);
|
||||
path = strtok_r(np_req->locations, delimiter, &save);
|
||||
while (path) {
|
||||
while (path)
|
||||
{
|
||||
json_object_array_add(j_location_array, json_object_new_string(path));
|
||||
path = strtok_r(NULL, delimiter, &save);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
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) {
|
||||
if (tmp && strlen(tmp) > 0)
|
||||
{
|
||||
nret = strlen(tmp);
|
||||
*msg = strdup(tmp);
|
||||
assert(*msg);
|
||||
@@ -249,18 +273,18 @@ 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));
|
||||
tmp = json_object_to_json_string(j_new_work_conn);
|
||||
if (tmp && strlen(tmp) > 0) {
|
||||
if (tmp && strlen(tmp) > 0)
|
||||
{
|
||||
nret = strlen(tmp);
|
||||
*msg = strdup(tmp);
|
||||
assert(*msg);
|
||||
@@ -278,7 +302,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,24 +311,43 @@ 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) {
|
||||
if (port)
|
||||
{
|
||||
port++;
|
||||
npr->remote_port = atoi(port);
|
||||
}
|
||||
|
||||
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 +363,43 @@ 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 +419,12 @@ 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 +442,35 @@ 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 +482,183 @@ 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_
|
||||
|
||||
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
|
||||
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.9.644"
|
||||
#define PROTOCOL_VERESION "0.43.0"
|
||||
#define CLIENT_V 1
|
||||
|
||||
|
||||
31
xfrpc.c
31
xfrpc.c
@@ -45,11 +45,40 @@
|
||||
#include "crypto.h"
|
||||
#include "msg.h"
|
||||
#include "utils.h"
|
||||
#include "tcp_redir.h"
|
||||
#include "config.h"
|
||||
|
||||
#include "plugins/telnetd.h"
|
||||
#include "plugins/instaloader.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, "instaloader_redir") == 0) {
|
||||
start_tcp_redir_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