40 Commits

Author SHA1 Message Date
staylightblow8
d9ff2e1a62 release version 1.11.587 2022-11-16 10:22:03 +08:00
Dengfeng Liu
3396100bc4 clean: clean unused function
Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-11-14 08:42:17 +00:00
DengfengLiu
c53693be69 refactor: refactor tcp mux
Signed-off-by: DengfengLiu <liu_df@qq.com>
2022-11-13 11:09:36 +00:00
staylightblow8
855f602e2f support openssl > 3.0 2022-10-20 10:45:37 +08:00
staylightblow8
fd9a5ae249 Update README.md 2022-10-18 11:37:02 +08:00
Dengfeng Liu
61d5243678 clean code of config
Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-07-25 14:37:16 +08:00
staylightblow8
e75a4dda25 update version 2022-07-24 21:20:01 +08:00
staylightblow8
65bfe5c03c Update README.md 2022-07-12 10:06:53 +08:00
Dengfeng Liu
b7c3e1f80b fix: check whether tcp mux header or not mor strictly
Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-07-11 16:10:08 +08:00
staylightblow8
3fb01ce735 Update version.h 2022-06-30 14:01:39 +08:00
staylightblow8
f14562b26b Delete xfrpc.conf 2022-06-30 14:00:25 +08:00
staylightblow8
d4f35909c5 Delete xfrpc.init 2022-06-30 14:00:18 +08:00
staylightblow8
1ee2b1ff56 Delete Makefile 2022-06-30 13:59:55 +08:00
staylightblow8
cab6e8a20f Update README.md 2022-06-28 17:58:25 +08:00
staylightblow8
e53f8e1c94 Update README.md 2022-06-15 09:50:10 +08:00
staylightblow8
23819fcb44 Update README.md 2022-06-14 09:51:19 +08:00
staylightblow8
51930c855f Update CONTRIBUTING.md 2022-06-13 11:30:55 +08:00
Dengfeng Liu
690a6f4feb fix: interval ping process
Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-06-08 11:09:34 +08:00
Dengfeng Liu
56e4020969 feat: heartbeat_timeout process
Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-06-07 15:18:37 +08:00
Dengfeng Liu
e06a9a40a3 fix: parse proxy response error when remote_address is domain
Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-06-07 14:20:27 +08:00
Dengfeng Liu
767d2859f9 feat: when xfrpc disconnected from frps, it should reconnect repeatly
Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-06-07 10:08:38 +08:00
Dengfeng Liu
fa0a273865 Merge branch 'master' of https://github.com/liudf0716/xfrpc 2022-06-06 15:27:56 +08:00
Dengfeng Liu
030a7b8784 fix: when reconnect frps should send proxy conf again
Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-06-06 15:26:36 +08:00
staylightblow8
d2cd54d831 Update README.md 2022-06-06 15:13:25 +08:00
Dengfeng Liu
9c90f6b5cd Merge branch 'master' of https://github.com/liudf0716/xfrpc 2022-06-05 17:39:27 +08:00
Dengfeng Liu
d1e2f549a7 fix: fix the following bug
when xfrpc connected frps, then stop frps and start frps
the bug happen

Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-06-05 17:26:20 +08:00
staylightblow8
ffa9e1ad97 Update README.md 2022-06-01 16:36:56 +08:00
Dengfeng Liu
ca64905266 fix: process unhandle tcp mux header
Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-05-31 17:21:08 +08:00
Dengfeng Liu
6b1f70407c Merge branch 'master' of https://github.com/liudf0716/xfrpc 2022-05-28 11:25:39 +08:00
Dengfeng Liu
5d298156e4 refactor: refactor code
Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-05-28 11:09:56 +08:00
Dengfeng Liu
37f124ffe7 Update README.md 2022-05-27 08:55:26 +08:00
Dengfeng Liu
675e47ef43 feat: add reconnect frps
Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-05-27 08:37:05 +08:00
Dengfeng Liu
176e4006f6 format: format code and prepare release
Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-05-26 21:11:52 +08:00
Dengfeng Liu
d91d5d91ef fix: exceed window size bug
Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-05-26 18:23:39 +08:00
Dengfeng Liu
36eba09bbe fix: replace auth_token with token
Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-05-23 18:47:20 +08:00
Dengfeng Liu
4ac16540b7 release: v1.05.552 release
support tcp mux

Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-05-23 15:24:33 +08:00
Dengfeng Liu
ab4da37e72 feat: add tcp mux support
Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-05-20 11:10:33 +08:00
Dengfeng Liu
b49dd4e97c Update README.md 2022-05-16 13:59:09 +08:00
Dengfeng Liu
6fb2e167d6 fix: clean all warning of code
Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>
2022-05-10 10:12:10 +08:00
Dengfeng Liu
4b633a7c2a Update README.md 2022-05-09 16:27:25 +08:00
39 changed files with 1611 additions and 737 deletions

View File

@@ -31,12 +31,12 @@ set(src_xfrpc
crypto.c
fastpbkdf2.c
utils.c
session.c
common.c
login.c
proxy_tcp.c
proxy_ftp.c
proxy.c
tcpmux.c
)
set(libs

View File

@@ -27,7 +27,7 @@ If you want to contribute to [xfrpc](https://github.com/liudf0716/xfrpc), please
6. Commit and push your changes, then make a pull request from Github.
git commit --signoff
git push -f
git push
7. Awaiting review, if accepted, merged!

View File

@@ -3,22 +3,24 @@
## What is xfrpc
`xfrpc` is [frp](https://github.com/fatedier/frp) client implemented by c language for [OpenWRT](https://github.com/openwrt/openwrt) and [LEDE](https://github.com/lede-project/source) system
The motivation to start xfrpc project is that we are OpenWRTer, and openwrt usually ran in device which has little ROM and RAM space, however golang always need more space and memory; therefore we start xfrpc project to support frp.
`xfrpc` is [frp](https://github.com/fatedier/frp) client implemented by c language for [OpenWRT](https://github.com/openwrt/openwrt)
The motivation to start xfrpc project is that we are OpenWRTer, and openwrt usually run in devices which have limit ROM and RAM space, however frpc always need more space and memory; therefore we launched xfrpc project.
## Development Status
xfrpc partially compitable with latest frp release feature, It target to fully compatible with latest frp release.
xfrpc partially compitable with latest frp release feature, It targets to fully compatible with latest frp release.
the following table is detail compatible feature:
| Feature | xfrpc | frpc |
| ------------- | ------------- | ---------|
| tcp | Yes | Yes |
| tcpmux | No | Yes |
| tcpmux | Yes | Yes |
| http | Yes | Yes |
| https | Yes | Yes |
| subdomain | No | Yes |
| use_encryption | No | Yes |
| use_compression | No | Yes |
| udp | No | Yes |
| p2p | No | Yes |
| xtcp | No | Yes |
@@ -28,9 +30,10 @@ the following table is detail compatible feature:
## Architecture
![Architecture](https://github.com/fatedier/frp/blob/dev/doc/pic/architecture.png?raw=true)
Architecture quote from [frp](https://github.com/fatedier/frp) project, replace frpc with xfrpc.
![Architecture](https://user-images.githubusercontent.com/1182593/196329678-1781b4e9-2355-4863-be3f-e128b31cc82c.png)
## Sequence Diagram
@@ -77,7 +80,7 @@ sequenceDiagram
```
## Compile
## Compile on Ubuntu 20.04.3 LTS
xfrp need [libevent](https://github.com/libevent/libevent) [openssl-dev](https://github.com/openssl/openssl) and [json-c](https://github.com/json-c/json-c) support
@@ -98,6 +101,14 @@ cmake ..
make
```
## Compile on OpenWrt
xfrpc was recruited by openwrt community since version 1.04.515
anyway I highly recommend you to use latest version
in order to compile xfrpc in openwrt sdk environment, you should firstly `make menuconfig`, then select `Network --> Web Servers/Proxies ---> xfrpc`
## Quick start
**before using xfrpc, you should get frps server: [frps](https://github.com/fatedier/frp/releases)**
@@ -110,11 +121,8 @@ frps use latest release 0.42.0
# frps.ini
[common]
bind_port = 7000
tcp_mux = false
```
**attention! cause xfrpc does not support tcp_mux yet, please disable tcp_mux otherwise xfrpc can not connect frps**
run frps
```
@@ -136,7 +144,17 @@ local_port = 22
remote_port = 6128
```
+ xfrpc http
+ xfrpc http&https
compare with supporting tcp, supporting http&https need to add vhost_http_port&vhost_https_port in frps.ini as the following
```
# frps.ini
[common]
bind_port = 7000
vhost_http_port = 80
vhost_https_port = 443
```
```
# xfrpc_mini.ini
@@ -144,10 +162,15 @@ remote_port = 6128
server_addr = x.x.x.x
server_port = 7000
[web]
[http]
type = http
local_port = 80
custom_domains = www.example.com
[https]
type = https
local_port = 443
custom_domains = www.example.com
```
+ Run in debug mode
@@ -162,6 +185,12 @@ xfrpc -c frpc_mini.ini -f -d 7
xfrpc -c frpc_mini.ini -d 0
```
## Openwrt luci configure ui
If running xfrpc in openwrt box, [luci-app-xfrpc](https://github.com/liudf0716/luci-app-xfrpc) is a good choice
luci-app-xfrpc was recruited by [luci project](https://github.com/openwrt/luci)
## How to contribute our project
See [CONTRIBUTING](https://github.com/liudf0716/xfrpc/blob/master/CONTRIBUTING.md) for details on submitting patches and the contribution workflow.
@@ -173,8 +202,27 @@ QQ群 [331230369](https://jq.qq.com/?_wv=1027&k=47QGEhL)
## Please support us and star our project
[![Star History Chart](https://api.star-history.com/svg?repos=liudf0716/xfrpc&type=Date)](https://star-history.com/#liudf0716/xfrpc&Date)
## 打赏
支付宝打赏
![支付宝打赏](https://user-images.githubusercontent.com/1182593/169465135-d4522479-4068-4714-ab58-987d7d7eb338.png)
微信打赏
![微信打赏](https://user-images.githubusercontent.com/1182593/169465249-db1b495e-078e-4cab-91fc-96dab3320b06.png)
<!--
## 广告
想学习OpenWrt开发但是摸不着门道自学没毅力基础太差怕太难学不会跟着佐大学OpenWrt开发入门培训班助你能学有所成
报名地址https://forgotfun.org/2018/04/openwrt-training-2018.html
-->

25
agent.c
View File

@@ -1,25 +0,0 @@
/* 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 agent.c
@brief agent for router to communicate with frp server
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/

View File

@@ -29,20 +29,12 @@
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <syslog.h>
#include <zlib.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>
#include "debug.h"
#include "client.h"
@@ -55,15 +47,16 @@
#include "common.h"
#include "proxy.h"
#include "utils.h"
#include "tcpmux.h"
static struct proxy_client *all_pc = NULL;
static void
xfrp_worker_event_cb(struct bufferevent *bev, short what, void *ctx)
{
struct proxy_client *client = (struct proxy_client *)ctx;
if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
debug(LOG_DEBUG, "working connection closed!");
bufferevent_free(bev);
free_proxy_client(client);
}
}
@@ -74,9 +67,13 @@ xfrp_proxy_event_cb(struct bufferevent *bev, short what, void *ctx)
assert(client);
if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
debug(LOG_DEBUG, "xfrpc proxy close connect server [%s:%d] : %s", client->ps->local_ip, client->ps->local_port, strerror(errno));
bufferevent_free(bev);
} else if (what & BEV_EVENT_CONNECTED) {
debug(LOG_DEBUG, "xfrpc proxy close connect server [%s:%d] stream_id %d: %s",
client->ps->local_ip, client->ps->local_port,
client->stream_id, strerror(errno));
tmux_stream_close(client->ctl_bev, &client->stream);
} else if (what & BEV_EVENT_CONNECTED) {
debug(LOG_DEBUG, "client [%d] connected", client->stream_id);
//client->stream.state = ESTABLISHED;
if (client->data_tail_size > 0) {
debug(LOG_DEBUG, "send client data ...");
send_client_data_tail(client);
@@ -127,7 +124,7 @@ start_xfrp_tunnel(struct proxy_client *client)
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);
bufferevent_free(client->ctl_bev);
del_proxy_client(client);
return;
}
@@ -137,30 +134,30 @@ start_xfrp_tunnel(struct proxy_client *client)
ps->local_ip ? ps->local_ip:"::1",
ps->local_port);
bufferevent_data_cb proxy_s2c_cb, proxy_c2s_cb;
bufferevent_data_cb proxy_s2c_recv, proxy_c2s_recv;
if (is_ftp_proxy(client->ps)) {
proxy_c2s_cb = ftp_proxy_c2s_cb;
proxy_s2c_cb = ftp_proxy_s2c_cb;
//ctl_prox->remote_data_port = client->ps->remote_data_port;
//ctl_prox->proxy_name = strdup(ps->proxy_name);
proxy_c2s_recv = ftp_proxy_c2s_cb;
proxy_s2c_recv = ftp_proxy_s2c_cb;
} else {
proxy_c2s_cb = tcp_proxy_c2s_cb; // local service <---> xfrpc
proxy_s2c_cb = tcp_proxy_s2c_cb; // frps <---> xfrpc
proxy_c2s_recv = tcp_proxy_c2s_cb; // local service ---> xfrpc
proxy_s2c_recv = tcp_proxy_s2c_cb; // frps ---> xfrpc
}
bufferevent_setcb(client->ctl_bev,
proxy_s2c_cb,
if (!c_conf->tcp_mux) {
bufferevent_setcb(client->ctl_bev,
proxy_s2c_recv,
NULL,
xfrp_worker_event_cb,
client);
bufferevent_enable(client->ctl_bev, EV_READ|EV_WRITE);
}
bufferevent_setcb(client->local_proxy_bev,
proxy_c2s_cb,
proxy_c2s_recv,
NULL,
xfrp_proxy_event_cb,
client);
bufferevent_enable(client->ctl_bev, EV_READ|EV_WRITE);
bufferevent_enable(client->local_proxy_bev, EV_READ|EV_WRITE);
}
@@ -177,19 +174,19 @@ send_client_data_tail(struct proxy_client *client)
return send_l;
}
void
static void
free_proxy_client(struct proxy_client *client)
{
if (client->ev_timeout) evtimer_del(client->ev_timeout);
debug(LOG_DEBUG, "free client %d", client->stream_id);
if (client->local_proxy_bev) bufferevent_free(client->local_proxy_bev);
free(client);
}
void
del_proxy_client(struct proxy_client *client)
{
struct proxy_client *all_pc = get_all_pc();
if (!client || !all_pc ) {
debug(LOG_INFO, "Error: all_pc or client is NULL");
debug(LOG_INFO, "all_pc or client is NULL");
return;
}
@@ -198,14 +195,21 @@ del_proxy_client(struct proxy_client *client)
free_proxy_client(client);
}
// Return NULL if proxy service not found with proxy_name
struct proxy_service *
get_proxy_service(const char *proxy_name)
void
del_proxy_client_by_stream_id(uint32_t sid)
{
struct proxy_service *ps = NULL;
struct proxy_service *all_ps = get_all_proxy_services();
HASH_FIND_STR(all_ps, proxy_name, ps);
return ps;
del_stream(sid);
struct proxy_client *pc = get_proxy_client(sid);
del_proxy_client(pc);
}
struct proxy_client *
get_proxy_client(uint32_t sid)
{
struct proxy_client *pc = NULL;
HASH_FIND_INT(all_pc, &sid, pc);
return pc;
}
struct proxy_client *
@@ -213,5 +217,21 @@ new_proxy_client()
{
struct proxy_client *client = calloc(1, sizeof(struct proxy_client));
assert(client);
client->stream_id = get_next_session_id();
init_tmux_stream(&client->stream, client->stream_id, INIT);
HASH_ADD_INT(all_pc, stream_id, client);
return client;
}
void
clear_all_proxy_client()
{
if (!all_pc) return;
struct proxy_client *client, *tmp;
HASH_ITER(hh, all_pc, client, tmp) {
HASH_DEL(all_pc, client);
free_proxy_client(client);
}
}

View File

@@ -20,7 +20,7 @@
\********************************************************************/
/** @file client.h
@brief xfrp client proxy client related
@brief xfrpc proxy client related
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
@@ -31,6 +31,7 @@
#include "uthash.h"
#include "common.h"
#include "tcpmux.h"
struct event_base;
struct base_conf;
@@ -42,17 +43,18 @@ struct proxy_client {
struct event_base *base;
struct bufferevent *ctl_bev; // xfrpc proxy <---> frps
struct bufferevent *local_proxy_bev; // xfrpc proxy <---> local service
struct event *ev_timeout;
struct base_conf *bconf;
struct tmux_stream stream;
//private arguments
UT_hash_handle hh;
uint32_t stream_id;
int connected;
int work_started;
struct proxy_service *ps;
unsigned char *data_tail; // storage untreated data
size_t data_tail_size;
// private arguments
UT_hash_handle hh;
};
struct proxy_service {
@@ -75,7 +77,7 @@ struct proxy_service {
char *http_user;
char *http_pwd;
//provate arguments
// private arguments
UT_hash_handle hh;
};
@@ -88,13 +90,16 @@ void start_xfrp_tunnel(struct proxy_client *client);
void del_proxy_client(struct proxy_client *client);
void free_proxy_client(struct proxy_client *client);
void del_proxy_client_by_stream_id(uint32_t sid);
struct proxy_service *get_proxy_service(const char *proxy_name);
struct proxy_client *get_proxy_client(uint32_t sid);
int send_client_data_tail(struct proxy_client *client);
int is_ftp_proxy(const struct proxy_service *ps);
struct proxy_client *new_proxy_client();
void clear_all_proxy_client();
#endif //_CLIENT_H_

View File

@@ -282,11 +282,11 @@ function(from_hex HEX DEC)
set(${DEC} ${_res} PARENT_SCOPE)
endfunction()
if (OPENSSL_INCLUDE_DIR)
if(OPENSSL_INCLUDE_DIR AND EXISTS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h")
file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" openssl_version_str
REGEX "^# *define[\t ]+OPENSSL_VERSION_NUMBER[\t ]+0x([0-9a-fA-F])+.*")
if(OPENSSL_INCLUDE_DIR AND EXISTS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h")
file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" openssl_version_str
REGEX "^#[\t ]*define[\t ]+OPENSSL_VERSION_NUMBER[\t ]+0x([0-9a-fA-F])+.*")
if(openssl_version_str)
# The version number is encoded as 0xMNNFFPPS: major minor fix patch status
# The status gives if this is a developer or prerelease and is ignored here.
# Major, minor, and fix directly translate into the version numbers shown in
@@ -315,6 +315,25 @@ if (OPENSSL_INCLUDE_DIR)
endif ()
set(OPENSSL_VERSION "${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_MINOR}.${OPENSSL_VERSION_FIX}${OPENSSL_VERSION_PATCH_STRING}")
else ()
# Since OpenSSL 3.0.0, the new version format is MAJOR.MINOR.PATCH and
# a new OPENSSL_VERSION_STR macro contains exactly that
file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" OPENSSL_VERSION_STR
REGEX "^#[\t ]*define[\t ]+OPENSSL_VERSION_STR[\t ]+\"([0-9])+\\.([0-9])+\\.([0-9])+\".*")
string(REGEX REPLACE "^.*OPENSSL_VERSION_STR[\t ]+\"([0-9]+\\.[0-9]+\\.[0-9]+)\".*$"
"\\1" OPENSSL_VERSION_STR "${OPENSSL_VERSION_STR}")
set(OPENSSL_VERSION "${OPENSSL_VERSION_STR}")
# Setting OPENSSL_VERSION_MAJOR OPENSSL_VERSION_MINOR and OPENSSL_VERSION_FIX
string(REGEX MATCHALL "([0-9])+" OPENSSL_VERSION_NUMBER "${OPENSSL_VERSION}")
list(POP_FRONT OPENSSL_VERSION_NUMBER
OPENSSL_VERSION_MAJOR
OPENSSL_VERSION_MINOR
OPENSSL_VERSION_FIX)
unset(OPENSSL_VERSION_NUMBER)
unset(OPENSSL_VERSION_STR)
endif ()
endif ()

View File

@@ -110,7 +110,7 @@ get_daemon_status()
/** @internal
* @brief Print usage
*
* Prints usage, called when wifidog is run with -h or with an unknown option
* Prints usage, called when xfrpc is run with -h or with an unknown option
*/
static void
usage(const char *appname)

View File

@@ -1,3 +1,29 @@
/* 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 common.c
@brief xfrp common function implemented
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
#include "uthash.h"
#include "common.h"

View File

@@ -1,3 +1,29 @@
/* 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 common.h
@brief xfrp common header
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
#ifndef _COMMON_H_
#define _COMMON_H_
@@ -6,6 +32,16 @@
#include <string.h>
#include <netinet/in.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>
#include <event2/dns.h>
#include <event2/event_struct.h>
#include <assert.h>
#include "uthash.h"
#define BIGENDIAN_64BIT 1

128
config.c
View File

@@ -20,7 +20,7 @@
\********************************************************************/
/** @file config.c
@brief xfrp client config related
@brief xfrpc client config related
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
#include <stdio.h>
@@ -42,8 +42,7 @@
#include "version.h"
static struct common_conf *c_conf;
static struct proxy_client *p_clients;
static struct proxy_service *p_services;
static struct proxy_service *all_ps;
static void new_ftp_data_proxy_service(struct proxy_service *ftp_ps);
@@ -57,43 +56,9 @@ void free_common_config()
struct common_conf *c_conf = get_common_config();
if (c_conf->server_addr) free(c_conf->server_addr);
if (c_conf->http_proxy) free(c_conf->http_proxy);
if (c_conf->log_file) free(c_conf->log_file);
if (c_conf->log_way) free(c_conf->log_way);
if (c_conf->log_level) free(c_conf->log_level);
if (c_conf->auth_token) free(c_conf->auth_token);
if (c_conf->privilege_token) free(c_conf->privilege_token);
SAFE_FREE(c_conf->server_ip);
};
void set_common_server_ip(const char *ip)
{
struct common_conf *c_conf = get_common_config();
c_conf->server_ip = strdup(ip);
assert(c_conf->server_ip);
debug(LOG_DEBUG, "server IP address: [%s]", c_conf->server_ip);
}
void free_base_config(struct base_conf *bconf)
{
if (bconf->name) free(bconf->name);
if (bconf->auth_token) free(bconf->auth_token);
if (bconf->privilege_token) free(bconf->privilege_token);
if (bconf->host_header_rewrite) free(bconf->host_header_rewrite);
if (bconf->subdomain) free(bconf->subdomain);
}
struct proxy_client *get_all_pc()
{
return p_clients;
}
struct proxy_service *get_all_proxy_services()
{
return p_services;
}
static int is_true(const char *val)
{
if (val && (strcmp(val, "true") == 0 || strcmp(val, "1") == 0))
@@ -110,9 +75,7 @@ static const char *get_valid_type(const char *val)
#define MATCH_VALUE(s) strcmp(val, s) == 0
if (MATCH_VALUE("tcp") ||
MATCH_VALUE("http") ||
MATCH_VALUE("https") ||
MATCH_VALUE("udp") ||
MATCH_VALUE("ftp")) {
MATCH_VALUE("https")) {
return val;
}
@@ -127,8 +90,9 @@ static void dump_common_conf()
return;
}
debug(LOG_DEBUG, "Section[common]: {server_addr:%s, server_port:%d, auth_token:%s, privilege_token:%s, interval:%d, timeout:%d}",
c_conf->server_addr, c_conf->server_port, c_conf->auth_token, c_conf->privilege_token, c_conf->heartbeat_interval, c_conf->heartbeat_timeout);
debug(LOG_DEBUG, "Section[common]: {server_addr:%s, server_port:%d, auth_token:%s, interval:%d, timeout:%d}",
c_conf->server_addr, c_conf->server_port, c_conf->auth_token,
c_conf->heartbeat_interval, c_conf->heartbeat_timeout);
}
static void dump_proxy_service(const int index, struct proxy_service *ps)
@@ -161,7 +125,7 @@ static void dump_all_ps()
struct proxy_service *ps = NULL, *tmp = NULL;
int index = 0;
HASH_ITER(hh, p_services, ps, tmp) {
HASH_ITER(hh, all_ps, ps, tmp) {
dump_proxy_service(index++, ps);
}
}
@@ -203,7 +167,7 @@ static void new_ftp_data_proxy_service(struct proxy_service *ftp_ps)
struct proxy_service *ps = NULL;
char *ftp_data_proxy_name = get_ftp_data_proxy_name((const char *)ftp_ps->proxy_name);
HASH_FIND_STR(p_services, ftp_data_proxy_name, ps);
HASH_FIND_STR(all_ps, ftp_data_proxy_name, ps);
if (!ps) {
ps = new_proxy_service(ftp_data_proxy_name);
if (! ps) {
@@ -220,7 +184,7 @@ static void new_ftp_data_proxy_service(struct proxy_service *ftp_ps)
ps->local_ip = ftp_ps->local_ip;
ps->local_port = 0; //will be init in working tunnel connectting
HASH_ADD_KEYPTR(hh, p_services, ps->proxy_name, strlen(ps->proxy_name), ps);
HASH_ADD_KEYPTR(hh, all_ps, ps->proxy_name, strlen(ps->proxy_name), ps);
}
free(ftp_data_proxy_name);
@@ -240,7 +204,7 @@ proxy_service_handler(void *user, const char *sect, const char *nm, const char *
return 0;
}
HASH_FIND_STR(p_services, section, ps);
HASH_FIND_STR(all_ps, section, ps);
if (!ps) {
ps = new_proxy_service(section);
if (! ps) {
@@ -248,7 +212,7 @@ proxy_service_handler(void *user, const char *sect, const char *nm, const char *
exit(0);
}
HASH_ADD_KEYPTR(hh, p_services, ps->proxy_name, strlen(ps->proxy_name), ps);
HASH_ADD_KEYPTR(hh, all_ps, ps->proxy_name, strlen(ps->proxy_name), ps);
}
#define MATCH_NAME(s) strcmp(nm, s) == 0
@@ -275,22 +239,17 @@ proxy_service_handler(void *user, const char *sect, const char *nm, const char *
ps->remote_data_port = atoi(value);
} else if (MATCH_NAME("http_user")) {
ps->http_user = strdup(value);
assert(ps->http_user);
} else if (MATCH_NAME("http_pwd")) {
ps->http_pwd = strdup(value);
assert(ps->http_pwd);
} else if (MATCH_NAME("subdomain")) {
ps->subdomain = strdup(value);
assert(ps->http_pwd);
} else if (MATCH_NAME("custom_domains")) {
ps->custom_domains = strdup(value);
assert(ps->custom_domains);
} else if (MATCH_NAME("locations")) {
ps->locations = strdup(value);
assert(ps->locations);
} else if (MATCH_NAME("host_header_rewrite")) {
ps->host_header_rewrite = strdup(value);
assert(ps->host_header_rewrite);
} else if (MATCH_NAME("use_encryption")) {
ps->use_encryption = TO_BOOL(value);
} else if (MATCH_NAME("use_compression")) {
@@ -308,39 +267,10 @@ static int common_handler(void *user, const char *section, const char *name, con
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
if (MATCH("common", "server_addr")) {
SAFE_FREE(config->server_addr);
int addr_len = strlen(value) + 1;
config->server_addr = (char *)calloc(1, addr_len);
config->server_addr = strdup(value);
assert(config->server_addr);
if(dns_unified(value, config->server_addr, addr_len)) {
debug(LOG_ERR, "error: server_addr [%s] is invalid!", value);
exit(0);
}
if (is_valid_ip_address(value))
set_common_server_ip(value);
} else if (MATCH("common", "server_port")) {
config->server_port = atoi(value);
} else if (MATCH("common", "http_proxy")) {
SAFE_FREE(config->http_proxy);
config->http_proxy = strdup(value);
assert(config->http_proxy);
} else if (MATCH("common", "log_file")) {
SAFE_FREE(config->log_file);
config->log_file = strdup(value);
assert(config->log_file);
} else if (MATCH("common", "log_way")) {
SAFE_FREE(config->log_way);
config->log_way = strdup(value);
assert(config->log_way);
} else if (MATCH("common", "log_level")) {
SAFE_FREE(config->log_level);
config->log_level = strdup(value);
assert(config->log_level);
} else if (MATCH("common", "log_max_days")) {
config->log_max_days = atoi(value);
} else if (MATCH("common", "privilege_token")) {
SAFE_FREE(config->privilege_token);
config->privilege_token = strdup(value);
assert(config->privilege_token);
} else if (MATCH("common", "heartbeat_interval")) {
config->heartbeat_interval = atoi(value);
} else if (MATCH("common", "heartbeat_timeout")) {
@@ -349,12 +279,9 @@ static int common_handler(void *user, const char *section, const char *name, con
SAFE_FREE(config->auth_token);
config->auth_token = strdup(value);
assert(config->auth_token);
} else if (MATCH("common", "user")) {
SAFE_FREE(config->user);
config->user = strdup(value);
assert(config->user);
} else if (MATCH("common", "tcp_mux")) {
config->tcp_mux = 0; // set tcp_mux to default: false
config->tcp_mux = atoi(value);
config->tcp_mux = !!config->tcp_mux;
}
return 1;
}
@@ -367,18 +294,9 @@ static void init_common_conf(struct common_conf *config)
config->server_addr = strdup("0.0.0.0");
assert(config->server_addr);
config->server_port = 7000;
config->log_file = strdup("console");
assert(config->log_file);
config->log_way = strdup("console");
assert(config->log_way);
config->log_level = strdup("info");
assert(config->log_level);
config->log_max_days = 3;
config->heartbeat_interval = 30;
config->heartbeat_timeout = 60;
config->tcp_mux = 0;
config->user = NULL;
config->server_ip = NULL;
config->heartbeat_timeout = 90;
config->tcp_mux = 1;
config->is_router = 0;
}
@@ -435,3 +353,17 @@ int is_running_in_router()
{
return c_conf->is_router;
}
struct proxy_service *
get_proxy_service(const char *proxy_name)
{
struct proxy_service *ps = NULL;
HASH_FIND_STR(all_ps, proxy_name, ps);
return ps;
}
struct proxy_service *
get_all_proxy_services()
{
return all_ps;
}

View File

@@ -20,7 +20,7 @@
\********************************************************************/
/** @file config.h
@brief xfrp client config related
@brief xfrpc client config related
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
#ifndef _CONFIG_H_
@@ -31,51 +31,31 @@
#define FTP_RMT_CTL_PROXY_SUFFIX "_ftp_remote_ctl_proxy"
struct base_conf{
char *name;
char *auth_token;
int use_encryption;
int use_gzip;
int privilege_mode;
char *privilege_token;
int pool_count;
char *host_header_rewrite;
char *subdomain;
};
// common config
//client common config
struct common_conf {
char *server_addr; /* default 0.0.0.0 */
char *server_ip;
int server_port; /* default 7000 */
char *http_proxy;
char *log_file; /* default consol */
char *log_way; /* default console */
char *log_level; /* default info */
int log_max_days; /* default 3 */
char *privilege_token;
char *auth_token;
int heartbeat_interval; /* default 10 */
int heartbeat_timeout; /* default 30 */
int tcp_mux; /* default 0 */
char *user;
/* private fields */
int is_router; // to sign router (Openwrt/LEDE) or not
};
struct common_conf *get_common_config();
struct proxy_service *get_all_proxy_services();
void free_common_config();
void free_base_config(struct base_conf *bconf);
struct proxy_client *get_all_pc();
void load_config(const char *confile);
char *get_ftp_data_proxy_name(const char *ftp_proxy_name);
void set_common_server_ip(const char *ip);
int is_running_in_router();
struct proxy_service *get_proxy_service(const char *proxy_name);
struct proxy_service *get_all_proxy_services();
#endif //_CONFIG_H_

12
const.h
View File

@@ -20,7 +20,7 @@
\********************************************************************/
/** @file const.h
@brief xfrp constant parameter define
@brief xfrpc constant parameter define
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
@@ -34,15 +34,5 @@ enum server_status {
Closed
};
// msg type
// enum msg_type {
// NewCtlConn = 0,
// NewWorkConn,
// NoticeUserConn,
// NewCtlConnRes,
// HeartbeatReq,
// HeartbeatRes,
// NewWorkConnUdp
// };
#endif

455
control.c
View File

@@ -34,14 +34,8 @@
#include <netinet/in.h>
#include <json-c/json.h>
#include <syslog.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>
#include <event2/dns.h>
#include <event2/event_struct.h>
#include <unistd.h>
#include <time.h>
#include "debug.h"
#include "client.h"
@@ -53,39 +47,43 @@
#include "uthash.h"
#include "crypto.h"
#include "utils.h"
#include "session.h"
#include "common.h"
#include "login.h"
#include "tcpmux.h"
static struct control *main_ctl;
static int clients_conn_signel = 0;
static int client_connected = 0;
static int is_login = 0;
static time_t pong_time = 0;
static void sync_new_work_connection(struct bufferevent *bev);
static void new_work_connection(struct bufferevent *bev, struct tmux_stream *stream);
static void recv_cb(struct bufferevent *bev, void *ctx);
static void clear_main_control();
static void start_base_connect();
static void keep_control_alive();
static int
is_client_connected()
{
return clients_conn_signel;
return client_connected;
}
static int
client_connected(int is_connected)
set_client_status(int is_connected)
{
if (is_connected)
clients_conn_signel = 1;
client_connected = 1;
else
clients_conn_signel = 0;
client_connected = 0;
return clients_conn_signel;
return client_connected;
}
static int
set_client_work_start(struct proxy_client *client, int is_start_work)
{
assert(client->ps);
if (is_start_work) {
assert(client->ps);
client->work_started = 1;
}else
client->work_started = 0;
@@ -108,12 +106,12 @@ client_start_event_cb(struct bufferevent *bev, short what, void *ctx)
}
debug(LOG_ERR, "Proxy connect server [%s:%d] error: %s", c_conf->server_addr, c_conf->server_port, strerror(errno));
bufferevent_free(bev);
free_proxy_client(client);
del_proxy_client(client);
} else if (what & BEV_EVENT_CONNECTED) {
bufferevent_setcb(bev, recv_cb, NULL, client_start_event_cb, client);
bufferevent_enable(bev, EV_READ|EV_WRITE);
sync_new_work_connection(bev);
client_connected(1);
new_work_connection(bev, &main_ctl->stream);
set_client_status(1);
debug(LOG_INFO, "proxy service start");
}
}
@@ -125,6 +123,15 @@ new_client_connect()
struct common_conf *c_conf = get_common_config();
assert(c_conf);
client->base = main_ctl->connect_base;
if (c_conf->tcp_mux) {
debug(LOG_DEBUG, "new client through tcp mux: %d", client->stream_id);
client->ctl_bev = main_ctl->connect_bev;
send_window_update(client->ctl_bev, &client->stream, 0);
new_work_connection(client->ctl_bev, &client->stream);
return;
}
struct bufferevent *bev = connect_server(client->base, c_conf->server_addr, c_conf->server_port);
if (!bev) {
debug(LOG_DEBUG, "Connect server [%s:%d] failed", c_conf->server_addr, c_conf->server_port);
@@ -142,10 +149,13 @@ static void
start_proxy_services()
{
struct proxy_service *all_ps = get_all_proxy_services();
assert(all_ps);
struct proxy_service *ps = NULL, *tmp = NULL;
if (!all_ps) {
debug(LOG_INFO, "no proxy service configure by user");
return;
}
debug(LOG_INFO, "Start xfrp proxy services ...");
HASH_ITER(hh, all_ps, ps, tmp) {
@@ -158,30 +168,23 @@ start_proxy_services()
}
static void
ping(struct bufferevent *bev)
ping()
{
struct bufferevent *bout = NULL;
if (bev) {
bout = bev;
} else {
bout = main_ctl->connect_bev;
}
struct bufferevent *bout = main_ctl->connect_bev;
if ( ! bout) {
debug(LOG_ERR, "bufferevent is not legal!");
return;
}
uint32_t sid = get_main_control()->session_id;
char *ping_msg = "{}";
send_enc_msg_frp_server(bev, TypePing, ping_msg, strlen(ping_msg), sid);
send_enc_msg_frp_server(bout, TypePing, ping_msg, strlen(ping_msg), &main_ctl->stream);
}
static void
sync_new_work_connection(struct bufferevent *bev)
new_work_connection(struct bufferevent *bev, struct tmux_stream *stream)
{
struct bufferevent *bout = bev;
assert(bout);
assert(bev);
/* send new work session regist request to frps*/
struct work_conn *work_c = new_work_conn();
@@ -198,7 +201,7 @@ sync_new_work_connection(struct bufferevent *bev)
return;
}
send_msg_frp_server(bev, TypeNewWorkConn, new_work_conn_request_message, nret, 0);
send_msg_frp_server(bev, TypeNewWorkConn, new_work_conn_request_message, nret, stream);
SAFE_FREE(new_work_conn_request_message);
SAFE_FREE(work_c);
@@ -232,13 +235,22 @@ set_ticker_ping_timer(struct event *timeout)
static void
hb_sender_cb(evutil_socket_t fd, short event, void *arg)
{
debug(LOG_DEBUG, "hb_sender_cb");
if (is_client_connected()) {
debug(LOG_DEBUG, "ping frps");
//debug(LOG_DEBUG, "ping frps");
ping(NULL);
}
set_ticker_ping_timer(main_ctl->ticker_ping);
struct common_conf *c_conf = get_common_config();
time_t current_time = time(NULL);
int interval = current_time - pong_time;
if (pong_time && interval > c_conf->heartbeat_timeout) {
debug(LOG_INFO, " interval [%d] greater than heartbeat_timeout [%d]", interval, c_conf->heartbeat_timeout);
clear_main_control();
run_control();
return;
}
}
// return: 0: raw succeed 1: raw failed
@@ -286,34 +298,19 @@ proxy_service_resp_raw(struct new_proxy_response *npr)
return 0;
}
static void
dump_all_unknown_encypt(uint8_t *enc_msg, int ilen)
{
uint8_t *iv = get_main_decoder()->iv;
uint8_t *key = get_main_decoder()->key;
FILE *fout = fopen("unkown.encrypt", "w");
assert(fout);
fwrite(iv, 16, 1, fout);
fwrite(key, 16, 1, fout);
fwrite((uint8_t *)&ilen, sizeof(int), 1, fout);
fwrite(enc_msg, ilen, 1, fout);
fclose(fout);
}
static int
handle_enc_msg(uint8_t *enc_msg, int ilen, uint8_t **out)
handle_enc_msg(const uint8_t *enc_msg, int ilen, uint8_t **out)
{
if (ilen <= 0) {
debug(LOG_INFO, "enc_msg length should not be %d", ilen);
return -1;
}
uint8_t *buf = enc_msg;
const uint8_t *buf = enc_msg;
if ( !is_decoder_inited() && get_block_size() <= ilen) {
init_main_decoder(buf);
buf += get_block_size();
ilen -= get_block_size();
debug(LOG_DEBUG, "first recv stream message, init decoder iv succeed! %d", ilen);
if (!ilen) {
// recv only iv
debug(LOG_DEBUG, "recv eas1238 iv data");
@@ -324,34 +321,24 @@ handle_enc_msg(uint8_t *enc_msg, int ilen, uint8_t **out)
uint8_t *dec_msg = NULL;
size_t len = decrypt_data(buf, ilen, get_main_decoder(), &dec_msg);
*out = dec_msg;
debug(LOG_DEBUG, "dec out len %d ", len);
return len;
}
static void
print_enc_msg(uint8_t *enc_msg, int len)
{
printf("enc_msg is [%d]:\n", len);
for (int i = 0; i < len; i++) {
if (i%16 == 0)
printf("\n");
printf("%1x ", enc_msg[i]);
}
printf("\n");
}
static void
handle_control_work(const uint8_t *buf, int len, void *ctx)
{
uint8_t *frps_cmd = NULL;
uint8_t cmd_type;
uint8_t *enc_msg = buf;
const uint8_t *enc_msg = buf;
if (!ctx)
if (!ctx) {
//debug(LOG_DEBUG, "main control message");
handle_enc_msg(enc_msg, len, &frps_cmd);
else
frps_cmd = buf;
} else {
//debug(LOG_DEBUG, "worker message");
frps_cmd = (uint8_t *)buf;
}
if (!frps_cmd)
return; // only recv iv
@@ -359,19 +346,19 @@ handle_control_work(const uint8_t *buf, int len, void *ctx)
struct msg_hdr *msg = (struct msg_hdr *)frps_cmd;
cmd_type = msg->type;
debug(LOG_DEBUG, "cmd_type is %d data is %s", cmd_type, msg->data);
switch(cmd_type) {
case TypeReqWorkConn:
debug(LOG_DEBUG, "TypeReqWorkConn cmd");
{
if (! is_client_connected()) {
start_proxy_services();
client_connected(1);
set_client_status(1);
}
new_client_connect();
break;
}
case TypeNewProxyResp:
debug(LOG_DEBUG, "TypeNewProxyResp cmd");
struct new_proxy_response *npr = new_proxy_resp_unmarshal(msg->data);
{
struct new_proxy_response *npr = new_proxy_resp_unmarshal((const char *)msg->data);
if (npr == NULL) {
debug(LOG_ERR, "new proxy response buffer unmarshal faild!");
return;
@@ -380,9 +367,10 @@ handle_control_work(const uint8_t *buf, int len, void *ctx)
proxy_service_resp_raw(npr);
SAFE_FREE(npr);
break;
}
case TypeStartWorkConn:
debug(LOG_DEBUG, "TypeStartWorkConn cmd");
struct start_work_conn_resp *sr = start_work_conn_resp_unmarshal(msg->data);
{
struct start_work_conn_resp *sr = start_work_conn_resp_unmarshal((const char *)msg->data);
if (! sr) {
debug(LOG_ERR,
"TypeStartWorkConn unmarshal failed, it should never be happend!");
@@ -416,12 +404,12 @@ handle_control_work(const uint8_t *buf, int len, void *ctx)
set_client_work_start(client, 1);
break;
}
case TypePong:
pong_time = time(NULL);
break;
default:
debug(LOG_INFO, "command type dont support: ctx is %d", ctx?1:0);
print_enc_msg(enc_msg, len);
dump_all_unknown_encypt(enc_msg, len);
}
if (!ctx)
@@ -437,7 +425,7 @@ handle_login_response(const uint8_t *buf, int len)
return 0;
}
struct login_resp *lres = login_resp_unmarshal(mhdr->data);
struct login_resp *lres = login_resp_unmarshal((const char *)mhdr->data);
if (!lres) {
return 0;
}
@@ -452,18 +440,22 @@ handle_login_response(const uint8_t *buf, int len)
is_login = 1;
int login_len = msg_hton(mhdr->length);
debug(LOG_ERR, "login success! %d len %d", login_len, len);
if (len-login_len-sizeof(struct msg_hdr) == 0)
int ilen = len - login_len - sizeof(struct msg_hdr);
debug(LOG_ERR, "login success! login_len %d len %d ilen %d", login_len, len, ilen);
assert(ilen >= 0);
if (ilen <= 0)
return 1;
// in case, system get 3 packet together
uint8_t *enc_msg = mhdr->data+login_len;
// in case, libevent reveive continue packet together
struct common_conf *c_conf = get_common_config();
assert(c_conf->tcp_mux == 0);
uint8_t *enc_msg = mhdr->data + login_len;
uint8_t *frps_cmd = NULL;
int nret = handle_enc_msg(enc_msg, len-login_len-sizeof(struct msg_hdr), &frps_cmd);
int nret = handle_enc_msg(enc_msg, ilen, &frps_cmd);
assert(nret > 0);
// start proxy services must first send
start_proxy_services();
client_connected(1);
set_client_status(1);
debug(LOG_DEBUG, "TypeReqWorkConn cmd, msg :%s", &frps_cmd[8]);
assert (frps_cmd[0] == TypeReqWorkConn);
new_client_connect();
@@ -472,22 +464,18 @@ handle_login_response(const uint8_t *buf, int len)
}
static void
handle_frps_msg(unsigned char *buf, int len, void *ctx)
handle_frps_msg(uint8_t *buf, int len, void *ctx)
{
if (!is_login) {
// login response
handle_login_response(buf, len);
}else if (!ctx) {
// control msg
debug(LOG_DEBUG, "main control message");
handle_control_work(buf, len, NULL);
}else {
// client msg
debug(LOG_DEBUG, "client message");
handle_control_work(buf, len, ctx);
}
}
static struct tmux_stream abandon_stream;
// ctx: if recv_cb was called by common control, ctx == NULL
// else ctx == client struct
static void
@@ -496,17 +484,109 @@ recv_cb(struct bufferevent *bev, void *ctx)
struct evbuffer *input = bufferevent_get_input(bev);
int len = evbuffer_get_length(input);
if (len <= 0) {
return;
return;
}
unsigned char *buf = calloc(len+1, 1);
assert(buf);
evbuffer_remove(input, buf, len);
debug(LOG_DEBUG, "recv msg from frps %d ", len);
handle_frps_msg(buf, len, ctx);
struct common_conf *c_conf = get_common_config();
if (c_conf->tcp_mux) {
static struct tcp_mux_header tmux_hdr;
static uint32_t stream_len = 0;
while (len > 0) {
struct tmux_stream *cur = get_cur_stream();
size_t nr = 0;
if (!cur) {
memset(&tmux_hdr, 0, sizeof(tmux_hdr));
uint8_t *data = (uint8_t *)&tmux_hdr;
if (len < sizeof(tmux_hdr)) {
debug(LOG_INFO, "len [%d] < sizeof tmux_hdr", len);
break;
}
nr = bufferevent_read(bev, data, sizeof(tmux_hdr));
assert(nr == sizeof(tmux_hdr));
assert(validate_tcp_mux_protocol(&tmux_hdr) > 0);
len -= nr;
if (tmux_hdr.type == DATA) {
uint32_t stream_id = ntohl(tmux_hdr.stream_id);
stream_len = ntohl(tmux_hdr.length);
cur = get_stream_by_id(stream_id);
if (!cur) {
debug(LOG_INFO, "cur is NULL stream_id is %d, stream_len is %d len is %d",
stream_id, stream_len, len);
if (stream_len > 0)
cur = &abandon_stream;
else
continue;
}
if (len == 0) {
set_cur_stream(cur);
break;
}
if (len >= stream_len) {
nr = tmux_stream_read(bev, cur, stream_len);
assert(nr == stream_len);
len -= stream_len;
} else {
nr = tmux_stream_read(bev, cur, len);
stream_len -= len;
assert(nr == len);
set_cur_stream(cur);
len -= nr;
break;
}
}
} else {
assert(tmux_hdr.type == DATA);
if (len >= stream_len ) {
nr = tmux_stream_read(bev, cur, stream_len);
assert(nr == stream_len);
len -= stream_len;
} else {
nr = tmux_stream_read(bev, cur, len);
stream_len -= len;
assert(nr == len);
len -= nr;
break;
}
}
if (cur == &abandon_stream) {
debug(LOG_INFO, "abandon stream data ...");
memset(cur , 0, sizeof(abandon_stream));
set_cur_stream(NULL);
continue;
}
switch(tmux_hdr.type) {
case DATA:
case WINDOW_UPDATE:
{
handle_tcp_mux_stream(&tmux_hdr, handle_frps_msg);
break;
}
case PING:
handle_tcp_mux_ping(&tmux_hdr);
break;
case GO_AWAY:
handle_tcp_mux_go_away(&tmux_hdr);
break;
default:
debug(LOG_ERR, "impossible here!!!!");
exit(-1);
}
set_cur_stream(NULL);
}
} else {
uint8_t *buf = calloc(len, 1);
assert(buf);
evbuffer_remove(input, buf, len);
handle_frps_msg(buf, len, ctx);
SAFE_FREE(buf);
}
SAFE_FREE(buf);
return;
}
@@ -514,76 +594,57 @@ static void
connect_event_cb (struct bufferevent *bev, short what, void *ctx)
{
struct common_conf *c_conf = get_common_config();
static int retry_times = 0;
static int retry_times = 1;
if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
if (retry_times >= 10) { // only try 10 times consecutively
debug(LOG_ERR,
"have retry connect to xfrp server for %d times, exit!",
if (retry_times >= 100) {
debug(LOG_INFO,
"have retry connect to xfrp server for %d times, exit?",
retry_times);
exit(0);
}
sleep(2);
retry_times++;
debug(LOG_ERR, "error: connect server [%s:%d] failed",
debug(LOG_ERR, "error: connect server [%s:%d] failed %s",
c_conf->server_addr,
c_conf->server_port);
free_control();
init_main_control();
start_base_connect();
close_main_control();
c_conf->server_port,
strerror(errno));
reset_session_id();
clear_main_control();
run_control();
} else if (what & BEV_EVENT_CONNECTED) {
retry_times = 0;
send_window_update(bev, &main_ctl->stream, 0);
login();
keep_control_alive();
}
}
static void
keep_control_alive()
{
debug(LOG_DEBUG, "start keep_control_alive");
main_ctl->ticker_ping = evtimer_new(main_ctl->connect_base, hb_sender_cb, NULL);
if ( ! main_ctl->ticker_ping) {
if ( !main_ctl->ticker_ping) {
debug(LOG_ERR, "Ping Ticker init failed!");
return;
}
pong_time = time(NULL);
set_ticker_ping_timer(main_ctl->ticker_ping);
}
static void
server_dns_cb(int event_code, struct evutil_addrinfo *addr, void *ctx)
{
if (event_code) {
set_common_server_ip((const char *)evutil_gai_strerror(event_code));
} else {
struct evutil_addrinfo *ai;
if (addr->ai_canonname)
debug(LOG_DEBUG, "addr->ai_canonname [%s]", addr->ai_canonname);
for (ai = addr; ai; ai = ai->ai_next) {
char buf[128];
const char *s = NULL;
if (ai->ai_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr;
s = evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, 128);
} else if (ai->ai_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr;
s = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, 128);
}
if (s) set_common_server_ip(s);
}
if (addr) evutil_freeaddrinfo(addr);
}
}
void
start_base_connect()
{
struct common_conf *c_conf = get_common_config();
if (main_ctl->connect_bev)
bufferevent_free(main_ctl->connect_bev);
main_ctl->connect_bev = connect_server(main_ctl->connect_base,
c_conf->server_addr,
c_conf->server_port);
if ( ! main_ctl->connect_bev) {
debug(LOG_ERR, "error: connect server [%s:%d] failed", c_conf->server_addr, c_conf->server_port);
debug(LOG_ERR, "error: connect server [%s:%d] failed: [%d: %s]",
c_conf->server_addr, c_conf->server_port, errno, strerror(errno));
exit(0);
}
@@ -603,7 +664,7 @@ login()
exit(0);
}
send_msg_frp_server(NULL, TypeLogin, lg_msg, len, main_ctl->session_id);
send_msg_frp_server(NULL, TypeLogin, lg_msg, len, &main_ctl->stream);
SAFE_FREE(lg_msg);
}
@@ -612,7 +673,7 @@ send_msg_frp_server(struct bufferevent *bev,
const enum msg_type type,
const char *msg,
const size_t msg_len,
uint32_t sid)
struct tmux_stream *stream)
{
struct bufferevent *bout = NULL;
if (bev) {
@@ -622,15 +683,22 @@ send_msg_frp_server(struct bufferevent *bev,
}
assert(bout);
debug(LOG_DEBUG, "send plain msg ----> [%c: %s]", type, msg);
struct msg_hdr *req_msg = calloc(msg_len+sizeof(struct msg_hdr), 1);
size_t len = msg_len + sizeof(struct msg_hdr);
struct msg_hdr *req_msg = calloc(len, 1);
assert(req_msg);
req_msg->type = type;
req_msg->length = msg_hton((uint64_t)msg_len);
memcpy(req_msg->data, msg, msg_len);
bufferevent_write(bout, (uint8_t *)req_msg, msg_len+sizeof(struct msg_hdr));
struct common_conf *c_conf = get_common_config();
if (c_conf->tcp_mux)
tmux_stream_write(bout, (uint8_t *)req_msg, len, stream);
else
bufferevent_write(bout, (uint8_t *)req_msg, len);
free(req_msg);
}
@@ -640,7 +708,7 @@ send_enc_msg_frp_server(struct bufferevent *bev,
const enum msg_type type,
const char *msg,
const size_t msg_len,
uint32_t sid)
struct tmux_stream *stream)
{
struct bufferevent *bout = NULL;
if (bev) {
@@ -650,26 +718,28 @@ send_enc_msg_frp_server(struct bufferevent *bev,
}
assert(bout);
debug(LOG_DEBUG, "send enc msg ----> [%c: %s]", type, msg);
struct msg_hdr *req_msg = calloc(msg_len+sizeof(struct msg_hdr), 1);
assert(req_msg);
req_msg->type = type;
req_msg->length = msg_hton((uint64_t)msg_len);
memcpy(req_msg->data, msg, msg_len);
struct common_conf *c_conf = get_common_config();
if (get_main_encoder() == NULL) {
debug(LOG_DEBUG, "init_main_encoder .......");
struct frp_coder *coder = init_main_encoder();
bufferevent_write(bout, coder->iv, 16);
if (c_conf->tcp_mux)
tmux_stream_write(bout, coder->iv, 16, stream);
else
bufferevent_write(bout, coder->iv, 16);
}
uint8_t *enc_msg = NULL;
size_t olen = encrypt_data((uint8_t *)req_msg, msg_len+sizeof(struct msg_hdr), get_main_encoder(), &enc_msg);
assert(olen > 0);
debug(LOG_DEBUG, "encrypt_data length %d", olen);
bufferevent_write(bout, enc_msg, olen);
if (c_conf->tcp_mux)
tmux_stream_write(bout, enc_msg, olen, stream);
else
bufferevent_write(bout, enc_msg, olen);
free(enc_msg);
free(req_msg);
@@ -717,7 +787,7 @@ send_new_proxy(struct proxy_service *ps)
debug(LOG_DEBUG, "control proxy client: [Type %d : proxy_name %s : msg_len %d]", TypeNewProxy, ps->proxy_name, len);
send_enc_msg_frp_server(NULL, TypeNewProxy, new_proxy_msg, len, main_ctl->session_id);
send_enc_msg_frp_server(NULL, TypeNewProxy, new_proxy_msg, len, &main_ctl->stream);
SAFE_FREE(new_proxy_msg);
}
@@ -733,14 +803,6 @@ init_main_control()
assert(main_ctl);
struct common_conf *c_conf = get_common_config();
if (c_conf->tcp_mux) {
uint32_t *sid = init_sid_index();
assert(sid);
main_ctl->session_id = *sid;
debug(LOG_DEBUG, "Connect Frps with control session ID: %d", main_ctl->session_id);
}
struct event_base *base = NULL;
struct evdns_base *dnsbase = NULL;
base = event_base_new();
@@ -749,6 +811,14 @@ init_main_control()
exit(0);
}
main_ctl->connect_base = base;
if (c_conf->tcp_mux) {
init_tmux_stream(&main_ctl->stream, get_next_session_id(), INIT);
}
// if server_addr is ip, done control init.
if (is_valid_ip_address((const char *)c_conf->server_addr))
return;
dnsbase = evdns_base_new(base, 1);
if (! dnsbase) {
@@ -766,55 +836,44 @@ init_main_control()
evdns_base_nameserver_ip_add(dnsbase, "223.5.5.5"); //AliDNS
evdns_base_nameserver_ip_add(dnsbase, "223.6.6.6"); //AliDNS
evdns_base_nameserver_ip_add(dnsbase, "114.114.114.114"); //114DNS
}
// if server_addr is ip, done control init.
if (is_valid_ip_address((const char *)c_conf->server_addr))
return;
// if server_addr is domain, analyze it to ip for server_ip
debug(LOG_DEBUG, "Get ip address of [%s] from DNServer", c_conf->server_addr);
static void
free_main_control()
{
SAFE_FREE(main_ctl);
main_ctl = NULL;
}
struct evutil_addrinfo hints;
struct evdns_getaddrinfo_request *dns_req;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_flags = EVUTIL_AI_CANONNAME;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
dns_req = evdns_getaddrinfo(dnsbase,
c_conf->server_addr,
NULL /* no service name given */,
&hints,
server_dns_cb,
NULL);
if (! dns_req) {
debug(LOG_ERR, "error: can not analyse the dns of [%s]", c_conf->server_addr);
exit(0);
}
static void
clear_main_control()
{
assert(main_ctl);
if (main_ctl->ticker_ping) evtimer_del(main_ctl->ticker_ping);
if (main_ctl->tcp_mux_ping_event) evtimer_del(main_ctl->tcp_mux_ping_event);
clear_all_proxy_client();
free_evp_cipher_ctx();
set_client_status(0);
pong_time = 0;
is_login = 0;
}
void
close_main_control()
{
assert(main_ctl);
clear_main_control();
event_base_dispatch(main_ctl->connect_base);
event_base_free(main_ctl->connect_base);
evdns_base_free(main_ctl->dnsbase, 0);
event_base_free(main_ctl->connect_base);
free_main_control();
}
void
run_control()
{
start_base_connect();
keep_control_alive();
}
void
free_control()
{
if (!main_ctl)
return;
SAFE_FREE(main_ctl);
}

View File

@@ -40,38 +40,45 @@ struct control {
struct event_base *connect_base; //main netevent
struct evdns_base *dnsbase;
struct bufferevent *connect_bev; //main io evet buf
char session_id;
struct event *ticker_ping; //heartbeat timer
struct event *tcp_mux_ping_event;
uint32_t tcp_mux_ping_id;
struct tmux_stream stream;
};
void connect_eventcb(struct bufferevent *bev, short events, void *ptr);
void start_base_connect();
void init_main_control();
void run_control();
struct control *get_main_control();
void close_main_control();
void start_login_frp_server(struct event_base *base);
void send_login_frp_server(struct bufferevent *bev);
void login();
void free_control();
void sync_session_id(uint32_t sid);
void send_msg_frp_server(struct bufferevent *bev,
const enum msg_type type,
const char *msg,
const size_t msg_len,
uint32_t sid);
struct tmux_stream *stream);
void send_enc_msg_frp_server(struct bufferevent *bev,
const enum msg_type type,
const char *msg,
const size_t msg_len,
uint32_t sid);
struct tmux_stream *stream);
void control_process(struct proxy_client *client);
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_server(struct event_base *base, const char *name, const int port);
#endif //_CONTROL_H_

109
crypto.c
View File

@@ -1,3 +1,30 @@
/* 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 crypto.c
@brief xfrpc crypto implement
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
@@ -16,6 +43,46 @@ static const char *default_salt = "frp";
static const size_t block_size = 16;
static struct frp_coder *main_encoder = NULL;
static struct frp_coder *main_decoder = NULL;
static EVP_CIPHER_CTX *enc_ctx = NULL;
static EVP_CIPHER_CTX *dec_ctx = NULL;
static void
free_frp_coder(struct frp_coder *coder)
{
free(coder->salt);
free(coder->token);
free(coder);
}
static void
free_all_frp_coder()
{
if (main_encoder) {
free_frp_coder(main_encoder);
main_encoder = NULL;
}
if (main_decoder) {
free_frp_coder(main_decoder);
main_decoder = NULL;
}
}
void
free_evp_cipher_ctx()
{
free_all_frp_coder();
if (enc_ctx) {
EVP_CIPHER_CTX_free(enc_ctx);
enc_ctx = NULL;
}
if (dec_ctx) {
EVP_CIPHER_CTX_free(dec_ctx);
dec_ctx = NULL;
}
}
size_t
get_block_size()
@@ -24,14 +91,14 @@ get_block_size()
}
struct frp_coder *
new_coder(const char *privilege_token, const char *salt)
new_coder(const char *token, const char *salt)
{
struct frp_coder *enc = calloc(sizeof(struct frp_coder), 1);
assert(enc);
enc->privilege_token = privilege_token ? strdup(privilege_token):"\0";
enc->token = token ? strdup(token):strdup("\0");
enc->salt = strdup(salt);
encrypt_key(enc->privilege_token, strlen(enc->privilege_token), enc->salt, enc->key, block_size);
encrypt_key(enc->token, strlen(enc->token), enc->salt, enc->key, block_size);
encrypt_iv(enc->iv, block_size);
return enc;
}
@@ -42,7 +109,7 @@ clone_coder(const struct frp_coder *coder)
assert(coder);
struct frp_coder *enc = calloc(sizeof(struct frp_coder), 1);
memcpy(enc, coder, sizeof(*coder));
enc->privilege_token = strdup(coder->privilege_token);
enc->token = strdup(coder->token);
enc->salt = strdup(coder->salt);
return enc;
@@ -67,7 +134,7 @@ init_main_encoder()
}
struct frp_coder *
init_main_decoder(unsigned char *iv)
init_main_decoder(const uint8_t *iv)
{
struct common_conf *c_conf = get_common_config();
main_decoder = new_coder(c_conf->auth_token, default_salt);
@@ -132,19 +199,11 @@ encrypt_iv(unsigned char *iv_buf, size_t iv_len)
return iv_buf;
}
static void
print_hex(uint8_t *val, size_t len)
{
for(int i = 0; i < len; i++)
printf("%1x", val[i]);
printf("\n");
}
// using aes-128-cfb and nopadding
size_t
encrypt_data(const unsigned char *src_data, size_t srclen, struct frp_coder *encoder, unsigned char **ret)
encrypt_data(const uint8_t *src_data, size_t srclen, struct frp_coder *encoder, unsigned char **ret)
{
uint8_t *intext = src_data;
uint8_t *intext = (uint8_t *)src_data;
assert(intext);
assert(encoder);
struct frp_coder *c = encoder;
@@ -156,11 +215,11 @@ encrypt_data(const unsigned char *src_data, size_t srclen, struct frp_coder *enc
assert(outbuf);
*ret = outbuf;
static EVP_CIPHER_CTX *ctx = NULL;
if (!ctx) {
ctx = EVP_CIPHER_CTX_new();
EVP_EncryptInit_ex(ctx, EVP_aes_128_cfb(), NULL, c->key, c->iv);
if (!enc_ctx) {
enc_ctx = EVP_CIPHER_CTX_new();
EVP_EncryptInit_ex(enc_ctx, EVP_aes_128_cfb(), NULL, c->key, c->iv);
}
EVP_CIPHER_CTX *ctx = enc_ctx;
if(!EVP_EncryptUpdate(ctx, outbuf, &tmplen, intext, (int)srclen)) {
debug(LOG_ERR, "EVP_EncryptUpdate error!");
@@ -180,7 +239,7 @@ E_END:
size_t
decrypt_data(const uint8_t *enc_data, size_t enclen, struct frp_coder *decoder, uint8_t **ret)
{
uint8_t *inbuf = enc_data;
uint8_t *inbuf = (uint8_t *)enc_data;
uint8_t *outbuf = calloc(enclen+1, 1);
struct frp_coder *c = decoder;
assert(inbuf);
@@ -189,12 +248,12 @@ decrypt_data(const uint8_t *enc_data, size_t enclen, struct frp_coder *decoder,
assert(decoder);
int outlen = 0, tmplen = 0;
static EVP_CIPHER_CTX *ctx= NULL;
if (!ctx) {
ctx= EVP_CIPHER_CTX_new();
EVP_DecryptInit_ex(ctx, EVP_aes_128_cfb(), NULL, c->key, c->iv);
if (!dec_ctx) {
dec_ctx= EVP_CIPHER_CTX_new();
EVP_DecryptInit_ex(dec_ctx, EVP_aes_128_cfb(), NULL, c->key, c->iv);
}
EVP_CIPHER_CTX *ctx = dec_ctx;
if(!EVP_DecryptUpdate(ctx, outbuf, &tmplen, inbuf, enclen)) {
debug(LOG_ERR, "EVP_DecryptUpdate error!");
goto D_END;
@@ -214,7 +273,7 @@ D_END:
void
free_encoder(struct frp_coder *encoder) {
if (encoder) {
SAFE_FREE(encoder->privilege_token);
SAFE_FREE(encoder->token);
SAFE_FREE(encoder->salt);
free(encoder);
}

View File

@@ -1,3 +1,30 @@
/* 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 crypto.h
@brief xfrpc crypto header
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
#ifndef _CRYPTO_H_
#define _CRYPTO_H_
@@ -11,22 +38,23 @@ struct frp_coder {
uint8_t key[16];
char *salt;
uint8_t iv[16];
char *privilege_token;
char *token;
};
size_t get_encrypt_block_size();
size_t decrypt_data(const unsigned char *enc_data, size_t enc_len, struct frp_coder *decoder, unsigned char **ret);
size_t decrypt_data(const uint8_t *enc_data, size_t enc_len, struct frp_coder *decoder, uint8_t **ret);
int is_encoder_inited();
int is_decoder_inited();
struct frp_coder *init_main_encoder();
struct frp_coder *init_main_decoder(unsigned char *iv);
struct frp_coder *new_coder(const char *privilege_token, const char *salt);
struct frp_coder *init_main_decoder(const uint8_t *iv);
struct frp_coder *new_coder(const char *token, const char *salt);
uint8_t *encrypt_key(const char *token, size_t token_len, const char *salt, uint8_t *key, size_t key_len);
uint8_t *encrypt_iv(unsigned char *iv_buf, size_t iv_len);
size_t encrypt_data(const unsigned char *src_data, size_t srclen, struct frp_coder *encoder, unsigned char **ret);
uint8_t *encrypt_iv(uint8_t *iv_buf, size_t iv_len);
size_t encrypt_data(const uint8_t *src_data, size_t srclen, struct frp_coder *encoder, uint8_t **ret);
struct frp_coder *get_main_encoder();
struct frp_coder *get_main_decoder();
size_t get_block_size();
void free_encoder(struct frp_coder *encoder);
void free_evp_cipher_ctx();
#endif // _CRYPTO_H_

View File

@@ -34,6 +34,8 @@
#include "debug.h"
#define PROGNAME "xfrpc"
debugconf_t debugconf = {
.debuglevel = LOG_INFO,
.log_stderr = 1,
@@ -46,7 +48,7 @@ Do not use directly, use the debug macro */
void
_debug(const char *filename, int line, int level, const char *format, ...)
{
char buf[28];
char buf[32] = {0};
va_list vlist;
time_t ts;
sigset_t block_chld;
@@ -75,7 +77,7 @@ _debug(const char *filename, int line, int level, const char *format, ...)
}
if (debugconf.log_syslog) {
openlog("wifidog", LOG_PID, debugconf.syslog_facility);
openlog(PROGNAME, LOG_PID, debugconf.syslog_facility);
va_start(vlist, format);
vsyslog(level, format, vlist);
va_end(vlist);

View File

@@ -28,6 +28,7 @@
#define _WIFIDOG_DEBUG_H_
#include <string.h>
#include <syslog.h>
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)

27
login.c
View File

@@ -1,3 +1,29 @@
/* 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 login.c
@brief xfrpc login protocol implemented
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -63,7 +89,6 @@ void init_login()
c_login->metas = NULL;
c_login->pool_count = 1;
c_login->privilege_key = NULL;
c_login->user = c_conf->user;
c_login->logged = 0;

26
login.h
View File

@@ -1,3 +1,29 @@
/* 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 login.h
@brief xfrp login header
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
#ifndef _LOGIN_H_
#define _LOGIN_H_

17
msg.c
View File

@@ -20,7 +20,7 @@
\********************************************************************/
/** @file msg.c
@brief xfrp client msg related
@brief xfrpc client msg related
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
@@ -48,7 +48,7 @@ json_object_object_add(jobj, key, json_object_new_##jtype((item)));
#define SAFE_JSON_STRING(str_target) \
str_target?str_target:"\0"
const char msg_typs[] = {TypeLogin,
const char msg_types[] = {TypeLogin,
TypeLoginResp,
TypeNewProxy,
TypeNewProxyResp,
@@ -268,8 +268,7 @@ new_proxy_resp_unmarshal(const char *jres)
if (port) {
port++;
npr->remote_port = atoi(port);
}else
goto END_ERROR;
}
struct json_object *npr_proxy_name = NULL;
if (! json_object_object_get_ex(j_np_res, "proxy_name", &npr_proxy_name))
@@ -388,11 +387,17 @@ int
msg_type_valid_check(char msg_type)
{
int i = 0;
for(i = 0; i<(sizeof(msg_typs) / sizeof(*msg_typs)); i++) {
if (msg_typs[i] == msg_type)
for(i = 0; i<(sizeof(msg_types) / sizeof(*msg_types)); i++) {
if (msg_types[i] == msg_type)
return 1;
}
return 0;
}
char *
get_msg_type(uint8_t type)
{
return NULL;
}

4
msg.h
View File

@@ -20,7 +20,7 @@
\********************************************************************/
/** @file msg.h
@brief xfrp msg struct
@brief xfrpc msg struct
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
@@ -114,4 +114,6 @@ int new_work_conn_marshal(const struct work_conn *work_c, char **msg);
void control_response_free(struct control_response *res);
char *get_msg_type(uint8_t type);
#endif //_MSG_H_

View File

@@ -1,54 +0,0 @@
#
# Copyright (C) 2022 Dengfeng Liu
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=xfrpc
PKG_VERSION:=master
PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/liudf0716/xfrpc.git
PKG_SOURCE_VERSION:=$(PKG_VERSION)
PKG_MIRROR_HASH:=a7141da8a85203d8341fd6d2551f1228396ae54aace6ec0bad2dfeb89e0a377d
PKG_MAINTAINER:=Dengfeng Liu <liudf0716@gmail.com>
PKG_LICENSE:=GPL-3.0-or-later
PKG_LICENSE_FILES:=COPYING
include $(INCLUDE_DIR)/package.mk
include $(INCLUDE_DIR)/cmake.mk
define Package/xfrpc
SUBMENU:=Web Servers/Proxies
SECTION:=net
CATEGORY:=Network
DEPENDS:=+zlib +libjson-c +libevent2 +libevent2-openssl
TITLE:= C language fast reverse proxy client
URL:=https://github.com/liudf0716/xfrpc
endef
define Package/xfrpc/description
xfrpc is C language fast reverse proxy client
compare with golang version frpc
xfrpc can run in almost all openwrt device
endef
define Package/xfrpc/conffiles
/etc/config/xfrpc
endef
define Package/xfrpc/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/xfrpc $(1)/usr/bin/xfrpc
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/xfrpc.init $(1)/etc/init.d/xfrpc
$(INSTALL_DIR) $(1)/etc/config
$(CP) ./files/xfrpc.conf $(1)/etc/config/xfrpc
endef
$(eval $(call BuildPackage,xfrpc))

View File

@@ -1,31 +0,0 @@
config xfrp 'init'
option disabled 1
option loglevel 7
config xfrpc 'common'
option server_addr 127.0.0.1
option server_port 7000
option auth_token abdesf13d
config xfrpc 'ssh01'
option type tcp
option local_ip 127.0.0.1
option local_port 22
option remote_port 6000
#config xfrpc 'ftp01'
# option type ftp
# option local_ip 127.0.01
# option local_port 21
# option remote_port 8021
# option remote_data_port 8022
#config xfrpc 'web01'
# option type http
# option local_ip 127.0.0.1
# option local_port 8080
#config xfrpc 'web02'
# option type https
# option local_ip 127.0.0.1
# option local_port 8443

View File

@@ -1,93 +0,0 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2022 Dengfeng Liu <liu_df@qq.com>
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#
START=99
USE_PROCD=1
NAME=xfrpc
PROG=/usr/bin/$NAME
handle_xfrpc() {
local name="$1"
local config="$2"
echo "[$name]" >> "$config"
handle_type() {
uci_validate_section xfrpc xfrpc "$name" \
'type:or("tcp", "udp", "ftp", "http", "https")' \
'local_ip:ipaddr:127.0.0.1' \
'local_port:uinteger'
echo "type = $type" >> "$config"
echo "local_ip = $local_ip" >> "$config"
echo "local_port = $local_port" >> "$config"
case "$type" in
"tcp"|"udp")
config_get remote_port "$name" remote_port
echo "remote_port = $remote_port" >> "$config"
;;
"ftp")
config_get remote_port "$name" remote_port
config_get remote_data_port "$name" remote_data_port
echo "remote_port = $remote_port" >> "$config"
echo "remote_data_port = $remote_data_port" >> "$config"
;;
esac
}
if [ "$name" = "common" ]; then
uci_validate_section xfrpc xfrpc "$name" \
'server_addr:ipaddr' \
'server_port:uinteger' \
'auth_token:string'
[ -z "$auth_token" ] && {
echo "no auth_token"
exit
}
echo "server_addr = $server_addr" >> "$config"
echo "server_port = $server_port" >> "$config"
echo "auth_token = $auth_token" >> "$config"
else
handle_type
fi
}
service_triggers() {
procd_add_reload_trigger "$NAME"
}
start_service() {
local conf_file="/var/etc/$NAME.ini"
> "$conf_file"
config_load "$NAME"
uci_validate_section xfrpc xfrpc init \
'disabled:bool:1' \
'loglevel:uinteger:0'
if [ $disabled = 1 ]; then
echo "xfrpc service disabled"
return
fi
config_foreach handle_xfrpc xfrpc "$conf_file"
procd_open_instance
procd_set_param command "$PROG" -c "$conf_file" -f -d $loglevel
procd_set_param file "$conf_file"
procd_set_param respawn
procd_close_instance
}
reload_service() {
stop
start
}

30
proxy.c
View File

@@ -1,3 +1,29 @@
/* 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.c
@brief xfrp proxy implemented
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
@@ -8,10 +34,6 @@
#include <errno.h>
#include <syslog.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/event.h>
#include "debug.h"
#include "uthash.h"

26
proxy.h
View File

@@ -1,3 +1,29 @@
/* 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.h
@brief xfrp proxy header file
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
#ifndef _PROXY_H_
#define _PROXY_H_

View File

@@ -101,12 +101,12 @@ void ftp_proxy_c2s_cb(struct bufferevent *bev, void *ctx)
struct ftp_pasv *r_fp = new_ftp_pasv();
r_fp->code = local_fp->code;
if (! c_conf->server_ip) {
if (! c_conf->server_addr) {
debug(LOG_ERR, "error: FTP proxy without server ip!");
exit(0);
}
strncpy(r_fp->ftp_server_ip, c_conf->server_ip, IP_LEN);
strncpy(r_fp->ftp_server_ip, c_conf->server_addr, IP_LEN);
r_fp->ftp_server_port = p->remote_data_port;
if (r_fp->ftp_server_port <= 0) {
@@ -264,4 +264,4 @@ static void free_ftp_pasv(struct ftp_pasv *fp)
SAFE_FREE(fp);
fp = NULL;
}
}

View File

@@ -1,3 +1,29 @@
/* 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 tcp implemented
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
@@ -7,6 +33,7 @@
#include <netinet/in.h>
#include <errno.h>
#include <syslog.h>
#include <unistd.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
@@ -17,25 +44,42 @@
#include "uthash.h"
#include "common.h"
#include "proxy.h"
#include "config.h"
#include "tcpmux.h"
#define BUF_LEN 2*1024
// read data from local service
void tcp_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, *dst;
size_t len;
src = bufferevent_get_input(bev);
len = evbuffer_get_length(src);
if (len > 0) {
dst = bufferevent_get_output(partner);
evbuffer_add_buffer(dst, src);
struct evbuffer *src = bufferevent_get_input(bev);
size_t len = evbuffer_get_length(src);
assert(len > 0);
if (!c_conf->tcp_mux) {
struct evbuffer *dst = bufferevent_get_output(partner);
evbuffer_add_buffer(dst, src);
return;
}
uint8_t *buf = (uint8_t *)malloc(len);
assert(buf != NULL);
memset(buf, 0, len);
uint32_t nr = bufferevent_read(bev, buf, len);
assert(nr == len);
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);
}
}
// read data from frps
// when tcp mux enable this function will not be used
void tcp_proxy_s2c_cb(struct bufferevent *bev, void *ctx)
{
struct proxy_client *client = (struct proxy_client *)ctx;
@@ -44,6 +88,8 @@ void tcp_proxy_s2c_cb(struct bufferevent *bev, void *ctx)
assert(partner);
struct evbuffer *src, *dst;
src = bufferevent_get_input(bev);
size_t len = evbuffer_get_length(src);
assert(len > 0);
dst = bufferevent_get_output(partner);
evbuffer_add_buffer(dst, src);
}

View File

@@ -1,54 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <syslog.h>
#include "uthash.h"
#include "session.h"
#include "version.h"
#include "debug.h"
uint32_t *sid_index = NULL;
// need free
uint32_t *
init_sid_index()
{
if (NULL == sid_index) {
sid_index = (uint32_t *)calloc(1, sizeof(uint32_t));
if (NULL == sid_index)
return sid_index;
#ifdef CLIENT_V
*sid_index = 1;
#elif SERVER
*sid_index = 0;
#endif
}
*sid_index += 2; //xfrp client session id start from 3
return sid_index;
}
uint32_t
get_current_sid_index()
{
if (NULL == sid_index) {
return *init_sid_index();
}
return *sid_index;
}
uint32_t
new_sid()
{
if (NULL == sid_index) {
init_sid_index();
return get_current_sid_index();
}
*sid_index += 2;
return *sid_index;
}

View File

@@ -1,11 +0,0 @@
#ifndef _SESSION_H_
#define _SESSION_H_
#include "uthash.h"
#include "common.h"
uint32_t *init_sid_index();
uint32_t get_current_sid_index();
uint32_t new_sid();
#endif //_SESSION_H_

16
systemd/xfrpc.service Normal file
View File

@@ -0,0 +1,16 @@
# 1. put xfrpc and xfrpc.ini under /usr/local/xfrpc/
# 2. put this file (xfrpc.service) at /etc/systemd/system
# 3. run `sudo systemctl daemon-reload && sudo systemctl enable xfrpc && sudo systemctl start xfrpc`
# Then we can manage xfrpc with `sudo service xfrpc {start|stop|restart|status}`
[Unit]
Description=frp c language client
Wants=network-online.target
After=network.target network-online.target
[Service]
ExecStart=/usr/local/xfrpc/xfrpc -c /usr/local/xfrpc/xfrpc.ini -f -d 0
[Install]
WantedBy=multi-user.target

610
tcpmux.c Normal file
View File

@@ -0,0 +1,610 @@
/* 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 tcpmux.c
@brief xfrp tcp mux implemented
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
#include <unistd.h>
#include <stdlib.h>
#include "common.h"
#include "tcpmux.h"
#include "client.h"
#include "config.h"
#include "debug.h"
#include "control.h"
static uint8_t proto_version = 0;
static uint8_t remote_go_away;
static uint8_t local_go_away;
static uint32_t g_session_id = 1;
static struct tmux_stream *cur_stream = NULL;
static struct tmux_stream *all_stream;
static uint32_t ring_buffer_read(struct bufferevent *bev, struct ring_buffer *ring, uint32_t len);
static uint32_t ring_buffer_write(struct bufferevent *bev, struct ring_buffer *ring, uint32_t len);
void
add_stream(struct tmux_stream *stream)
{
HASH_ADD_INT(all_stream, id, stream);
}
void
del_stream(uint32_t id)
{
assert(all_stream != NULL);
struct tmux_stream *stream = get_stream_by_id(id);
if (stream)
HASH_DEL(all_stream, stream);
}
struct tmux_stream *
get_stream_by_id(uint32_t id)
{
if (!all_stream) return NULL;
struct tmux_stream *stream = NULL;
HASH_FIND_INT(all_stream, &id, stream);
return stream;
}
struct tmux_stream *
get_cur_stream()
{
return cur_stream;
}
void
set_cur_stream(struct tmux_stream *stream)
{
cur_stream = stream;
}
void
init_tmux_stream(struct tmux_stream *stream, uint32_t id, enum tcp_mux_state state)
{
stream->id = id;
stream->state = state;
stream->recv_window = MAX_STREAM_WINDOW_SIZE;
stream->send_window = MAX_STREAM_WINDOW_SIZE;
memset(&stream->tx_ring, 0, sizeof(struct ring_buffer));
memset(&stream->rx_ring, 0, sizeof(struct ring_buffer));
add_stream(stream);
};
int
validate_tcp_mux_protocol(struct tcp_mux_header *tmux_hdr)
{
if (tmux_hdr->version != proto_version) return 0;
if (tmux_hdr->type > GO_AWAY) return 0;
return 1;
}
void
tcp_mux_encode(enum tcp_mux_type type, enum tcp_mux_flag flags, uint32_t stream_id, uint32_t length, struct tcp_mux_header *tmux_hdr)
{
assert(tmux_hdr);
tmux_hdr->version = proto_version;
tmux_hdr->type = type;
tmux_hdr->flags = htons(flags);
tmux_hdr->stream_id = htonl(stream_id);
tmux_hdr->length = length?htonl(length):0;
}
static uint32_t
tcp_mux_flag()
{
struct common_conf *c_conf = get_common_config();
return c_conf->tcp_mux;
}
void
reset_session_id() {
g_session_id = 1;
}
uint32_t
get_next_session_id() {
uint32_t id = g_session_id;
g_session_id += 2;
return id;
}
static void
tcp_mux_send_win_update(struct bufferevent *bout, enum tcp_mux_flag flags, uint32_t stream_id, uint32_t delta)
{
struct tcp_mux_header tmux_hdr;
memset(&tmux_hdr, 0, sizeof(tmux_hdr));
tcp_mux_encode(WINDOW_UPDATE, flags, stream_id, delta, &tmux_hdr);
bufferevent_write(bout, (uint8_t *)&tmux_hdr, sizeof(tmux_hdr));
}
void
tcp_mux_send_win_update_syn(struct bufferevent *bout, uint32_t stream_id)
{
if (!tcp_mux_flag()) return;
tcp_mux_send_win_update(bout, SYN, stream_id, 0);
}
void
tcp_mux_send_win_update_ack(struct bufferevent *bout, uint32_t stream_id, uint32_t delta)
{
if (!tcp_mux_flag()) return;
tcp_mux_send_win_update(bout, ZERO, stream_id, 0);
}
void
tcp_mux_send_win_update_fin(struct bufferevent *bout, uint32_t stream_id)
{
if (!tcp_mux_flag()) return;
tcp_mux_send_win_update(bout, FIN, stream_id, 0);
}
void
tcp_mux_send_win_update_rst(struct bufferevent *bout, uint32_t stream_id)
{
if (!tcp_mux_flag()) return;
tcp_mux_send_win_update(bout, RST, stream_id, 0);
}
void
tcp_mux_send_data(struct bufferevent *bout, uint16_t flags, uint32_t stream_id, uint32_t length)
{
if (!tcp_mux_flag()) return;
struct tcp_mux_header tmux_hdr;
memset(&tmux_hdr, 0, sizeof(tmux_hdr));
tcp_mux_encode(DATA, flags, stream_id, length, &tmux_hdr);
//debug(LOG_DEBUG, "tcp mux [%d] send data len : %d", stream_id, length);
bufferevent_write(bout, (uint8_t *)&tmux_hdr, sizeof(tmux_hdr));
}
void
tcp_mux_send_ping(struct bufferevent *bout, uint32_t ping_id)
{
if (!tcp_mux_flag()) return;
struct tcp_mux_header tmux_hdr;
memset(&tmux_hdr, 0, sizeof(tmux_hdr));
tcp_mux_encode(PING, SYN, 0, ping_id, &tmux_hdr);
//debug(LOG_DEBUG, "tcp mux send ping syn : %d", ping_id);
bufferevent_write(bout, (uint8_t *)&tmux_hdr, sizeof(tmux_hdr));
}
static void
tcp_mux_handle_ping(struct bufferevent *bout, uint32_t ping_id)
{
if (!tcp_mux_flag()) return;
struct tcp_mux_header tmux_hdr;
memset(&tmux_hdr, 0, sizeof(tmux_hdr));
tcp_mux_encode(PING, ACK, 0, ping_id, &tmux_hdr);
//debug(LOG_DEBUG, "tcp mux send ping ack : %d", ping_id);
bufferevent_write(bout, (uint8_t *)&tmux_hdr, sizeof(tmux_hdr));
}
static void
tcp_mux_send_go_away(struct bufferevent *bout, uint32_t reason)
{
if (!tcp_mux_flag()) return;
struct tcp_mux_header tmux_hdr;
memset(&tmux_hdr, 0, sizeof(tmux_hdr));
tcp_mux_encode(GO_AWAY, 0, 0, reason, &tmux_hdr);
//debug(LOG_DEBUG, "tcp mux send ping ack : %d", ping_id);
bufferevent_write(bout, (uint8_t *)&tmux_hdr, sizeof(tmux_hdr));
}
static int
process_flags(uint16_t flags, struct tmux_stream *stream)
{
uint32_t close_stream = 0;
if ( (flags&ACK) == ACK ) {
if (stream->state == SYN_SEND)
stream->state = ESTABLISHED;
} else if ( (flags&FIN) == FIN ) {
switch(stream->state) {
case SYN_SEND:
case SYN_RECEIVED:
case ESTABLISHED:
stream->state = REMOTE_CLOSE;
break;
case LOCAL_CLOSE:
stream->state = CLOSED;
close_stream = 1;
break;
default:
debug(LOG_ERR, "unexpected FIN flag in state %d", stream->state);
assert(0);
return 0;
}
} else if ( (flags&RST) == RST ) {
stream->state = RESET;
close_stream = 1;
}
if (close_stream) {
debug(LOG_DEBUG, "free stream %d", stream->id);
del_proxy_client_by_stream_id(stream->id);
}
return 1;
}
static uint16_t
get_send_flags(struct tmux_stream *stream)
{
uint16_t flags = 0;
switch (stream->state) {
case INIT:
flags |= SYN;
stream->state = SYN_SEND;
break;
case SYN_RECEIVED:
flags |= ACK;
stream->state = ESTABLISHED;
break;
default:
break;
}
return flags;
}
void
send_window_update(struct bufferevent *bout, struct tmux_stream *stream, uint32_t length)
{
uint32_t max = MAX_STREAM_WINDOW_SIZE;
uint32_t delta = (max - length) - stream->recv_window;
uint16_t flags = get_send_flags(stream);
if (delta < max/2 && flags == 0)
return;
stream->recv_window += delta;
tcp_mux_send_win_update(bout, flags, stream->id, delta);
//debug(LOG_DEBUG, "send window update: flags %d, stream_id %d delta %d, recv_window %u length %u",
// flags, stream->id, delta, stream->recv_window, length);
}
static int
ring_buffer_pop(struct ring_buffer *ring, uint8_t *data, uint32_t len)
{
assert(ring->sz >= len);
assert(data != NULL);
uint32_t i = 0;
while(i < len) {
data[i] = ring->data[ring->cur++];
if (ring->cur == RBUF_SIZE)
ring->cur = 0;
i++;
ring->sz--;
}
assert(i == len);
return len;
}
static int
process_data(struct tmux_stream *stream, uint32_t length, uint16_t flags,
void (*fn)(uint8_t *, int, void *), void *param)
{
if (!process_flags(flags, stream)) return 0;
if (length > stream->recv_window) {
debug(LOG_ERR, "receive window exceed (remain %d, recv %d)", stream->recv_window, length);
return 0;
}
stream->recv_window -= length;
struct proxy_client *pc = (struct proxy_client *)param;
if (!pc || (pc && !pc->local_proxy_bev)) {
uint8_t *data = (uint8_t *)calloc(length, 1);
ring_buffer_pop(&stream->rx_ring, data, length);
fn(data, length, pc);
free(data);
} else {
ring_buffer_write(pc->local_proxy_bev, &stream->rx_ring, length);
}
struct bufferevent *bout = get_main_control()->connect_bev;
send_window_update(bout, stream, length);
return length;
}
static int
incr_send_window(struct bufferevent *bev, struct tcp_mux_header *tmux_hdr, uint16_t flags, struct tmux_stream *stream)
{
if (!process_flags(flags, stream))
return 0;
uint32_t length = ntohl(tmux_hdr->length);
if (stream->send_window == 0) bufferevent_enable(bev, EV_READ);
stream->send_window += length;
//debug(LOG_DEBUG, "incr_send_window : stream_id %d length %d send_window %d",
// stream->id, length, stream->send_window);
return 1;
}
static int
incoming_stream(uint32_t stream_id)
{
if (local_go_away) {
struct bufferevent *bout = get_main_control()->connect_bev;
tcp_mux_send_win_update_rst(bout, stream_id);
return 0;
}
// TODO
// create new stream
return 1;
}
void
handle_tcp_mux_ping(struct tcp_mux_header *tmux_hdr)
{
uint16_t flags = ntohs(tmux_hdr->flags);
uint32_t ping_id = ntohl(tmux_hdr->length);
if ( (flags&SYN) == SYN) {
struct bufferevent *bout = get_main_control()->connect_bev;
tcp_mux_handle_ping(bout, ping_id);
}
}
void
handle_tcp_mux_go_away(struct tcp_mux_header *tmux_hdr)
{
uint32_t code = ntohl(tmux_hdr->length);
switch(code) {
case NORMAL:
remote_go_away = 1;
break;
case PROTO_ERR:
debug(LOG_ERR, "receive protocol error go away");
break;
case INTERNAL_ERR:
debug(LOG_ERR, "receive internal error go away");
break;
default:
debug(LOG_ERR, "receive unexpected go away");
}
}
uint32_t
tmux_stream_read(struct bufferevent *bev, struct tmux_stream *stream, uint32_t len)
{
assert(stream != NULL);
return ring_buffer_read(bev, &stream->rx_ring, len);
}
int
handle_tcp_mux_stream(struct tcp_mux_header *tmux_hdr, handle_data_fn_t fn)
{
uint32_t stream_id = ntohl(tmux_hdr->stream_id);
uint16_t flags = ntohs(tmux_hdr->flags);
//debug(LOG_DEBUG, "handle_tcp_mux_stream stream_id %d type %d flags %d", stream_id, tmux_hdr->type, flags);
if ( (flags&SYN) == SYN) {
debug(LOG_INFO, "!!!! as xfrpc, it should not be here %d", stream_id);
if (!incoming_stream(stream_id))
return 0;
}
struct tmux_stream *stream = get_stream_by_id(stream_id);
struct proxy_client *pc = get_proxy_client(stream_id);
assert(stream != NULL);
if (tmux_hdr->type == WINDOW_UPDATE) {
struct bufferevent *bev = pc?pc->local_proxy_bev: get_main_control()->connect_bev;
if (!incr_send_window(bev, tmux_hdr, flags, stream)) {
struct bufferevent *bout = get_main_control()->connect_bev;
tcp_mux_send_go_away(bout, PROTO_ERR);
}
return 0;
}
int32_t length = ntohl(tmux_hdr->length);
if (!process_data(stream, length, flags, fn, (void *)pc)) {
struct bufferevent *bout = get_main_control()->connect_bev;
tcp_mux_send_go_away(bout, PROTO_ERR);
return 0;
}
return length;
}
static int
ring_buffer_append(struct ring_buffer *ring, uint8_t *data, uint32_t len)
{
uint32_t left = RBUF_SIZE - ring->sz;
assert(left >= len);
int i = 0;
for (; i < len; i++) {
ring->data[ring->end++] = data[i];
if (ring->end == RBUF_SIZE) ring->end = 0;
ring->sz++;
if (ring->cur == ring->end) {
break;
}
}
return i;
}
static uint32_t
ring_buffer_read(struct bufferevent *bev, struct ring_buffer *ring, uint32_t len)
{
if (ring->sz == RBUF_SIZE) {
debug(LOG_ERR, "ring buffer is full");
return 0;
}
uint32_t cap = RBUF_SIZE - ring->sz;
if (len > cap) {
debug(LOG_INFO, "prepare read data [%d] out size ring capacity [%d]", len, cap);
len = cap;
}
for (int i = 0; i < len; i++) {
bufferevent_read(bev, &ring->data[ring->end++], 1);
if (ring->end == RBUF_SIZE) ring->end = 0;
ring->sz++;
if (ring->cur == ring->end) {
break;
}
}
return len;
}
static uint32_t
ring_buffer_write(struct bufferevent *bev, struct ring_buffer *ring, uint32_t len)
{
if (ring->sz == 0) {
debug(LOG_ERR, "ring buffer is empty");
return 0;
}
if (len > ring->sz) {
debug(LOG_INFO, "prepare write data [%d] out size ring data [%d]", len, ring->sz);
len = ring->sz;
}
while(len > 0) {
bufferevent_write(bev, &ring->data[ring->cur++], 1);
len--;
ring->sz--;;
if (ring->cur == RBUF_SIZE) ring->cur = 0;
if (ring->cur == ring->end) {
assert(ring->sz == 0);
break;
}
}
return len;
}
uint32_t
tmux_stream_write(struct bufferevent *bev, uint8_t *data, uint32_t length, struct tmux_stream *stream)
{
switch(stream->state) {
case LOCAL_CLOSE:
case CLOSED:
case RESET:
debug(LOG_INFO, "stream %d state is closed", stream->id);
return 0;
default:
break;
}
struct ring_buffer *tx_ring = &stream->tx_ring;
uint32_t left = RBUF_SIZE - tx_ring->sz;
if (stream->send_window == 0) {
debug(LOG_INFO, "stream %d send_window is zero, length %d left %d", stream->id, length, left);
ring_buffer_append(tx_ring, data, length);
return 0;
}
uint16_t flags = get_send_flags(stream);
uint32_t max = length;
struct bufferevent *bout = get_main_control()->connect_bev;
//debug(LOG_DEBUG, "tmux_stream_write stream id %u: send_window %u tx_ring sz %u length %u",
// stream->id, stream->send_window, tx_ring->sz, length);
if (stream->send_window < tx_ring->sz) {
debug(LOG_INFO, " send_window %u less than tx_ring size %u", stream->send_window, tx_ring->sz);
max = stream->send_window;
tcp_mux_send_data(bout, flags, stream->id, max);
ring_buffer_write(bev, tx_ring, max);
ring_buffer_append(tx_ring, data, length);
} else if (stream->send_window < tx_ring->sz + length) {
debug(LOG_INFO, " send_window %u less than %u", stream->send_window, tx_ring->sz+length);
max = stream->send_window;
tcp_mux_send_data(bout, flags, stream->id, max);
if (tx_ring->sz > 0)
ring_buffer_write(bev, tx_ring, tx_ring->sz);
bufferevent_write(bev, data, max - tx_ring->sz);
ring_buffer_append(tx_ring, data + max - tx_ring->sz, length + tx_ring->sz - max);
} else {
max = tx_ring->sz + length;
tcp_mux_send_data(bout, flags, stream->id, max);
if (tx_ring->sz > 0)
ring_buffer_write(bev, tx_ring, tx_ring->sz);
bufferevent_write(bev, data, length);
}
stream->send_window -= max;
return max;
}
void
tmux_stream_close(struct bufferevent *bout, struct tmux_stream *stream)
{
int closed = 0;
switch(stream->state) {
case SYN_SEND:
case SYN_RECEIVED:
case ESTABLISHED:
stream->state = LOCAL_CLOSE;
break;
case LOCAL_CLOSE:
case REMOTE_CLOSE:
stream->state = CLOSED;
closed = 1;
case CLOSED:
case RESET:
default:
return;
}
uint16_t flags = get_send_flags(stream);
flags |= FIN;
tcp_mux_send_win_update(bout, flags, stream->id, 0);
if (closed) {
debug(LOG_DEBUG, "del proxy client %d", stream->id);
del_proxy_client_by_stream_id(stream->id);
}
}

153
tcpmux.h Normal file
View File

@@ -0,0 +1,153 @@
/* 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 tcpmux.h
@brief xfrp tcp mux header file
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
#ifndef __TCP_MUX__
#define __TCP_MUX__
#include "uthash.h"
#define MAX_STREAM_WINDOW_SIZE 256*1024
#define RBUF_SIZE 32*1024
struct ring_buffer {
uint32_t cur;
uint32_t end;
uint32_t sz;
uint8_t data[RBUF_SIZE];
};
enum go_away_type {
NORMAL,
PROTO_ERR,
INTERNAL_ERR,
};
enum tcp_mux_type {
DATA,
WINDOW_UPDATE,
PING,
GO_AWAY,
};
struct tcp_mux_type_desc {
enum tcp_mux_type type;
char *desc;
};
enum tcp_mux_flag {
ZERO,
SYN,
ACK = 1<<1,
FIN = 1<<2,
RST = 1<<3,
};
struct __attribute__((__packed__)) tcp_mux_header {
uint8_t version;
uint8_t type;
uint16_t flags;
uint32_t stream_id;
uint32_t length;
};
struct tcp_mux_flag_desc {
enum tcp_mux_flag flag;
char *desc;
};
enum tcp_mux_state {
INIT = 0,
SYN_SEND,
SYN_RECEIVED,
ESTABLISHED,
LOCAL_CLOSE,
REMOTE_CLOSE,
CLOSED,
RESET
};
struct tmux_stream {
uint32_t id;
uint32_t recv_window;
uint32_t send_window;
enum tcp_mux_state state;
struct ring_buffer tx_ring;
struct ring_buffer rx_ring;
// private arguments
UT_hash_handle hh;
};
typedef void (*handle_data_fn_t)(uint8_t *, int, void *);
void init_tmux_stream(struct tmux_stream *stream, uint32_t id, enum tcp_mux_state state);
int validate_tcp_mux_protocol(struct tcp_mux_header *tmux_hdr);
void send_window_update(struct bufferevent *bout, struct tmux_stream *stream, uint32_t length);
void tcp_mux_send_win_update_syn(struct bufferevent *bout, uint32_t stream_id);
void tcp_mux_send_win_update_ack(struct bufferevent *bout, uint32_t stream_id, uint32_t delta);
void tcp_mux_send_win_update_fin(struct bufferevent *bout, uint32_t stream_id);
void tcp_mux_send_win_update_rst(struct bufferevent *bout, uint32_t stream_id);
void tcp_mux_send_data(struct bufferevent *bout, uint16_t flags, uint32_t stream_id, uint32_t length);
void tcp_mux_send_ping(struct bufferevent *bout, uint32_t ping_id);
uint32_t get_next_session_id();
void tcp_mux_encode(enum tcp_mux_type type, enum tcp_mux_flag flags,
uint32_t stream_id, uint32_t length, struct tcp_mux_header *tmux_hdr);
int handle_tcp_mux_stream(struct tcp_mux_header *tmux_hdr, handle_data_fn_t fn);
void handle_tcp_mux_ping(struct tcp_mux_header *tmux_hdr);
void handle_tcp_mux_go_away(struct tcp_mux_header *tmux_hdr);
uint32_t tmux_stream_write(struct bufferevent *bev, uint8_t *data, uint32_t length, struct tmux_stream *stream);
uint32_t tmux_stream_read(struct bufferevent *bev, struct tmux_stream *stream, uint32_t len);
void reset_session_id();
struct tmux_stream *get_cur_stream();
void set_cur_stream(struct tmux_stream *stream);
void add_stream(struct tmux_stream *stream);
void del_stream(uint32_t stream_id);
struct tmux_stream* get_stream_by_id(uint32_t id);
void tmux_stream_close(struct bufferevent *bout, struct tmux_stream *stream);
#endif

View File

@@ -1,8 +1,8 @@
#ifndef _VERSION_H_
#define _VERSION_H_
#define VERSION "1.41.1"
#define PROTOCOL_VERESION "0.41.0"
#define VERSION "1.11.587"
#define PROTOCOL_VERESION "0.43.0"
#define CLIENT_V 1
#endif //_VERSION_H_

View File

@@ -20,7 +20,7 @@
\********************************************************************/
/** @file xfrpc.c
@brief xfrp client
@brief xfrpc client
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/

View File

@@ -20,7 +20,7 @@
\********************************************************************/
/** @file xfrpc.h
@brief xfrp client header file
@brief xfrpc client header file
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
*/
@@ -29,4 +29,4 @@
void xfrpc_loop();
#endif //_XFRPC_H_
#endif //_XFRPC_H_

View File

@@ -1,7 +1,6 @@
[common]
server_addr = your_server_ip
server_port = 7000
token = 12345678
[ssh]
type = tcp