Drop the depend of libuwsc

Signed-off-by: Jianhui Zhao <zhaojh329@gmail.com>
This commit is contained in:
Jianhui Zhao
2020-01-17 01:05:48 +08:00
parent baf36e67d9
commit e6cdf0bae8
35 changed files with 2326 additions and 2269 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "src/buffer"]
path = src/buffer
url = https://github.com/zhaojh329/buffer.git

View File

@@ -5,8 +5,6 @@ os:
before_install:
- sudo apt-get install -y libev-dev libssl-dev
- git clone --recursive https://github.com/zhaojh329/libuwsc.git
- cd libuwsc && cmake . && sudo make install && cd -
script:
- mkdir build && cd build

View File

@@ -2,9 +2,9 @@
You first need to send a command to the server through POST, the message format is as follows.
{"devid": "test", "username": "test", "password": "test", "cmd": "echo", "params": ["hello rtty"], "env": {"x": "12"}}
{"devid": "test", "username": "test", "password": "test", "cmd": "echo", "params": ["hello rtty"]}
The devid, username, cmd in the message must be provided. Password, params, and env are optional. Params is a JSON array and env is a JSON object.
The devid, username, cmd in the message must be provided. Password, params are optional. Params is a JSON array.
Then the server returns a unique token.

View File

@@ -2,9 +2,9 @@
首先要通过POST方式向服务器发送一条命令,消息格式如下所示:
{"devid": "test", "username": "test", "password": "test", "cmd": "echo", "params": ["hello rtty"], "env": {"x": "12"}}
{"devid": "test", "username": "test", "password": "test", "cmd": "echo", "params": ["hello rtty"]}
其中devid、username、cmd必须提供。password、params和env为可选项。params为一个JSON数组env为一个JSON对象
其中devid、username、cmd必须提供。password、params为可选项。params为一个JSON数组。
然后服务器返回一个唯一的token

View File

@@ -5,13 +5,6 @@
./configure --host=arm-linux-gnueabi
DESTDIR=/tmp/rtty_install make install
# Build libuwsc
git clone --recursive https://github.com/zhaojh329/libuwsc.git
cd libuwsc
cmake . -DCMAKE_C_COMPILER=arm-linux-gnueabi-gcc -DCMAKE_FIND_ROOT_PATH=/tmp/rtty_install -DUWSC_SSL_SUPPORT=OFF
DESTDIR=/tmp/rtty_install make install
# Build rtty
git clone https://github.com/zhaojh329/rtty.git
@@ -30,5 +23,3 @@
├── libev.so -> libev.so.4.0.0
├── libev.so.4 -> libev.so.4.0.0
├── libev.so.4.0.0
├── libuwsc.so -> libuwsc.so.3.3.2
└── libuwsc.so.3.3.2

View File

@@ -6,7 +6,7 @@
[4]: https://github.com/zhaojh329/rtty/pulls
[5]: https://img.shields.io/badge/Issues-welcome-brightgreen.svg?style=plastic
[6]: https://github.com/zhaojh329/rtty/issues/new
[7]: https://img.shields.io/badge/release-6.6.1-blue.svg?style=plastic
[7]: https://img.shields.io/badge/release-7.0.0-blue.svg?style=plastic
[8]: https://github.com/zhaojh329/rtty/releases
[9]: https://travis-ci.org/zhaojh329/rtty.svg?branch=master
[10]: https://travis-ci.org/zhaojh329/rtty
@@ -18,9 +18,7 @@
[![Build Status][9]][10]
[Xterm.js]: https://github.com/xtermjs/xterm.js
[lrzsz]: https://ohse.de/uwe/software/lrzsz.html
[libev]: http://software.schmorp.de/pkg/libev.html
[libuwsc]: https://github.com/zhaojh329/libuwsc
[openssl]: https://github.com/openssl/openssl
[mbedtls(polarssl)]: https://github.com/ARMmbed/mbedtls
[CyaSSl(wolfssl)]: https://github.com/wolfSSL/wolfssl
@@ -49,12 +47,10 @@ the world.
* SSL support: openssl, mbedtls, CyaSSl(wolfssl)
* Support device authorization
* Support Execute a command remote
* The client is very small, suitable for embedded Linux: rtty(20.1K) + libev(48.5K) + libuwsc(24.4K) = 93K. If you want
to support ssl, +libwolfssl(595.9K) = 688.9K
* The client is very small, suitable for embedded Linux
# Dependencies of the Client side
* [libev] - A full-featured and high-performance event loop
* [libuwsc] - A Lightweight and fully asynchronous WebSocket client library based on libev
* [mbedtls(polarssl)], [CyaSSl(wolfssl)] or [openssl] - If you want to support SSL
# [Deploying the server side](https://github.com/zhaojh329/rttys)
@@ -68,18 +64,21 @@ Install
Command-line Options
Usage: rtty [option]
-I id # Set an ID for the device(Maximum 63 bytes, valid character:letter,
number, underline and short line)
-h host # Server's host or ipaddr
-p port # Server port(Default is 5912)
-a # Auto reconnect to the server
-v # verbose
-d # Adding a description to the device(Maximum 126 bytes)
-s # SSL on
-k keepalive # keep alive in seconds for this client. Defaults to 5
-V # Show version
-D # Run in the background
-t token # Authorization token
-I, --id=string Set an ID for the device(Maximum 63 bytes, valid
character:letter, number, underline and short line)
-h, --host=string Server's host or ipaddr(Default is localhost)
-p, --port=number Server port(Default is 5912)
-d, --description=string Adding a description to the device(Maximum 126 bytes)
-a Auto reconnect to the server
-s SSL on
-D Run in the background
-t, --token=string Authorization token
-f username Skip a second login authentication. See man login(1) about the details
-R Receive file
-S file Send file
-v, --verbose verbose
-V, --version Show version
--help Show usage
Run RTTY(Replace the following parameters with your own parameters)
@@ -94,11 +93,11 @@ If your rttys is configured with a token, add the following parameter(Replace th
## [For Other Embedded Linux Platform](/CROSS_COMPILE.md)
# Usage
Use your web browser to access your server: `https://your-server-host:5912`, then click the connection button
Use your web browser to access your server: `https://your-server-host:5913`, then click the connection button
You can easily embed RTTY into your existing platform: `https://your-server-host:5912/#/?id=your-id`
You can easily embed RTTY into your existing platform: `https://your-server-host:5913/#/?id=your-id`
Automatic login: `https://your-server:5912/#/?id=device-id&username=device-username&password=device-password`
Automatic login: `https://your-server:5913/#/?id=device-id&username=device-username&password=device-password`
## Transfer file
Transfer file from local to remote device

View File

@@ -6,7 +6,7 @@
[4]: https://github.com/zhaojh329/rtty/pulls
[5]: https://img.shields.io/badge/Issues-welcome-brightgreen.svg?style=plastic
[6]: https://github.com/zhaojh329/rtty/issues/new
[7]: https://img.shields.io/badge/release-6.6.1-blue.svg?style=plastic
[7]: https://img.shields.io/badge/release-7.0.0-blue.svg?style=plastic
[8]: https://github.com/zhaojh329/rtty/releases
[9]: https://travis-ci.org/zhaojh329/rtty.svg?branch=master
[10]: https://travis-ci.org/zhaojh329/rtty
@@ -18,9 +18,7 @@
[![Build Status][9]][10]
[Xterm.js]: https://github.com/xtermjs/xterm.js
[lrzsz]: https://ohse.de/uwe/software/lrzsz.html
[libev]: http://software.schmorp.de/pkg/libev.html
[libuwsc]: https://github.com/zhaojh329/libuwsc
[openssl]: https://github.com/openssl/openssl
[mbedtls(polarssl)]: https://github.com/ARMmbed/mbedtls
[CyaSSl(wolfssl)]: https://github.com/wolfSSL/wolfssl
@@ -47,11 +45,10 @@ rtty非常适合远程维护你的或者你公司的部署在全球各地的成
* 支持SSL: openssl, mbedtls, CyaSSl(wolfssl)
* 支持设备认证
* 支持远程执行命令
* 客户端非常小适合嵌入式Linux: rtty(20.1K) + libev(48.5K) + libuwsc(24.4K) = 93K. 如果你希望支持SSL+libwolfssl(595.9K) = 688.9K
* 客户端非常小适合嵌入式Linux
# 客户端依赖
* [libev] - 高性能的事件循环库
* [libuwsc] - 一个轻量的针对嵌入式Linux的基于libev的WebSocket客户端C库。
* [mbedtls(polarssl)]、[CyaSSl(wolfssl)]或者[openssl] - 如果你需要支持SSL
# [部署服务端](https://github.com/zhaojh329/rttys/blob/master/README_ZH.md)
@@ -65,18 +62,21 @@ rtty非常适合远程维护你的或者你公司的部署在全球各地的成
查看命令行选项
Usage: rtty [option]
-I id # Set an ID for the device(Maximum 63 bytes, valid character:letter,
number, underline and short line)
-h host # Server's host or ipaddr
-p port # Server port(Default is 5912)
-a # Auto reconnect to the server
-v # verbose
-d # Adding a description to the device(Maximum 126 bytes)
-s # SSL on
-k keepalive # keep alive in seconds for this client. Defaults to 5
-V # Show version
-D # Run in the background
-t token # Authorization token
-I, --id=string Set an ID for the device(Maximum 63 bytes, valid
character:letter, number, underline and short line)
-h, --host=string Server's host or ipaddr(Default is localhost)
-p, --port=number Server port(Default is 5912)
-d, --description=string Adding a description to the device(Maximum 126 bytes)
-a Auto reconnect to the server
-s SSL on
-D Run in the background
-t, --token=string Authorization token
-f username Skip a second login authentication. See man login(1) about the details
-R Receive file
-S file Send file
-v, --verbose verbose
-V, --version Show version
--help Show usage
运行RTTY(将下面的参数替换为你自己的参数)
@@ -91,11 +91,11 @@ rtty非常适合远程维护你的或者你公司的部署在全球各地的成
## [其它嵌入式Linux平台](/CROSS_COMPILE.md)
# 如何使用
使用你的Web浏览器访问你的服务器: `https://your-server-host:5912`,然后点击连接按钮。
使用你的Web浏览器访问你的服务器: `https://your-server-host:5913`,然后点击连接按钮。
你可以非常方便的将RTTY嵌入到你现有的平台 `https://your-server-host:5912/#/?id=your-id`
你可以非常方便的将RTTY嵌入到你现有的平台 `https://your-server-host:5913/#/?id=your-id`
自动登录: `https://your-server:5912/#/?id=device-id&username=device-username&password=device-password`
自动登录: `https://your-server:5913/#/?id=device-id&username=device-username&password=device-password`
## 传输文件
从本地传输文件到远程设备

View File

@@ -1,29 +0,0 @@
# - Try to find libuwsc
# Once done this will define
# LIBUWSC_FOUND - System has libuwsc
# LIBUWSC_INCLUDE_DIR - The libuwsc include directories
# LIBUWSC_LIBRARY - The libraries needed to use libuwsc
find_path(LIBUWSC_INCLUDE_DIR uwsc)
find_library(LIBUWSC_LIBRARY uwsc PATH_SUFFIXES lib64)
if(LIBUWSC_INCLUDE_DIR)
file(STRINGS "${LIBUWSC_INCLUDE_DIR}/uwsc/config.h"
LIBUWSC_VERSION_MAJOR REGEX "^#define[ \t]+UWSC_VERSION_MAJOR[ \t]+[0-9]+")
file(STRINGS "${LIBUWSC_INCLUDE_DIR}/uwsc/config.h"
LIBUWSC_VERSION_MINOR REGEX "^#define[ \t]+UWSC_VERSION_MINOR[ \t]+[0-9]+")
string(REGEX REPLACE "[^0-9]+" "" LIBUWSC_VERSION_MAJOR "${LIBUWSC_VERSION_MAJOR}")
string(REGEX REPLACE "[^0-9]+" "" LIBUWSC_VERSION_MINOR "${LIBUWSC_VERSION_MINOR}")
set(LIBUWSC_VERSION "${LIBUWSC_VERSION_MAJOR}.${LIBUWSC_VERSION_MINOR}")
unset(LIBUWSC_VERSION_MINOR)
unset(LIBUWSC_VERSION_MAJOR)
endif()
include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set LIBUWSC_FOUND to TRUE
# if all listed variables are TRUE and the requested version matches.
find_package_handle_standard_args(Libuwsc REQUIRED_VARS
LIBUWSC_LIBRARY LIBUWSC_INCLUDE_DIR
VERSION_VAR LIBUWSC_VERSION)
mark_as_advanced(LIBUWSC_INCLUDE_DIR LIBUWSC_LIBRARY)

View File

@@ -0,0 +1,37 @@
find_path(MBEDTLS_INCLUDE_DIR mbedtls/ssl.h)
find_library(MBEDTLS_LIBRARY mbedtls)
find_library(MBEDX509_LIBRARY mbedx509)
find_library(MBEDCRYPTO_LIBRARY mbedcrypto)
if(MBEDTLS_INCLUDE_DIR)
file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h"
MBEDTLS_VERSION_MAJOR REGEX "^#define[ \t]+MBEDTLS_VERSION_MAJOR[ \t]+[0-9]+")
file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h"
MBEDTLS_VERSION_MINOR REGEX "^#define[ \t]+MBEDTLS_VERSION_MINOR[ \t]+[0-9]+")
file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h"
MBEDTLS_VERSION_PATCH REGEX "^#define[ \t]+MBEDTLS_VERSION_PATCH[ \t]+[0-9]+")
string(REGEX REPLACE "[^0-9]+" "" MBEDTLS_VERSION_MAJOR "${MBEDTLS_VERSION_MAJOR}")
string(REGEX REPLACE "[^0-9]+" "" MBEDTLS_VERSION_MINOR "${MBEDTLS_VERSION_MINOR}")
string(REGEX REPLACE "[^0-9]+" "" MBEDTLS_VERSION_PATCH "${MBEDTLS_VERSION_PATCH}")
set(MBEDTLS_VERSION "${MBEDTLS_VERSION_MAJOR}.${MBEDTLS_VERSION_MINOR}.${MBEDTLS_VERSION_PATCH}")
unset(MBEDTLS_VERSION_MINOR)
unset(MBEDTLS_VERSION_MAJOR)
unset(MBEDTLS_VERSION_PATCH)
endif()
set(MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARY}" "${MBEDX509_LIBRARY}" "${MBEDCRYPTO_LIBRARY}")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MbedTLS
REQUIRED_VARS
MBEDTLS_LIBRARY
MBEDX509_LIBRARY
MBEDCRYPTO_LIBRARY
MBEDTLS_INCLUDE_DIR
VERSION_VAR
MBEDTLS_VERSION
)
mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)

View File

@@ -0,0 +1,62 @@
if(WOLFSSL_PREFER_STATIC_LIB)
set(WOLFSSL_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
if(WIN32)
set(CMAKE_FIND_LIBRARY_SUFFIXES .a .lib ${CMAKE_FIND_LIBRARY_SUFFIXES})
else()
set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
endif()
endif()
if(UNIX)
find_package(PkgConfig QUIET)
pkg_check_modules(_WOLFSSL QUIET wolfssl)
endif()
find_path(WOLFSSL_INCLUDE_DIR NAMES wolfssl/version.h HINTS ${_WOLFSSL_INCLUDEDIR})
find_library(WOLFSSL_LIBRARY NAMES wolfssl HINTS ${_WOLFSSL_LIBDIR})
if(WOLFSSL_INCLUDE_DIR AND WOLFSSL_LIBRARY)
set(WOLFSSL_INCLUDE_DIR ${WOLFSSL_INCLUDE_DIR})
set(WOLFSSL_LIBRARY ${WOLFSSL_LIBRARY})
set(WOLFSSL_VERSION ${_WOLFSSL_VERSION})
set(WOLFSSL_IS_WOLFSSL ON)
else()
if(UNIX)
pkg_check_modules(_WOLFSSL QUIET WOLFSSL)
endif()
find_path(WOLFSSL_INCLUDE_DIR NAMES WOLFSSL/version.h HINTS ${_WOLFSSL_INCLUDEDIR})
find_library(WOLFSSL_LIBRARY NAMES WOLFSSL HINTS ${_WOLFSSL_LIBDIR})
set(WOLFSSL_VERSION ${_WOLFSSL_VERSION})
set(WOLFSSL_IS_WOLFSSL OFF)
endif()
if(NOT WOLFSSL_VERSION AND WOLFSSL_INCLUDE_DIR)
if(WOLFSSL_IS_WOLFSSL)
file(STRINGS "${WOLFSSL_INCLUDE_DIR}/wolfssl/version.h" WOLFSSL_VERSION_STR REGEX "^#define[\t ]+LIBWOLFSSL_VERSION_STRING[\t ]+\"[^\"]+\"")
else()
file(STRINGS "${WOLFSSL_INCLUDE_DIR}/WOLFSSL/version.h" WOLFSSL_VERSION_STR REGEX "^#define[\t ]+LIBWOLFSSL_VERSION_STRING[\t ]+\"[^\"]+\"")
endif()
if(WOLFSSL_VERSION_STR MATCHES "\"([^\"]+)\"")
set(WOLFSSL_VERSION "${CMAKE_MATCH_1}")
endif()
endif()
set(WOLFSSL_INCLUDE_DIRS ${WOLFSSL_INCLUDE_DIR})
set(WOLFSSL_LIBRARIES ${WOLFSSL_LIBRARY})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(WOLFSSL
REQUIRED_VARS
WOLFSSL_LIBRARY
WOLFSSL_INCLUDE_DIR
VERSION_VAR
WOLFSSL_VERSION
)
mark_as_advanced(WOLFSSL_INCLUDE_DIR WOLFSSL_LIBRARY WOLFSSL_INCLUDE_DIR WOLFSSL_LIBRARY)
if(WOLFSSL_PREFER_STATIC_LIB)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${WOLFSSL_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
unset(WOLFSSL_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES)
endif()

View File

@@ -1,18 +1,93 @@
add_definitions(-O -Wall -Werror --std=gnu99 -D_GNU_SOURCE)
# The version number.
set(RTTY_VERSION_MAJOR 6)
set(RTTY_VERSION_MINOR 6)
set(RTTY_VERSION_PATCH 1)
set(RTTY_VERSION_MAJOR 7)
set(RTTY_VERSION_MINOR 0)
set(RTTY_VERSION_PATCH 0)
# Check the third party Libraries
find_package(Libev REQUIRED)
find_package(Libuwsc 3.2 REQUIRED)
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${LIBUWSC_INCLUDE_DIR} ${LIBEV_INCLUDE_DIR})
set(EXTRA_LIBS ${LIBUWSC_LIBRARY} ${LIBEV_LIBRARY} util crypt m)
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/buffer ${LIBEV_INCLUDE_DIR})
set(EXTRA_LIBS ${LIBEV_LIBRARY} util crypt m)
add_executable(rtty main.c utils.c json.c command.c file.c)
set(RTTY_FILE_UNIX_SOCKET "/var/run/rttyfile.sock")
add_definitions(-DRTTY_FILE_UNIX_SOCKET="${RTTY_FILE_UNIX_SOCKET}")
set(RTTY_SSL_SUPPORT_CONFIG 1)
option(RTTY_SSL_SUPPORT "SSL support" ON)
option(RTTY_USE_OPENSSL "Force select OpenSSL" OFF)
option(RTTY_USE_WOLFSSL "Force select WolfSSL(CyaSSL)" OFF)
option(RTTY_USE_MBEDTLS "Force select MbedTLS(PolarSSL)" OFF)
set(SSL_NAME OFF)
set(RTTY_HAVE_OPENSSL_CONFIG 0)
set(RTTY_HAVE_WOLFSSL_CONFIG 0)
set(RTTY_HAVE_MBEDTLS_CONFIG 0)
if(NOT RTTY_SSL_SUPPORT)
set(RTTY_SSL_SUPPORT_CONFIG 0)
else()
find_package(OpenSSL)
find_package(WolfSSL)
find_package(MbedTLS)
if(RTTY_USE_OPENSSL)
if (NOT OPENSSL_FOUND)
set(RTTY_SSL_SUPPORT OFF)
message(WARNING "Force select OpenSSL, but not found it")
endif()
elseif(RTTY_USE_WOLFSSL)
if (NOT WOLFSSL_FOUND)
set(RTTY_SSL_SUPPORT OFF)
message(WARNING "Force select WolfSSL(CyaSSL), but not found it")
endif()
elseif(RTTY_USE_MBEDTLS)
if (NOT MBEDTLS_FOUND)
set(RTTY_SSL_SUPPORT OFF)
message(WARNING "Force select MbedTLS(PolarSSL), but not found it")
endif()
elseif(OPENSSL_FOUND)
set(RTTY_USE_OPENSSL ON)
elseif(WOLFSSL_FOUND)
set(RTTY_USE_WOLFSSL ON)
elseif(MBEDTLS_FOUND)
set(RTTY_USE_MBEDTLS ON)
else()
set(RTTY_SSL_SUPPORT OFF)
message(WARNING "No available SSL libraries found")
endif()
if(RTTY_USE_OPENSSL)
set(SSL_NAME "OpenSSL")
set(SSL_INC ${OPENSSL_INCLUDE_DIR})
set(SSL_LIB ${OPENSSL_LIBRARIES})
set(RTTY_HAVE_OPENSSL_CONFIG 1)
elseif(RTTY_USE_WOLFSSL)
set(SSL_NAME "WolfSSL(CyaSSL)")
set(SSL_INC ${WOLFSSL_INCLUDE_DIR})
set(SSL_LIB ${WOLFSSL_LIBRARIES})
set(RTTY_HAVE_WOLFSSL_CONFIG 1)
elseif(RTTY_USE_MBEDTLS)
set(SSL_NAME "MbedTLS(PolarSSL)")
set(SSL_INC ${MBEDTLS_INCLUDE_DIR})
set(SSL_LIB ${MBEDTLS_LIBRARIES})
set(RTTY_HAVE_MBEDTLS_CONFIG 1)
endif()
if(RTTY_SSL_SUPPORT)
include_directories(${SSL_INC})
list(APPEND EXTRA_LIBS ${SSL_LIB})
message(STATUS "Select ${SSL_NAME} as the SSL backend")
else()
set(SSL_NAME OFF)
set(RTTY_SSL_SUPPORT_CONFIG 0)
endif()
endif()
add_executable(rtty main.c utils.c buffer/buffer.c log.c net.c net.h rtty.c rtty.h command.c
file.c upfile.c upfile.h downfile.c downfile.h ssl.c ssl.h)
target_link_libraries(rtty ${EXTRA_LIBS})
# configure a header file to pass some of the CMake settings to the source code

1
src/buffer Submodule

Submodule src/buffer added at 74d566b6c4

View File

@@ -31,11 +31,9 @@
#include <limits.h>
#include <shadow.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <uwsc/log.h>
#include <uwsc/utils.h>
#include <math.h>
#include "log.h"
#include "list.h"
#include "utils.h"
#include "command.h"
@@ -121,55 +119,61 @@ static const char *cmderr2str(int err)
static void task_free(struct task *t)
{
int i;
/* stdout watcher */
if (t->ioo.fd > 0) {
close(t->ioo.fd);
ev_io_stop(t->ws->loop, &t->ioo);
ev_io_stop(t->rtty->loop, &t->ioo);
}
/* stderr watcher */
if (t->ioe.fd > 0) {
close(t->ioe.fd);
ev_io_stop(t->ws->loop, &t->ioe);
ev_io_stop(t->rtty->loop, &t->ioe);
}
ev_child_stop(t->ws->loop, &t->cw);
ev_timer_stop(t->ws->loop, &t->timer);
ev_child_stop(t->rtty->loop, &t->cw);
ev_timer_stop(t->rtty->loop, &t->timer);
buffer_free(&t->ob);
buffer_free(&t->eb);
json_value_free((json_value *)t->msg);
for (i = 0; i < t->nparams; i++)
free(t->params[i]);
free(t->params);
free(t);
}
static void cmd_err_reply(struct uwsc_client *ws, const char *token, int err)
static void cmd_err_reply(struct rtty *rtty, const char *token, int err)
{
char str[256] = "";
snprintf(str, sizeof(str) - 1, "{\"type\":\"cmd\",\"token\":\"%s\","
snprintf(str, sizeof(str) - 1, "{\"token\":\"%s\","
"\"attrs\":{\"err\":%d,\"msg\":\"%s\"}}", token, err, cmderr2str(err));
ws->send(ws, str, strlen(str), UWSC_OP_TEXT);
rtty_send_msg(rtty, MSG_TYPE_CMD, str, strlen(str));
}
static void cmd_reply(struct task *t, int code)
{
size_t len = buffer_length(&t->ob) + buffer_length(&t->eb);
int ret;
struct rtty *rtty = t->rtty;
char *str, *pos;
int ret;
len = ceil(len * 4.0 / 3) + 200;
str = calloc(1, len);
if (!str) {
cmd_err_reply(t->ws, t->token, RTTY_CMD_ERR_NOMEM);
cmd_err_reply(t->rtty, t->token, RTTY_CMD_ERR_NOMEM);
return;
}
pos = str;
ret = snprintf(pos, len, "{\"type\":\"cmd\",\"token\":\"%s\","
ret = snprintf(pos, len, "{\"token\":\"%s\","
"\"attrs\":{\"code\":%d,\"stdout\":\"", t->token, code);
len -= ret;
@@ -188,10 +192,10 @@ static void cmd_reply(struct task *t, int code)
pos += ret;
ret = snprintf(pos, len, "\"}}");
len -= ret;
pos += ret;
t->ws->send(t->ws, str, pos - str, UWSC_OP_TEXT);
rtty_send_msg(rtty, MSG_TYPE_CMD, str, pos - str);
free(str);
}
@@ -221,7 +225,7 @@ static void ev_timer_cb(struct ev_loop *loop, struct ev_timer *w, int revents)
task_free(t);
nrunning--;
uwsc_log_err("exec '%s' timeout\n", t->cmd);
log_err("exec '%s' timeout\n", t->cmd);
}
static void ev_io_stdout_cb(struct ev_loop *loop, struct ev_io *w, int revents)
@@ -249,7 +253,7 @@ static void run_task(struct task *t)
if (pipe2(opipe, O_CLOEXEC | O_NONBLOCK) < 0 ||
pipe2(epipe, O_CLOEXEC | O_NONBLOCK) < 0) {
uwsc_log_err("pipe2 failed: %s\n", strerror(errno));
log_err("pipe2 failed: %s\n", strerror(errno));
err = RTTY_CMD_ERR_SYSERR;
goto ERR;
}
@@ -257,15 +261,14 @@ static void run_task(struct task *t)
pid = fork();
switch (pid) {
case -1:
uwsc_log_err("fork: %s\n", strerror(errno));
log_err("fork: %s\n", strerror(errno));
err = RTTY_CMD_ERR_SYSERR;
goto ERR;
case 0: {
const json_value *params = json_get_value(t->attrs, "params");
const json_value *env = json_get_value(t->attrs, "env");
int i, arglen;
int arglen = 2 + t->nparams;
char **args;
int i;
/* Close unused read end */
close(opipe[0]);
@@ -277,30 +280,14 @@ static void run_task(struct task *t)
close(opipe[1]);
close(epipe[1]);
arglen = 2;
if (params)
arglen += params->u.array.length;
args = calloc(1, sizeof(char *) * arglen);
if (!args)
exit(1);
args[0] = t->cmd;
if (params) {
for (i = 0; i < params->u.array.length; i++)
args[i + 1] = (char *)json_get_array_string(params, i);
}
if (env) {
if (env->type == json_object) {
for (i = 0; i < env->u.object.length; i++) {
json_value *v = env->u.object.values[i].value;
if (v->type == json_string)
setenv(env->u.object.values[i].name, v->u.string.ptr, 1);
}
}
}
for (i = 0; i < t->nparams; i++)
args[i + 1] = t->params[i];
execv(t->cmd, args);
}
@@ -311,40 +298,46 @@ static void run_task(struct task *t)
/* Watch child's status */
ev_child_init(&t->cw, ev_child_exit, pid, 0);
ev_child_start(t->ws->loop, &t->cw);
ev_child_start(t->rtty->loop, &t->cw);
ev_io_init(&t->ioo, ev_io_stdout_cb, opipe[0], EV_READ);
ev_io_start(t->ws->loop, &t->ioo);
ev_io_start(t->rtty->loop, &t->ioo);
ev_io_init(&t->ioe, ev_io_stderr_cb, epipe[0], EV_READ);
ev_io_start(t->ws->loop, &t->ioe);
ev_io_start(t->rtty->loop, &t->ioe);
ev_timer_init(&t->timer, ev_timer_cb, RTTY_CMD_EXEC_TIMEOUT, 0);
ev_timer_start(t->ws->loop, &t->timer);
ev_timer_start(t->rtty->loop, &t->timer);
nrunning++;
return;
}
ERR:
cmd_err_reply(t->ws, t->token, err);
cmd_err_reply(t->rtty, t->token, err);
task_free(t);
}
static void add_task(struct uwsc_client *ws, const char *token, const char *cmd,
const json_value *msg, const json_value *attrs)
static void add_task(struct rtty *rtty, const char *token, const char *cmd, const char *data)
{
struct task *t;
int i;
t = calloc(1, sizeof(struct task) + strlen(cmd) + 1);
if (!t) {
cmd_err_reply(ws, token, RTTY_CMD_ERR_NOMEM);
cmd_err_reply(rtty, token, RTTY_CMD_ERR_NOMEM);
return;
}
t->ws = ws;
t->msg = msg;
t->attrs = attrs;
t->rtty = rtty;
t->nparams = *data++;
t->params = calloc(t->nparams, sizeof(char *));
for (i = 0; i < t->nparams; i++) {
t->params[i] = strdup(data);
data += strlen(t->params[i]) + 1;
}
strcpy(t->cmd, cmd);
strcpy(t->token, token);
@@ -355,30 +348,30 @@ static void add_task(struct uwsc_client *ws, const char *token, const char *cmd,
list_add_tail(&t->list, &task_pending);
}
void run_command(struct uwsc_client *ws, const json_value *msg)
void run_command(struct rtty *rtty, const char *data)
{
const json_value *attrs = json_get_value(msg, "attrs");
const char *username = json_get_string(attrs, "username");
const char *password = json_get_string(attrs, "password");
const char *token = json_get_string(msg, "token");
const char *cmd;
const char *username = data;
const char *password = username + strlen(username) + 1;
const char *cmd = password + strlen(password) + 1;
const char *token = cmd + strlen(cmd) + 1;
int err = 0;
if (!username || !username[0] || !login_test(username, password)) {
data = token + strlen(token) + 1;
if (!username[0] || !login_test(username, password)) {
err = RTTY_CMD_ERR_PERMIT;
goto ERR;
}
cmd = cmd_lookup(json_get_string(attrs, "cmd"));
cmd = cmd_lookup(cmd);
if (!cmd) {
err = RTTY_CMD_ERR_NOT_FOUND;
goto ERR;
}
add_task(ws, token, cmd, msg, attrs);
add_task(rtty, token, cmd, data);
return;
ERR:
cmd_err_reply(ws, token, err);
json_value_free((json_value *)msg);
cmd_err_reply(rtty, token, err);
}

View File

@@ -25,9 +25,7 @@
#ifndef _COMMAND_H
#define _COMMAND_H
#include <uwsc/uwsc.h>
#include "json.h"
#include "rtty.h"
#define RTTY_CMD_MAX_RUNNING 5
#define RTTY_CMD_EXEC_TIMEOUT 30
@@ -42,19 +40,19 @@ enum {
struct task {
struct list_head list;
struct uwsc_client *ws;
struct rtty *rtty;
struct ev_child cw;
struct ev_timer timer;
struct ev_io ioo; /* Watch stdout of child */
struct ev_io ioe; /* Watch stderr of child */
struct buffer ob; /* buffer for stdout */
struct buffer eb; /* buffer for stderr */
const json_value *msg; /* message from server */
const json_value *attrs;
int nparams;
char **params;
char token[33];
char cmd[0];
};
void run_command(struct uwsc_client *ws, const json_value *msg);
void run_command(struct rtty *rtty, const char *data);
#endif

View File

@@ -30,4 +30,10 @@
#define RTTY_VERSION_PATCH @RTTY_VERSION_PATCH@
#define RTTY_VERSION_STRING "@RTTY_VERSION_MAJOR@.@RTTY_VERSION_MINOR@.@RTTY_VERSION_PATCH@"
#define RTTY_SSL_SUPPORT @RTTY_SSL_SUPPORT_CONFIG@
#define RTTY_HAVE_OPENSSL @RTTY_HAVE_OPENSSL_CONFIG@
#define RTTY_HAVE_WOLFSSL @RTTY_HAVE_WOLFSSL_CONFIG@
#define RTTY_HAVE_MBEDTLS @RTTY_HAVE_MBEDTLS_CONFIG@
#endif

198
src/downfile.c Normal file
View File

@@ -0,0 +1,198 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdbool.h>
#include <netinet/in.h>
#include "file.h"
#include "utils.h"
static struct buffer rb;
static struct buffer wb;
static uint32_t remain;
static uint32_t file_size;
static ev_tstamp start_time;
static struct ev_io iow;
static struct ev_io ior;
static struct ev_signal sw;
static char *file_name;
static bool canceled;
static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents)
{
canceled = true;
ev_signal_stop (loop, w);
}
static void on_file_write(struct ev_loop *loop, struct ev_io *w, int revents)
{
int ret = buffer_pull_to_fd (&wb, w->fd, -1, NULL, NULL);
if (ret < 0) {
fprintf(stderr,"socket write error: %s\n", strerror(errno));
return;
}
remain -= ret;
fprintf (stdout, "%100c\r", ' ');
fprintf(stdout," %lu%% %s %.3lfs\r",
(file_size - remain) * 100UL / file_size,
format_size(file_size - remain), ev_now(loop) - start_time);
fflush (stdout);
if (buffer_length (&wb) < 1)
ev_io_stop (loop, w);
}
static void start_write_file(struct ev_loop *loop, int msglen)
{
int fd;
remain = file_size = ntohl(buffer_pull_u32 (&rb));
file_name = strndup (buffer_data (&rb), msglen - 4);
buffer_pull (&rb, NULL, msglen - 4);
printf ("Transferring '%s'...\n", file_name);
fd = open (file_name, O_WRONLY | O_TRUNC | O_CREAT, 0644);
if (fd < 0) {
printf ("%s\n", strerror (errno));
return;
}
ev_io_init(&iow, on_file_write, fd, EV_WRITE);
}
static void parse_msg(struct ev_loop *loop)
{
int type;
int len;
while (true) {
if (buffer_length (&rb) < 3)
return;
len = buffer_get_u16 (&rb, 1);
if (buffer_length (&rb) < len + 3)
return;
type = buffer_pull_u8 (&rb);
buffer_pull (&rb, NULL, 2);
switch (type) {
case RTTY_FILE_MSG_INFO:
start_write_file(loop, len);
break;
case RTTY_FILE_MSG_DATA:
if (len == 0) {
ev_io_stop (loop, &ior);
ev_signal_stop (loop, &sw);
return;
}
buffer_put_data (&wb, buffer_data (&rb), len);
buffer_pull (&rb, NULL, len);
ev_io_start (loop, &iow);
break;
case RTTY_FILE_MSG_CANCELED:
canceled = true;
ev_break (loop, EVBREAK_ALL);
break;
default:
break;
}
}
}
static void on_net_read(struct ev_loop *loop, struct ev_io *w, int revents)
{
bool eof;
int ret;
if (canceled) {
ev_io_stop (loop, w);
return;
}
ret = buffer_put_fd(&rb, w->fd, -1, &eof, NULL, NULL);
if (ret < 0) {
fprintf (stderr, "socket read error: %s\n", strerror (errno));
return;
}
parse_msg(loop);
if (eof) {
printf("busy\n");
ev_break (loop, EVBREAK_ALL);
}
}
void download_file()
{
struct ev_loop *loop = EV_DEFAULT;
int sock = -1;
sock = connect_rtty_file_service();
if (sock < 0)
goto done;
detect_sid('d');
ev_signal_init(&sw, signal_cb, SIGINT);
ev_signal_start(loop, &sw);
ev_io_init(&ior, on_net_read, sock, EV_READ);
ev_io_start (loop, &ior);
start_time = ev_now (loop);
printf("Waiting to receive. Press Ctrl+C to cancel\n");
ev_run(loop, 0);
puts("");
if (iow.fd > 0) {
close (iow.fd);
if (canceled) {
unlink (file_name);
buffer_free(&wb);
buffer_put_u8 (&wb, RTTY_FILE_MSG_CANCELED);
buffer_put_u16 (&wb, 0);
buffer_pull_to_fd (&wb, sock, -1, NULL, NULL);
}
}
done:
if (sock > -1)
close(sock);
}

30
src/downfile.h Normal file
View File

@@ -0,0 +1,30 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef RTTY_DOWNFILE_H_
#define RTTY_DOWNFILE_H_
void download_file();
#endif

View File

@@ -22,278 +22,227 @@
* SOFTWARE.
*/
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <termios.h>
#include <stdbool.h>
#include <arpa/inet.h>
#include <uwsc/log.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "log.h"
#include "file.h"
#include "utils.h"
static void set_stdin(bool raw)
static uint8_t RTTY_FILE_MAGIC[] = {0xb6, 0xbc, 0xbd};
struct rtty_file_context file_context;
static void clear_file_context()
{
static struct termios ots;
static bool current_raw;
struct termios nts;
file_context.running = false;
close(file_context.sock);
if (raw) {
if (current_raw)
return;
buffer_free (&file_context.rb);
buffer_free (&file_context.wb);
current_raw = true;
tcgetattr(STDIN_FILENO, &ots);
nts = ots;
nts.c_iflag = IGNBRK;
/* No echo, crlf mapping, INTR, QUIT, delays, no erase/kill */
nts.c_lflag &= ~(ECHO | ICANON | ISIG);
nts.c_oflag = 0;
nts.c_cc[VMIN] = 1;
nts.c_cc[VTIME] = 1;
tcsetattr(STDIN_FILENO, TCSADRAIN, &nts);
fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) | O_NONBLOCK);
} else {
if (!current_raw)
return;
current_raw = false;
tcsetattr(STDIN_FILENO, TCSADRAIN, &ots);
fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) & ~O_NONBLOCK);
}
ev_io_stop(file_context.rtty->loop, &file_context.ior);
ev_io_stop(file_context.rtty->loop, &file_context.iow);
}
static void rf_write(int fd, void *buf, int len)
static void on_file_msg_read(struct ev_loop *loop, struct ev_io *w, int revents)
{
if (write(fd, buf, len) < 0) {
uwsc_log_err("Write failed: %s\n", strerror(errno));
exit(0);
struct buffer *rb = &file_context.rb;
struct rtty *rtty = file_context.rtty;
struct buffer *wb = &rtty->wb;
int msglen;
bool eof;
int ret;
ret = buffer_put_fd(rb, w->fd, -1, &eof, NULL, NULL);
if (ret < 0) {
log_err("socket read error: %s\n", strerror (errno));
return;
}
while (true) {
if (buffer_length (rb) < 3)
break;
msglen = buffer_get_u16 (rb, 1);
if (buffer_length (rb) < msglen + 3)
break;
buffer_put_u8 (wb, MSG_TYPE_FILE);
buffer_put_u16 (wb, htons(2 + msglen));
buffer_put_u8 (wb, file_context.sid);
buffer_put_u8 (wb, buffer_pull_u8 (rb));
buffer_pull (rb, NULL, 2);
buffer_put_data (wb, buffer_data (rb), msglen);
buffer_pull (rb, NULL, msglen);
ev_io_start(loop, &rtty->iow);
}
if (eof)
clear_file_context ();
}
static bool parse_file_info(struct transfer_context *tc)
static void on_file_msg_write(struct ev_loop *loop, struct ev_io *w, int revents)
{
struct buffer *b = &tc->b;
int len;
struct buffer *wb = &file_context.wb;
int ret = buffer_pull_to_fd (wb, w->fd, -1, NULL, NULL);
if (ret < 0) {
log_err ("socket write error: %s\n", strerror(errno));
clear_file_context ();
return;
}
if (buffer_length(b) < 3)
if (buffer_length (wb) < 1)
ev_io_stop (loop, w);
}
static void on_net_accept(struct ev_loop *loop, struct ev_io *w, int revents)
{
int sock = accept(w->fd, NULL, NULL);
if (sock < 0) {
log_err("accept fail: %s\n", strerror(errno));
return;
}
if (file_context.running) {
log_err("up/down file busy\n");
close(sock);
return;
}
fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK);
file_context.sock = sock;
file_context.running = true;
ev_io_init(&file_context.ior, on_file_msg_read, sock, EV_READ);
ev_io_start (loop, &file_context.ior);
ev_io_init(&file_context.iow, on_file_msg_write, sock, EV_WRITE);
}
int start_file_service(struct rtty *rtty)
{
struct sockaddr_un sun = {
.sun_family = AF_UNIX
};
const int on = 1;
int sock;
if (strlen(RTTY_FILE_UNIX_SOCKET) >= sizeof(sun.sun_path)) {
log_err("unix socket path too long\n");
return -1;
}
sock = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (sock < 0) {
log_err("create socket fail: %s\n", strerror(errno));
return -1;
}
strcpy(sun.sun_path, RTTY_FILE_UNIX_SOCKET);
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
unlink(RTTY_FILE_UNIX_SOCKET);
if (bind(sock, (struct sockaddr*)&sun, sizeof(sun)) < 0) {
log_err("bind socket fail: %s\n", strerror(errno));
goto err;
}
if (listen(sock, SOMAXCONN) < 0) {
log_err("listen socket fail: %s\n", strerror(errno));
goto err;
}
ev_io_init(&rtty->iof, on_net_accept, sock, EV_READ);
ev_io_start (rtty->loop, &rtty->iof);
file_context.rtty = rtty;
return sock;
err:
if (sock > -1)
close (sock);
return -1;
}
bool detect_file_msg(uint8_t *buf, int len, int sid, int *type)
{
if (len != 4)
return false;
len = buffer_get_u8(b, 1);
if (buffer_length(b) < len + 2)
if (memcmp (buf, RTTY_FILE_MAGIC, sizeof (RTTY_FILE_MAGIC)))
return false;
buffer_pull(b, NULL, 2);
buffer_pull(b, tc->name, len);
if (buf[3] != 'u' && buf[3] != 'd')
return false;
tc->size = ntohl(buffer_pull_u32(b));
file_context.sid = sid;
*type = -1;
if (buf[3] == 'd')
*type = RTTY_FILE_MSG_START_DOWNLOAD;
return true;
}
static bool parse_file_data(struct transfer_context *tc)
void recv_file(struct buffer *b, int len)
{
struct buffer *b = &tc->b;
ev_tstamp n = ev_time();
char unit = 'K';
float offset;
int len;
struct buffer *wb = &file_context.wb;
if (buffer_length(b) < 3)
return false;
buffer_put_u8 (wb, buffer_pull_u8 (b));
buffer_put_u16 (wb, len - 1);
buffer_put_data (wb, buffer_data (b), len - 1);
buffer_pull (b, NULL, len - 1);
len = ntohs(buffer_get_u16(b, 1));
ev_io_start (file_context.rtty->loop, &file_context.iow);
}
if (buffer_length(b) < len + 3)
return false;
int connect_rtty_file_service()
{
struct sockaddr_un sun = {
.sun_family = AF_UNIX
};
int sock = -1;
buffer_pull(b, NULL, 3);
if (tc->fd > 0) {
buffer_pull_to_fd(b, tc->fd, len, NULL, NULL);
} else {
/* skip */
buffer_pull(b, NULL, len);
return true;
sock = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (sock < 0) {
fprintf(stderr, "create socket fail: %s\n", strerror(errno));
return -1;
}
tc->offset += len;
offset = tc->offset / 1024.0;
strcpy(sun.sun_path, RTTY_FILE_UNIX_SOCKET);
if ((int)offset / 1024 > 0) {
offset = offset / 1024;
unit = 'M';
if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
fprintf(stderr,"connect to rtty fail: %s\n", strerror(errno));
goto err;
}
printf(" %d%% %.3f %cB %.3fs\r",
(int)(tc->offset * 1.0 / tc->size * 100), offset, unit, n - tc->ts);
return sock;
err:
if (sock > -1)
close(sock);
return -1;
}
void detect_sid(char type)
{
uint8_t buf[4];
memcpy(buf, RTTY_FILE_MAGIC, 3);
buf[3] = type;
fwrite(buf, 4, 1, stdout);
fflush(stdout);
return true;
usleep (10000);
}
static int parse_file(struct transfer_context *tc)
{
struct buffer *b = &tc->b;
int type;
while (buffer_length(b) > 0) {
type = buffer_get_u8(b, 0);
switch (type) {
case 0x01: /* file info */
if (!parse_file_info(tc))
return false;
tc->ts = ev_time();
printf("Transferring '%s'...\r\n", tc->name);
tc->fd = open(tc->name, O_WRONLY | O_TRUNC | O_CREAT, 0644);
if (tc->fd < 0) {
char magic_err[] = {0xB6, 0xBC, 'e'};
printf("Create '%s' failed: %s\r\n", tc->name, strerror(errno));
usleep(1000);
rf_write(STDOUT_FILENO, magic_err, 3);
}
break;
case 0x02: /* file data */
if (!parse_file_data(tc))
return false;
break;
case 0x03: /* file eof */
if (tc->fd > 0) {
close(tc->fd);
tc->fd = -1;
if (tc->mode == RF_RECV)
printf("\r\n");
}
return true;
default:
printf("error type\r\n");
exit(1);
}
}
return false;
}
static void stdin_read_cb(struct ev_loop *loop, struct ev_io *w, int revents)
{
struct transfer_context *tc = w->data;
bool eof = false;
buffer_put_fd(&tc->b, w->fd, -1, &eof, NULL, NULL);
if (parse_file(tc))
ev_io_stop(loop, w);
}
static void timer_cb(struct ev_loop *loop, ev_timer *w, int revents)
{
struct transfer_context *tc = w->data;
static uint8_t buf[RF_BLK_SIZE + 3];
int len;
/* Canceled by user */
if (tc->fd < 0) {
buf[0] = 0x03;
rf_write(STDOUT_FILENO, buf, 1);
ev_break(loop, EVBREAK_ALL);
return;
}
len = read(tc->fd, buf + 3, RF_BLK_SIZE);
if (len == 0) {
buf[0] = 0x03;
rf_write(STDOUT_FILENO, buf, 1);
ev_break(loop, EVBREAK_ALL);
return;
}
buf[0] = 0x02;
*(uint16_t *)&buf[1] = htons(len);
rf_write(STDOUT_FILENO, buf, len + 3);
}
void transfer_file(const char *name)
{
struct ev_loop *loop = EV_DEFAULT;
char magic[3] = {0xB6, 0xBC};
struct transfer_context tc = {};
const char *bname = "";
struct ev_timer t;
struct ev_io w;
if (name) {
struct stat st;
bname = basename(name);
magic[2] = tc.mode = RF_SEND;
printf("Transferring '%s'...Press Ctrl+C to cancel\r\n", bname);
tc.fd = open(name, O_RDONLY);
if (tc.fd < 0) {
if (errno == ENOENT) {
printf("Open '%s' failed: No such file\r\n", name);
exit(0);
}
printf("Open '%s' failed: %s\r\n", name, strerror(errno));
exit(0);
}
fstat(tc.fd, &st);
tc.size = st.st_size;
if (!(st.st_mode & S_IFREG)) {
printf("'%s' is not a regular file\r\n", name);
exit(0);
}
} else {
magic[2] = tc.mode = RF_RECV;
printf("rtty waiting to receive. Press Ctrl+C to cancel\n");
}
set_stdin(true);
ev_io_init(&w, stdin_read_cb, STDIN_FILENO, EV_READ);
ev_io_start(loop, &w);
w.data = &tc;
rf_write(STDOUT_FILENO, magic, 3);
if (tc.mode == RF_SEND) {
uint8_t info[512] = {0x01};
ev_timer_init(&t, timer_cb, 0.01, 0.01);
ev_timer_start(loop, &t);
t.data = &tc;
info[1] = strlen(bname);
memcpy(info + 2, bname, strlen(bname));
*(uint32_t *)&info[2 + strlen(bname)] = htonl(tc.size);
rf_write(STDOUT_FILENO, info, 6 + strlen(bname));
}
ev_run(loop, 0);
set_stdin(false);
exit(0);
}

View File

@@ -22,30 +22,34 @@
* SOFTWARE.
*/
#ifndef _FILE_H
#define _FILE_H
#ifndef RTTY_FILE_H
#define RTTY_FILE_H
#include <uwsc/buffer.h>
#include <ev.h>
#define RF_BLK_SIZE 8912 /* 8KB */
#include "rtty.h"
enum {
RF_SEND = 's',
RF_RECV = 'r'
RTTY_FILE_MSG_START_DOWNLOAD,
RTTY_FILE_MSG_INFO,
RTTY_FILE_MSG_DATA,
RTTY_FILE_MSG_CANCELED
};
struct transfer_context {
int size;
int offset;
int mode;
int fd;
char name[512];
ev_tstamp ts;
struct buffer b;
struct rtty_file_context {
int sock;
int sid;
bool running;
struct buffer rb;
struct buffer wb;
struct ev_io ior;
struct ev_io iow;
struct rtty *rtty;
};
void transfer_file(const char *name);
int start_file_service(struct rtty *rtty);
bool detect_file_msg(uint8_t *buf, int len, int sid, int *type);
void recv_file(struct buffer *b, int len);
int connect_rtty_file_service();
void detect_sid(char type);
#endif

1094
src/json.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,295 +0,0 @@
/* vim: set et ts=3 sw=3 sts=3 ft=c:
*
* Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved.
* https://github.com/udp/json-parser
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _JSON_H
#define _JSON_H
#ifndef json_char
#define json_char char
#endif
#ifndef json_int_t
#ifndef _MSC_VER
#include <inttypes.h>
#define json_int_t int64_t
#else
#define json_int_t __int64
#endif
#endif
#include <stdlib.h>
#include <stdbool.h>
#ifdef __cplusplus
#include <string.h>
extern "C"
{
#endif
typedef struct
{
unsigned long max_memory;
int settings;
/* Custom allocator support (leave null to use malloc/free)
*/
void * (* mem_alloc) (size_t, int zero, void * user_data);
void (* mem_free) (void *, void * user_data);
void * user_data; /* will be passed to mem_alloc and mem_free */
size_t value_extra; /* how much extra space to allocate for values? */
} json_settings;
#define json_enable_comments 0x01
typedef enum
{
json_none,
json_object,
json_array,
json_integer,
json_double,
json_string,
json_boolean,
json_null
} json_type;
extern const struct _json_value json_value_none;
typedef struct _json_object_entry
{
json_char * name;
unsigned int name_length;
struct _json_value * value;
} json_object_entry;
typedef struct _json_value
{
struct _json_value * parent;
json_type type;
union
{
int boolean;
json_int_t integer;
double dbl;
struct
{
unsigned int length;
json_char * ptr; /* null terminated */
} string;
struct
{
unsigned int length;
json_object_entry * values;
#if defined(__cplusplus) && __cplusplus >= 201103L
decltype(values) begin () const
{ return values;
}
decltype(values) end () const
{ return values + length;
}
#endif
} object;
struct
{
unsigned int length;
struct _json_value ** values;
#if defined(__cplusplus) && __cplusplus >= 201103L
decltype(values) begin () const
{ return values;
}
decltype(values) end () const
{ return values + length;
}
#endif
} array;
} u;
union
{
struct _json_value * next_alloc;
void * object_mem;
} _reserved;
#ifdef JSON_TRACK_SOURCE
/* Location of the value in the source JSON
*/
unsigned int line, col;
#endif
/* Some C++ operator sugar */
#ifdef __cplusplus
public:
inline _json_value ()
{ memset (this, 0, sizeof (_json_value));
}
inline const struct _json_value &operator [] (int index) const
{
if (type != json_array || index < 0
|| ((unsigned int) index) >= u.array.length)
{
return json_value_none;
}
return *u.array.values [index];
}
inline const struct _json_value &operator [] (const char * index) const
{
if (type != json_object)
return json_value_none;
for (unsigned int i = 0; i < u.object.length; ++ i)
if (!strcmp (u.object.values [i].name, index))
return *u.object.values [i].value;
return json_value_none;
}
inline operator const char * () const
{
switch (type)
{
case json_string:
return u.string.ptr;
default:
return "";
};
}
inline operator json_int_t () const
{
switch (type)
{
case json_integer:
return u.integer;
case json_double:
return (json_int_t) u.dbl;
default:
return 0;
};
}
inline operator bool () const
{
if (type != json_boolean)
return false;
return u.boolean != 0;
}
inline operator double () const
{
switch (type)
{
case json_integer:
return (double) u.integer;
case json_double:
return u.dbl;
default:
return 0;
};
}
#endif
} json_value;
json_value * json_parse (const json_char * json,
size_t length);
#define json_error_max 128
json_value * json_parse_ex (json_settings * settings,
const json_char * json,
size_t length,
char * error);
void json_value_free (json_value *);
/* Not usually necessary, unless you used a custom mem_alloc and now want to
* use a custom mem_free.
*/
void json_value_free_ex (json_settings * settings,
json_value *);
const json_value *json_get_value(const json_value *value, const char *name);
const char *json_get_string(const json_value *value, const char *name);
int json_get_int(const json_value *value, const char *name);
double json_get_double(const json_value *value, const char *name);
bool json_get_bool(const json_value *value, const char *name);
const char *json_get_array_string(const json_value *value, int index);
int json_get_array_int(const json_value *value, int index);
double json_get_array_double(const json_value *value, int index);
bool json_get_array_bool(const json_value *value, int index);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

130
src/log.c Normal file
View File

@@ -0,0 +1,130 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <time.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include "log.h"
static int log_threshold = LOG_DEBUG;
static bool log_initialized;
static const char *ident;
void (*log_write)(int priority, const char *fmt, va_list ap);
static const char *log_ident()
{
FILE *self;
static char line[64];
char *p = NULL;
char *sbuf;
if ((self = fopen("/proc/self/status", "r")) != NULL) {
while (fgets(line, sizeof(line), self)) {
if (!strncmp(line, "Name:", 5)) {
strtok_r(line, "\t\n", &sbuf);
p = strtok_r(NULL, "\t\n", &sbuf);
break;
}
}
fclose(self);
}
return p;
}
static inline void log_write_stdout(int priority, const char *fmt, va_list ap)
{
time_t now;
struct tm tm;
char buf[32];
now = time(NULL);
localtime_r(&now, &tm);
strftime(buf, sizeof(buf), "%Y/%m/%d %H:%M:%S", &tm);
fprintf(stderr, "%s ", buf);
vfprintf(stderr, fmt, ap);
}
static inline void log_write_syslog(int priority, const char *fmt, va_list ap)
{
vsyslog(priority, fmt, ap);
}
static inline void log_init()
{
if (log_initialized)
return;
ident = log_ident();
if (isatty(STDOUT_FILENO)) {
log_write = log_write_stdout;
} else {
log_write = log_write_syslog;
openlog(ident, 0, LOG_DAEMON);
}
log_initialized = true;
}
void set_log_threshold(int threshold)
{
log_threshold = threshold;
}
void log_close()
{
if (!log_initialized)
return;
closelog();
log_initialized = 0;
}
void __ilog(const char *filename, int line, int priority, const char *fmt, ...)
{
static char new_fmt[256];
va_list ap;
if (priority > log_threshold)
return;
log_init();
snprintf(new_fmt, sizeof(new_fmt), "(%s:%d) %s", filename, line, fmt);
va_start(ap, fmt);
log_write(priority, new_fmt, ap);
va_end(ap);
}

44
src/log.h Normal file
View File

@@ -0,0 +1,44 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _LOG_H
#define _LOG_H
#include <syslog.h>
#include <string.h>
void set_log_threshold(int threshold);
void log_close();
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define log(priority, fmt...) __ilog(__FILENAME__, __LINE__, priority, fmt)
#define log_debug(fmt...) log(LOG_DEBUG, fmt)
#define log_info(fmt...) log(LOG_INFO, fmt)
#define log_err(fmt...) log(LOG_ERR, fmt)
void __ilog(const char *filename, int line, int priority, const char *fmt, ...);
#endif

View File

@@ -22,471 +22,156 @@
* SOFTWARE.
*/
#include <pty.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <uwsc/uwsc.h>
#include <getopt.h>
#include "list.h"
#include "file.h"
#include "json.h"
#include "log.h"
#include "rtty.h"
#include "config.h"
#include "utils.h"
#include "command.h"
#include "upfile.h"
#include "downfile.h"
#define RTTY_RECONNECT_INTERVAL 5
#define RTTY_MAX_SESSIONS 5
#define RTTY_BUFFER_PERSISTENT_SIZE 4096
struct tty_session {
pid_t pid;
int pty;
int sid;
struct ev_loop *loop;
struct ev_timer timer;
struct uwsc_client *cl;
struct ev_io ior;
struct ev_io iow;
struct ev_child cw;
struct buffer wb;
enum {
LONG_OPT_HELP = 1
};
static char login[128]; /* /bin/login */
static char server_url[512];
static char extra_header[128]; /* authorization token */
static char *username = NULL;
static bool auto_reconnect;
static int keepalive = 5; /* second */
static struct ev_timer reconnect_timer;
static struct tty_session *sessions[RTTY_MAX_SESSIONS + 1];
static void del_tty_session(struct tty_session *tty)
{
ev_io_stop(tty->loop, &tty->ior);
ev_io_stop(tty->loop, &tty->iow);
ev_timer_stop(tty->loop, &tty->timer);
ev_child_stop(tty->loop, &tty->cw);
buffer_free(&tty->wb);
close(tty->pty);
kill(tty->pid, SIGTERM);
sessions[tty->sid] = NULL;
uwsc_log_info("Del session: %d\n", tty->sid);
free(tty);
}
static inline struct tty_session *find_tty_session(int sid)
{
if (sid > RTTY_MAX_SESSIONS)
return NULL;
return sessions[sid];
}
static inline void del_tty_session_by_sid(int sid)
{
struct tty_session *tty = find_tty_session(sid);
if (tty)
del_tty_session(tty);
}
static void pty_read_cb(struct ev_loop *loop, struct ev_io *w, int revents)
{
struct tty_session *tty = container_of(w, struct tty_session, ior);
struct uwsc_client *cl = tty->cl;
static uint8_t buf[4096 + 1];
int len;
buf[0] = tty->sid;
while (1) {
len = read(w->fd, buf + 1, sizeof(buf) - 1);
if (likely(len > 0))
break;
if (len < 0) {
if (errno == EINTR)
continue;
if (errno != EIO)
uwsc_log_err("Read from pty failed: %s\n", strerror(errno));
return;
}
if (len == 0)
return;
}
cl->send(cl, buf, len + 1, UWSC_OP_BINARY);
}
static void pty_write_cb(struct ev_loop *loop, struct ev_io *w, int revents)
{
struct tty_session *tty = container_of(w, struct tty_session, iow);
struct buffer *wb = &tty->wb;
int ret;
ret = buffer_pull_to_fd(wb, w->fd, buffer_length(wb), NULL, NULL);
if (ret < 0) {
uwsc_log_err("Write to pty failed: %s\n", strerror(errno));
return;
}
if (buffer_length(wb) < 1)
ev_io_stop(loop, w);
}
static void pty_on_exit(struct ev_loop *loop, struct ev_child *w, int revents)
{
struct tty_session *tty = container_of(w, struct tty_session, cw);
char str[128] = "";
snprintf(str, sizeof(str) - 1, "{\"type\":\"logout\",\"sid\":%d}", tty->sid);
tty->cl->send(tty->cl, str, strlen(str), UWSC_OP_TEXT);
del_tty_session(tty);
}
static void new_tty_session(struct uwsc_client *cl, int sid)
{
struct tty_session *s;
char str[128] = "";
pid_t pid;
int pty;
s = calloc(1, sizeof(struct tty_session));
if (!s)
return;
pid = forkpty(&pty, NULL, NULL, NULL);
if (pid == 0)
username ? execl(login,"-p","-f", username , NULL) : execl(login, login, NULL);
s->cl = cl;
s->sid = sid;
s->pid = pid;
s->pty = pty;
s->loop = cl->loop;
fcntl(pty, F_SETFL, fcntl(pty, F_GETFL, 0) | O_NONBLOCK);
ev_io_init(&s->ior, pty_read_cb, pty, EV_READ);
ev_io_start(cl->loop, &s->ior);
ev_io_init(&s->iow, pty_write_cb, pty, EV_WRITE);
ev_child_init(&s->cw, pty_on_exit, pid, 0);
ev_child_start(cl->loop, &s->cw);
buffer_set_persistent_size(&s->wb, RTTY_BUFFER_PERSISTENT_SIZE);
sessions[sid] = s;
/* Notifying the user that the session was successfully created */
snprintf(str, sizeof(str) - 1, "{\"type\":\"login\",\"sid\":%d,\"code\":0}", sid);
cl->send(cl, str, strlen(str), UWSC_OP_TEXT);
uwsc_log_info("New session:%llu\n", sid);
}
static void change_winsize(int sid, int cols, int rows)
{
struct tty_session *tty = find_tty_session(sid);
struct winsize size = {
.ws_col = cols,
.ws_row = rows
};
if (!tty) {
uwsc_log_err("non-existent sid: %d\n", sid);
return;
}
if(ioctl(tty->pty, TIOCSWINSZ, &size) < 0)
uwsc_log_err("ioctl TIOCSWINSZ error\n");
}
static void uwsc_onmessage(struct uwsc_client *cl, void *data, size_t len, bool binary)
{
if (binary) {
int sid = (*(uint8_t *)data);
struct tty_session *tty = find_tty_session(sid);
if (!tty) {
uwsc_log_err("non-existent sid: %d\n", sid);
return;
}
buffer_put_data(&tty->wb, data + 1, len - 1);
ev_io_start(tty->loop, &tty->iow);
return;
} else {
const json_value *json;
const char *type;
int sid;
json = json_parse((char *)data, len);
if (!json) {
uwsc_log_err("Invalid format: [%.*s]\n", len, (char *)data);
return;
}
type = json_get_string(json, "type");
if (!type || !type[0]) {
uwsc_log_err("Invalid format, not found type\n");
goto done;
}
sid = json_get_int(json, "sid");
if (!strcmp(type, "register")) {
uwsc_log_err("register failed: %s\n", json_get_string(json, "msg"));
ev_break(cl->loop, EVBREAK_ALL);
} else if (!strcmp(type, "login")) {
if (sid > RTTY_MAX_SESSIONS) {
char str[128] = "";
/* Notifies the user that the session creation failed */
snprintf(str, sizeof(str) - 1, "{\"type\":\"login\",\"sid\":%d,\"err\":2,\"msg\":\"sessions is full\"}", sid);
cl->send(cl, str, strlen(str), UWSC_OP_TEXT);
uwsc_log_err("Can only run up to 5 sessions at the same time\n");
goto done;
}
new_tty_session(cl, sid);
} if (!strcmp(type, "logout")) {
del_tty_session_by_sid(sid);
} if (!strcmp(type, "cmd")) {
run_command(cl, json);
return;
} if (!strcmp(type, "winsize")) {
int cols = json_get_int(json, "cols");
int rows = json_get_int(json, "rows");
change_winsize(sid, cols, rows);
}
done:
json_value_free((json_value *)json);
}
}
static void uwsc_onopen(struct uwsc_client *cl)
{
uwsc_log_info("Connect to server succeed\n");
}
static void uwsc_onerror(struct uwsc_client *cl, int err, const char *msg)
{
struct ev_loop *loop = cl->loop;
uwsc_log_err("onerror:%d: %s\n", err, msg);
free(cl);
if (auto_reconnect)
ev_timer_again(loop, &reconnect_timer);
else
ev_break(loop, EVBREAK_ALL);
}
static void uwsc_onclose(struct uwsc_client *cl, int code, const char *reason)
{
struct ev_loop *loop = cl->loop;
int i;
uwsc_log_err("onclose:%d: %s\n", code, reason);
for (i = 0; i < RTTY_MAX_SESSIONS + 1; i++)
if (sessions[i])
del_tty_session(sessions[i]);
free(cl);
if (auto_reconnect)
ev_timer_again(loop, &reconnect_timer);
else
ev_break(loop, EVBREAK_ALL);
}
static void do_connect(struct ev_loop *loop, struct ev_timer *w, int revents)
{
struct uwsc_client *cl = uwsc_new(loop, server_url, keepalive, extra_header);
if (cl) {
cl->onopen = uwsc_onopen;
cl->onmessage = uwsc_onmessage;
cl->onerror = uwsc_onerror;
cl->onclose = uwsc_onclose;
ev_timer_stop(cl->loop, &reconnect_timer);
return;
}
if (!auto_reconnect)
ev_break(loop, EVBREAK_ALL);
}
static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents)
{
if (w->signum == SIGINT) {
ev_break(loop, EVBREAK_ALL);
uwsc_log_info("Normal quit\n");
log_info("Normal quit\n");
}
}
static struct option long_options[] = {
{"id", required_argument, NULL, 'I'},
{"host", required_argument, NULL, 'h'},
{"port", required_argument, NULL, 'p'},
{"description", required_argument, NULL, 'd'},
{"token", required_argument, NULL, 't'},
{"verbose", no_argument, NULL, 'v'},
{"version", no_argument, NULL, 'V'},
{"help", no_argument, NULL, LONG_OPT_HELP},
{0, 0, 0, 0}
};
static void usage(const char *prog)
{
fprintf(stderr, "Usage: %s [option]\n"
" -I id # Set an ID for the device(Maximum 63 bytes, valid character:letter,\n"
" number, underline and short line)\n"
" -h host # Server's host or ipaddr\n"
" -p port # Server port(Default is 5912)\n"
" -a # Auto reconnect to the server\n"
" -v # verbose\n"
" -d # Adding a description to the device(Maximum 126 bytes)\n"
" -s # SSL on\n"
" -k keepalive # keep alive in seconds for this client. Defaults to 5\n"
" -b baseurl # Set a base url\n"
" -V # Show version\n"
" -D # Run in the background\n"
" -R # Receive file\n"
" -S file # Send file\n"
" -t token # Authorization token\n"
" -f username # Skip a second login authentication. See man login(1) about the details\n"
, prog);
" -I, --id=string Set an ID for the device(Maximum 63 bytes, valid\n"
" character:letter, number, underline and short line)\n"
" -h, --host=string Server's host or ipaddr(Default is localhost)\n"
" -p, --port=number Server port(Default is 5912)\n"
" -d, --description=string Adding a description to the device(Maximum 126 bytes)\n"
" -a Auto reconnect to the server\n"
" -s SSL on\n"
" -D Run in the background\n"
" -t, --token=string Authorization token\n"
" -f username Skip a second login authentication. See man login(1) about the details\n"
" -R Receive file\n"
" -S file Send file\n"
" -v, --verbose verbose\n"
" -V, --version Show version\n"
" --help Show usage\n",
prog);
exit(1);
}
int main(int argc, char **argv)
{
int opt;
struct ev_loop *loop = EV_DEFAULT;
struct ev_signal signal_watcher;
char devid[64] = "";
const char *baseurl = NULL;
const char *host = NULL;
int port = 5912;
char *description = NULL;
bool background = false;
bool verbose = false;
bool ssl = false;
struct rtty rtty = {
.host = "localhost",
.port = 5912,
.loop = loop,
.sock = -1,
.sock_file = -1
};
int option_index;
int c;
while ((opt = getopt(argc, argv, "h:b:f:p:I:avd:sk:VDRS:t:")) != -1) {
switch (opt) {
case 'h':
host = optarg;
while (true) {
c = getopt_long(argc, argv, "I:h:p:d:asDt:f:RS:vV", long_options, &option_index);
if (c == -1)
break;
case 'b':
baseurl = optarg;
break;
case 'f':
username = optarg;
break;
case 'p':
port = atoi(optarg);
break;
case 'I':
strncpy(devid, optarg, sizeof(devid) - 1);
break;
case 'a':
auto_reconnect = true;
break;
case 'v':
verbose = true;
break;
case 'd':
if (strlen(optarg) > 126) {
uwsc_log_err("Description too long\n");
switch (c) {
case 'I':
rtty.devid = optarg;
break;
case 'h':
rtty.host = optarg;
break;
case 'p':
rtty.port = atoi(optarg);
break;
case 'd':
if (strlen(optarg) > 126) {
log_err("Description too long\n");
usage(argv[0]);
}
rtty.description = optarg;
break;
case 'a':
rtty.reconnect = true;
break;
case 's':
rtty.ssl_on = true;
break;
case 'D':
background = true;
break;
case 't':
rtty.token = optarg;
break;
case 'f':
rtty.username = optarg;
break;
case 'R':
download_file();
return 0;
case 'S':
upload_file(optarg);
return 0;
case 'v':
verbose = true;
break;
case 'V':
log_info("rtty version %s\n", RTTY_VERSION_STRING);
exit(0);
case LONG_OPT_HELP:
usage(argv[0]);
}
description = calloc(1, strlen(optarg) * 4);
if (!description) {
uwsc_log_err("malloc failed:%s\n", strerror(errno));
exit(1);
}
urlencode(description, strlen(optarg) * 4, optarg, strlen(optarg));
break;
case 's':
ssl = true;
break;
case 'k':
keepalive = atoi(optarg);
break;
case 'V':
uwsc_log_info("rtty version %s\n", RTTY_VERSION_STRING);
exit(0);
break;
case 'D':
background = true;
break;
case 'R':
transfer_file(NULL);
break;
case 'S':
transfer_file(optarg);
break;
case 't':
snprintf(extra_header, sizeof(extra_header) - 1, "Authorization: %s\r\n", optarg);
break;
default: /* '?' */
usage(argv[0]);
break;
default: /* '?' */
usage(argv[0]);
break;
}
}
if (background && daemon(0, 0))
uwsc_log_err("Can't run in the background: %s\n", strerror(errno));
log_err("Can't run in the background: %s\n", strerror(errno));
if (!verbose)
uwsc_log_threshold(LOG_ERR);
set_log_threshold(LOG_ERR);
if (!devid[0]) {
uwsc_log_err("You must specify an id\n");
usage(argv[0]);
}
if (!valid_id(devid)) {
uwsc_log_err("Invalid device id\n");
usage(argv[0]);
}
if (!host || !port) {
uwsc_log_err("You must specify the host and port\n");
usage(argv[0]);
}
uwsc_log_info("libuwsc version %s\n", UWSC_VERSION_STRING);
uwsc_log_info("rtty version %s\n", RTTY_VERSION_STRING);
log_info("rtty version %s\n", RTTY_VERSION_STRING);
if (getuid() > 0) {
uwsc_log_err("Operation not permitted\n");
log_err("Operation not permitted, must be run as root\n");
return -1;
}
if (find_login(login, sizeof(login) - 1) < 0) {
uwsc_log_err("The program 'login' is not found\n");
return -1;
}
snprintf(server_url, sizeof(server_url),
"ws%s://%s:%d%s/ws?device=1&devid=%s&description=%s&keepalive=%d",
ssl ? "s" : "", host, port, baseurl ? baseurl : "", devid, description ? description : "", keepalive);
free(description);
ev_timer_init(&reconnect_timer, do_connect, 0.0, RTTY_RECONNECT_INTERVAL);
ev_timer_start(loop, &reconnect_timer);
ev_signal_init(&signal_watcher, signal_cb, SIGINT);
ev_signal_start(loop, &signal_watcher);
if (rtty_start(&rtty) < 0)
return -1;
ev_run(loop, 0);
return 0;

171
src/net.c Normal file
View File

@@ -0,0 +1,171 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <sys/socket.h>
#include <stdbool.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <netdb.h>
#include "net.h"
#include "log.h"
struct net_context {
struct ev_timer tmr;
struct ev_io iow;
int sock;
void *arg;
void (*on_connected)(int sock, void *arg);
};
static const char *port2str(int port)
{
static char buffer[sizeof("65535\0")];
if (port < 0 || port > 65535)
return NULL;
snprintf(buffer, sizeof(buffer), "%u", port);
return buffer;
}
static void sock_write_cb(struct ev_loop *loop, struct ev_io *w, int revents)
{
struct net_context *ctx = w->data;
int err = 0;
socklen_t len = sizeof(err);
int ret;
ev_io_stop(loop, w);
ev_timer_stop(loop, &ctx->tmr);
ret = getsockopt(w->fd, SOL_SOCKET, SO_ERROR, &err, &len);
if (ret < 0) {
log_err("getsockopt: %s\n", strerror(errno));
goto err;
}
if (err > 0) {
log_err("network connect failed: %s\n", strerror(err));
goto err;
}
ctx->on_connected(w->fd, ctx->arg);
return;
err:
close(w->fd);
ctx->on_connected(-1, ctx->arg);
}
static void timer_cb(struct ev_loop *loop, struct ev_timer *w, int revents)
{
struct net_context *ctx = w->data;
log_err("network connect timeout\n");
ev_io_stop(loop, &ctx->iow);
close(ctx->sock);
ctx->on_connected(-1, ctx->arg);
}
static void wait_connect(struct ev_loop *loop, int sock, int timeout,
void (*on_connected)(int sock, void *arg), void *arg)
{
static struct net_context ctx;
ctx.arg = arg;
ctx.on_connected = on_connected;
ev_timer_init(&ctx.tmr, timer_cb, timeout, 0);
ctx.tmr.data = &ctx;
ev_timer_start(loop, &ctx.tmr);
ev_io_init(&ctx.iow, sock_write_cb, sock, EV_WRITE);
ctx.iow.data = &ctx;
ev_io_start (loop, &ctx.iow);
}
int tcp_connect(struct ev_loop *loop, const char *host, int port,
void (*on_connected)(int sock, void *arg), void *arg)
{
struct sockaddr *addr = NULL;
struct addrinfo *result, *rp;
struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
.ai_flags = AI_ADDRCONFIG
};
int sock = -1;
int addr_len;
int ret;
ret = getaddrinfo(host, port2str(port), &hints, &result);
if (ret) {
if (ret == EAI_SYSTEM) {
log_err("getaddrinfo failed: %s\n", strerror(errno));
return -1;
}
log_err("getaddrinfo failed: %s\n", gai_strerror(ret));
return -1;
}
for (rp = result; rp != NULL; rp = rp->ai_next) {
if (rp->ai_family == AF_INET) {
addr = rp->ai_addr;
addr_len = rp->ai_addrlen;
break;
}
}
if (!addr) {
log_err("getaddrinfo failed: Not found addr\n");
goto free_addrinfo;
}
sock = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (sock < 0) {
log_err("create socket failed: %s\n", strerror(errno));
goto free_addrinfo;
}
if (connect(sock, addr, addr_len) < 0) {
if (errno != EINPROGRESS) {
log_err("connect failed: %s\n", strerror(errno));
close(sock);
sock = -1;
}
wait_connect(loop, sock, 3, on_connected, arg);
} else {
on_connected(sock, arg);
}
free_addrinfo:
freeaddrinfo(result);
return sock;
}

33
src/net.h Normal file
View File

@@ -0,0 +1,33 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef RTTY_NET_H
#define RTTY_NET_H
#include <ev.h>
int tcp_connect(struct ev_loop *loop, const char *host, int port,
void (*on_connected)(int sock, void *arg), void *arg);
#endif

508
src/rtty.c Normal file
View File

@@ -0,0 +1,508 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <pty.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include "ssl.h"
#include "net.h"
#include "log.h"
#include "file.h"
#include "rtty.h"
#include "list.h"
#include "utils.h"
#include "command.h"
static char login_path[128]; /* /bin/login */
static void del_tty(struct tty *tty) {
struct rtty *rtty = tty->rtty;
struct ev_loop *loop = rtty->loop;
ev_io_stop(loop, &tty->ior);
ev_io_stop(loop, &tty->iow);
ev_child_stop(loop, &tty->cw);
buffer_free(&tty->wb);
close(tty->pty);
kill(tty->pid, SIGTERM);
rtty->ttys[tty->sid] = NULL;
log_info("delete tty: %d\n", tty->sid);
free(tty);
}
static inline struct tty *find_tty(struct rtty *rtty, int sid) {
if (sid > RTTY_MAX_TTY - 1)
return NULL;
return rtty->ttys[sid];
}
static inline void tty_logout(struct rtty *rtty, int sid) {
struct tty *tty = find_tty (rtty, sid);
if (tty)
del_tty (tty);
}
static void pty_on_read(struct ev_loop *loop, struct ev_io *w, int revents) {
struct tty *tty = container_of(w, struct tty, ior);
struct rtty *rtty = tty->rtty;
struct buffer *wb = &rtty->wb;
static uint8_t buf[4096];
int type;
int len;
while (1) {
len = read(w->fd, buf, sizeof(buf));
if (likely(len > 0))
break;
if (len < 0) {
if (errno == EINTR)
continue;
if (errno != EIO)
log_err("read from pty failed: %s\n", strerror(errno));
return;
}
if (len == 0)
return;
}
if (!detect_file_msg(buf, len, tty->sid, &type)) {
buffer_put_u8 (wb, MSG_TYPE_TERMDATA);
buffer_put_u16 (wb, htons(len + 1));
buffer_put_u8 (wb, tty->sid);
buffer_put_data (wb, buf, len);
} else if (type > -1) {
buffer_put_u8 (wb, MSG_TYPE_FILE);
buffer_put_u16 (wb, htons(2));
buffer_put_u8 (wb, tty->sid);
buffer_put_u8 (wb, type);
}
ev_io_start(loop, &rtty->iow);
}
static void pty_on_write(struct ev_loop *loop, struct ev_io *w, int revents) {
struct tty *tty = container_of(w, struct tty, iow);
struct buffer *wb = &tty->wb;
int ret;
ret = buffer_pull_to_fd(wb, w->fd, buffer_length(wb), NULL, NULL);
if (ret < 0) {
log_err("write to pty failed: %s\n", strerror(errno));
return;
}
if (buffer_length(wb) < 1)
ev_io_stop(loop, w);
}
static void pty_on_exit(struct ev_loop *loop, struct ev_child *w, int revents) {
struct tty *tty = container_of(w, struct tty, cw);
struct rtty *rtty = tty->rtty;
struct buffer *wb = &rtty->wb;
del_tty (tty);
buffer_put_u8 (wb, MSG_TYPE_LOGOUT);
buffer_put_u16 (wb, htons(1));
buffer_put_u8 (wb, tty->sid);
ev_io_start(loop, &rtty->iow);
}
static void tty_login(struct rtty *rtty)
{
struct tty *tty;
pid_t pid;
int pty;
int sid;
buffer_put_u8 (&rtty->wb, MSG_TYPE_LOGIN);
for (sid = 0; sid < RTTY_MAX_TTY; sid++) {
if (!rtty->ttys[sid])
break;
}
if (sid == RTTY_MAX_TTY) {
log_info ("tty login fail, device busy\n");
buffer_put_u16 (&rtty->wb, htons(1));
buffer_put_u8 (&rtty->wb, 1);
ev_io_start (rtty->loop, &rtty->iow);
return;
}
tty = calloc(1, sizeof(struct tty));
pid = forkpty(&pty, NULL, NULL, NULL);
if (pid == 0)
rtty->username ? execl(login_path, "-p", "-f", rtty->username, NULL) : execl(login_path, login_path, NULL);
tty->sid = sid;
tty->pid = pid;
tty->pty = pty;
tty->rtty = rtty;
fcntl(pty, F_SETFL, fcntl(pty, F_GETFL, 0) | O_NONBLOCK);
ev_io_init(&tty->ior, pty_on_read, pty, EV_READ);
ev_io_start(rtty->loop, &tty->ior);
ev_io_init(&tty->iow, pty_on_write, pty, EV_WRITE);
ev_child_init(&tty->cw, pty_on_exit, pid, 0);
ev_child_start(rtty->loop, &tty->cw);
buffer_set_persistent_size(&tty->wb, RTTY_BUFFER_PERSISTENT_SIZE);
rtty->ttys[sid] = tty;
buffer_put_u16 (&rtty->wb, htons(2));
buffer_put_u8 (&rtty->wb, 0);
buffer_put_u8 (&rtty->wb, sid);
ev_io_start (rtty->loop, &rtty->iow);
log_info("new tty: %d\n", sid);
}
static void write_data_to_tty(struct rtty *rtty, int sid, int len)
{
struct tty *tty = find_tty (rtty, sid);
if (!tty) {
log_err("non-existent sid: %d\n", sid);
return;
}
buffer_put_data (&tty->wb, buffer_data (&rtty->rb), len);
buffer_pull(&rtty->rb, NULL, len);
ev_io_start(rtty->loop, &tty->iow);
}
static void set_tty_winsize(struct rtty *rtty, int sid)
{
struct tty *tty = find_tty (rtty, sid);
struct winsize size;
if (!tty) {
log_err("non-existent sid: %d\n", sid);
return;
}
size.ws_col = ntohs(buffer_pull_u16 (&rtty->rb));
size.ws_row = ntohs(buffer_pull_u16 (&rtty->rb));
if (ioctl(tty->pty, TIOCSWINSZ, &size) < 0)
log_err("ioctl TIOCSWINSZ failed: %s\n", strerror(errno));
}
static void rtty_exit(struct rtty *rtty)
{
close(rtty->sock);
rtty->sock = -1;
if (rtty->sock_file > -1) {
close (rtty->sock_file);
rtty->sock_file = -1;
}
ev_io_stop (rtty->loop, &rtty->ior);
ev_io_stop (rtty->loop, &rtty->iow);
ev_io_stop (rtty->loop, &rtty->iof);
for (int i = 0; i < RTTY_MAX_TTY; i++)
if (rtty->ttys[i])
del_tty (rtty->ttys[i]);
if (!rtty->reconnect)
ev_break (rtty->loop, EVBREAK_ALL);
#if RTTY_SSL_SUPPORT
rtty_ssl_free(rtty->ssl);
#endif
}
static void rtty_register(struct rtty *rtty)
{
size_t len = 3 + strlen(rtty->devid);
struct buffer *wb = &rtty->wb;
if (rtty->description)
len += strlen(rtty->description);
if (rtty->token)
len += strlen(rtty->token);
buffer_put_u8(wb, MSG_TYPE_REGISTER);
buffer_put_u16(wb, htons(len));
buffer_put_string(wb, rtty->devid);
buffer_put_u8(wb, '\0');
if (rtty->description)
buffer_put_string(wb, rtty->description);
buffer_put_u8(wb, '\0');
if (rtty->token)
buffer_put_string(wb, rtty->token);
buffer_put_u8(wb, '\0');
ev_io_start(rtty->loop, &rtty->iow);
}
static void parse_msg(struct rtty *rtty)
{
struct buffer *rb = &rtty->rb;
int msgtype;
int msglen;
while (true) {
if (buffer_length (rb) < 3)
return;
msglen = ntohs(buffer_get_u16 (rb, 1));
if (buffer_length (rb) < msglen + 3)
return;
msgtype = buffer_pull_u8 (rb);
buffer_pull_u16 (rb);
switch (msgtype) {
case MSG_TYPE_REGISTER:
if (buffer_pull_u8 (rb)) {
char errs[128] = "";
buffer_pull (rb, errs, msglen - 1);
rtty_exit (rtty);
log_err("register fail: %s\n", errs);
return;
}
buffer_pull (rb, NULL, msglen - 1);
log_info ("register success\n");
break;
case MSG_TYPE_LOGIN:
tty_login (rtty);
break;
case MSG_TYPE_LOGOUT:
tty_logout (rtty, buffer_pull_u8 (rb));
break;
case MSG_TYPE_TERMDATA:
write_data_to_tty (rtty, buffer_pull_u8 (rb), msglen - 1);
break;
case MSG_TYPE_WINSIZE:
set_tty_winsize (rtty, buffer_pull_u8 (rb));
break;
case MSG_TYPE_CMD:
run_command (rtty, buffer_data (rb));
buffer_pull (rb, NULL, msglen);
break;
case MSG_TYPE_HEARTBEAT:
break;
case MSG_TYPE_FILE:
recv_file(rb, msglen);
break;
default:
log_err ("invalid message type: %d\n", msgtype);
rtty_exit (rtty);
return;
}
}
}
static void on_net_read(struct ev_loop *loop, struct ev_io *w, int revents)
{
struct rtty *rtty = container_of (w, struct rtty, ior);
bool eof;
int ret;
#if RTTY_SSL_SUPPORT
if (rtty->ssl)
ret = buffer_put_fd(&rtty->rb, w->fd, -1, &eof, rtty_ssl_read, rtty->ssl);
else
ret = buffer_put_fd(&rtty->rb, w->fd, -1, &eof, NULL, NULL);
#endif
if (ret < 0) {
log_err("socket read error: %s\n", strerror (errno));
return;
}
rtty->active = ev_now(loop);
parse_msg(rtty);
if (eof) {
log_info ("socket closed by server\n");
rtty_exit (rtty);
return;
}
}
static void on_net_write(struct ev_loop *loop, struct ev_io *w, int revents)
{
struct rtty *rtty = container_of (w, struct rtty, iow);
int ret;
#if RTTY_SSL_SUPPORT
if (rtty->ssl) {
if (!rtty->ssl_handshaked) {
ret = rtty_ssl_handshake(rtty->ssl);
if (ret == -1) {
log_err("ssl handshake failed\n");
rtty_exit(rtty);
return;
}
if (ret != 1)
return;
rtty->ssl_handshaked = true;
}
}
if (rtty->ssl)
ret = buffer_pull_to_fd (&rtty->wb, w->fd, -1, rtty_ssl_write, rtty->ssl);
else
ret = buffer_pull_to_fd (&rtty->wb, w->fd, -1, NULL, NULL);
#endif
if (ret < 0) {
log_err ("socket write error: %s\n", strerror(errno));
rtty_exit(rtty);
return;
}
if (buffer_length (&rtty->wb) < 1)
ev_io_stop (loop, w);
}
static void on_net_connected(int sock, void *arg)
{
struct rtty *rtty = arg;
if (sock < 0) {
if (!rtty->reconnect)
ev_break (rtty->loop, EVBREAK_ALL);
return;
}
log_info ("connected to server\n");
rtty->sock = sock;
ev_io_init(&rtty->ior, on_net_read, sock, EV_READ);
ev_io_start (rtty->loop, &rtty->ior);
ev_io_init(&rtty->iow, on_net_write, sock, EV_WRITE);
rtty->sock_file = start_file_service(rtty);
if (rtty->ssl_on) {
#if (RTTY_SSL_SUPPORT)
rtty_ssl_init((struct rtty_ssl_ctx **)&rtty->ssl, sock, rtty->host);
#endif
}
rtty_register(rtty);
}
static void rtty_timer_cb(struct ev_loop *loop, struct ev_timer *w, int revents)
{
struct rtty *rtty = container_of (w, struct rtty, tmr);
ev_tstamp now = ev_now(loop);
if (rtty->sock < 0) {
if (now - rtty->active < 5)
return;
rtty->active = now;
log_err ("rtty reconnecting...\n");
tcp_connect(rtty->loop, rtty->host, rtty->port, on_net_connected, rtty);
return;
}
if (now - rtty->active > RTTY_HEARTBEAT_INTEVAL * 3 / 2) {
log_err("Inactive too long time\n");
rtty_exit(rtty);
return;
}
if (now - rtty->last_heartbeat > RTTY_HEARTBEAT_INTEVAL - 1) {
rtty->last_heartbeat = now;
rtty_send_msg(rtty, MSG_TYPE_HEARTBEAT, NULL, 0);
}
}
int rtty_start(struct rtty *rtty)
{
if (rtty->ssl_on) {
#if (!RTTY_SSL_SUPPORT)
log_err("SSL is not enabled at compile\n");
return -1;
#endif
}
if (!rtty->devid) {
log_err("you must specify an id for your device\n");
return -1;
}
if (!valid_id(rtty->devid)) {
log_err("invalid device id\n");
return -1;
}
if (find_login(login_path, sizeof(login_path) - 1) < 0) {
log_err("the program 'login' is not found\n");
return -1;
}
ev_timer_init(&rtty->tmr, rtty_timer_cb, 1.0, 1.0);
ev_timer_start(rtty->loop, &rtty->tmr);
if (tcp_connect(rtty->loop, rtty->host, rtty->port, on_net_connected, rtty) < 0
&& !rtty->reconnect)
return -1;
rtty->active = ev_now(rtty->loop);
return 0;
}
void rtty_send_msg(struct rtty *rtty, int type, void *data, int len)
{
struct buffer *wb = &rtty->wb;
buffer_put_u8 (wb, type);
buffer_put_u16 (wb, htons(len));
buffer_put_data(wb, data, len);
ev_io_start (rtty->loop, &rtty->iow);
}

89
src/rtty.h Normal file
View File

@@ -0,0 +1,89 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef RTTY_RTTY_H
#define RTTY_RTTY_H
#include <stdbool.h>
#include <ev.h>
#include "buffer.h"
#define RTTY_MAX_TTY 5
#define RTTY_HEARTBEAT_INTEVAL 5.0
#define RTTY_BUFFER_PERSISTENT_SIZE 4096
enum {
MSG_TYPE_REGISTER = 0x00,
MSG_TYPE_LOGIN = 0x01,
MSG_TYPE_LOGOUT = 0x02,
MSG_TYPE_TERMDATA = 0x03,
MSG_TYPE_WINSIZE = 0x04,
MSG_TYPE_CMD = 0x05,
MSG_TYPE_HEARTBEAT = 0x06,
MSG_TYPE_FILE = 0x07
};
struct rtty;
struct tty {
pid_t pid;
int pty;
int sid;
struct ev_io ior;
struct ev_io iow;
struct ev_child cw;
struct buffer wb;
struct rtty *rtty;
};
struct rtty {
const char *host;
int port;
int sock;
int sock_file;
const char *devid;
const char *token; /* authorization token */
const char *description;
const char *username;
bool ssl_on;
struct buffer rb;
struct buffer wb;
struct ev_io iow;
struct ev_io ior;
struct ev_io iof;
struct ev_timer tmr;
struct ev_loop *loop;
ev_tstamp active;
ev_tstamp last_heartbeat;
bool reconnect;
void *ssl; /* Context wrap of openssl, wolfssl and mbedtls */
bool ssl_handshaked;
struct tty *ttys[RTTY_MAX_TTY];
};
int rtty_start(struct rtty *rtty);
void rtty_send_msg(struct rtty *rtty, int type, void *data, int len);
#endif

219
src/ssl.c Normal file
View File

@@ -0,0 +1,219 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <errno.h>
#include <stdlib.h>
#include "log.h"
#include "ssl.h"
#include "buffer.h"
#if RTTY_SSL_SUPPORT
#if RTTY_HAVE_MBEDTLS
#include <mbedtls/ssl.h>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/net_sockets.h>
struct rtty_ssl_ctx {
mbedtls_net_context net;
mbedtls_ssl_context ssl;
mbedtls_ssl_config cfg;
mbedtls_ctr_drbg_context drbg;
mbedtls_entropy_context etpy;
mbedtls_x509_crt x509;
bool last_read_ok;
};
#else
#if RTTY_HAVE_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#elif RTTY_HAVE_WOLFSSL
#define WC_NO_HARDEN
#include <wolfssl/openssl/ssl.h>
#include <wolfssl/openssl/err.h>
#endif
struct rtty_ssl_ctx {
SSL_CTX *ctx;
SSL *ssl;
};
#endif
int rtty_ssl_init(struct rtty_ssl_ctx **ctx, int sock, const char *host)
{
struct rtty_ssl_ctx *c = calloc(1, sizeof(struct rtty_ssl_ctx));
if (!ctx) {
log_err("calloc failed: %s\n", strerror(errno));
return -1;
}
#if RTTY_HAVE_MBEDTLS
mbedtls_net_init(&c->net);
mbedtls_ssl_init(&c->ssl);
mbedtls_ssl_config_init(&c->cfg);
mbedtls_ctr_drbg_init(&c->drbg);
mbedtls_x509_crt_init(&c->x509);
mbedtls_entropy_init(&c->etpy);
mbedtls_ctr_drbg_seed(&c->drbg, mbedtls_entropy_func, &c->etpy, NULL, 0);
mbedtls_ssl_config_defaults(&c->cfg, MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
mbedtls_ssl_conf_authmode(&c->cfg, MBEDTLS_SSL_VERIFY_OPTIONAL);
mbedtls_ssl_conf_ca_chain(&c->cfg, &c->x509, NULL);
mbedtls_ssl_conf_rng(&c->cfg, mbedtls_ctr_drbg_random, &c->drbg);
mbedtls_ssl_set_bio(&c->ssl, &c->net, mbedtls_net_send,
mbedtls_net_recv, NULL);
mbedtls_ssl_set_hostname(&c->ssl, host);
mbedtls_ssl_setup(&c->ssl, &c->cfg);
c->net.fd = sock;
#else
#if UHTTPD_HAVE_WOLFSSL
wolfSSL_Init();
c->ctx = SSL_CTX_new(TLSv1_2_client_method());
#elif OPENSSL_VERSION_NUMBER < 0x10100000L
SSL_library_init();
SSL_load_error_strings();
c->ctx = SSL_CTX_new(SSLv23_client_method());
#else
c->ctx = SSL_CTX_new(TLS_client_method());
#endif
SSL_CTX_set_verify(c->ctx, SSL_VERIFY_NONE, NULL);
c->ssl = SSL_new(c->ctx);
SSL_set_tlsext_host_name(c->ssl, host);
SSL_set_fd(c->ssl, sock);
#endif
*ctx = c;
return 0;
}
int rtty_ssl_handshake(struct rtty_ssl_ctx *ctx)
{
#if RTTY_HAVE_MBEDTLS
int ret = mbedtls_ssl_handshake(&ctx->ssl);
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)
return 0;
if (ret == 0)
return 1;
return -1;
#else
int ret = SSL_connect(ctx->ssl);
if (ret == 1) {
return 1;
} else {
int err = SSL_get_error(ctx->ssl, ret);
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
return 0;
log_err("%s\n", ERR_reason_error_string(err));
return -1;
}
#endif
}
void rtty_ssl_free(struct rtty_ssl_ctx *ctx)
{
if (!ctx)
return;
#if RTTY_HAVE_MBEDTLS
mbedtls_ssl_free(&ctx->ssl);
mbedtls_ssl_config_free(&ctx->cfg);
#else
SSL_shutdown(ctx->ssl);
SSL_CTX_free(ctx->ctx);
#endif
free(ctx);
}
int rtty_ssl_read(int fd, void *buf, size_t count, void *arg)
{
struct rtty_ssl_ctx *ctx = arg;
#if RTTY_HAVE_MBEDTLS
int ret;
if (ctx->last_read_ok) {
ctx->last_read_ok = false;
return P_FD_PENDING;
}
ret = mbedtls_ssl_read(&ctx->ssl, buf, count);
if (ret < 0) {
if (ret == MBEDTLS_ERR_SSL_WANT_READ)
return P_FD_PENDING;
return P_FD_ERR;
}
if (ret > 0)
ctx->last_read_ok = true;
#else
int ret = SSL_read(ctx->ssl, buf, count);
if (ret < 0) {
int err = SSL_get_error(ctx->ssl, ret);
if (err == SSL_ERROR_WANT_READ)
return P_FD_PENDING;
log_err("%s\n", ERR_reason_error_string(err));
return P_FD_ERR;
}
#endif
return ret;
}
int rtty_ssl_write(int fd, void *buf, size_t count, void *arg)
{
struct rtty_ssl_ctx *ctx = arg;
#if RTTY_HAVE_MBEDTLS
int ret = mbedtls_ssl_write(&ctx->ssl, buf, count);
if (ret < 0) {
if (ret == MBEDTLS_ERR_SSL_WANT_WRITE)
return P_FD_PENDING;
return P_FD_ERR;
}
#else
int ret = SSL_write(ctx->ssl, buf, count);
if (ret < 0) {
int err = SSL_get_error(ctx->ssl, ret);
if (err == SSL_ERROR_WANT_WRITE)
return P_FD_PENDING;
log_err("%s\n", ERR_reason_error_string(err));
return P_FD_ERR;
}
#endif
return ret;
}
#endif

46
src/ssl.h Normal file
View File

@@ -0,0 +1,46 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef RTTY_SSL_H
#define RTTY_SSL_H
#include <stdint.h>
#include <sys/types.h>
#include "config.h"
#if RTTY_SSL_SUPPORT
struct rtty_ssl_ctx;
int rtty_ssl_init(struct rtty_ssl_ctx **ctx, int sock, const char *host);
int rtty_ssl_handshake(struct rtty_ssl_ctx *ctx);
void rtty_ssl_free(struct rtty_ssl_ctx *ctx);
int rtty_ssl_read(int fd, void *buf, size_t count, void *arg);
int rtty_ssl_write(int fd, void *buf, size_t count, void *arg);
#endif
#endif

157
src/upfile.c Normal file
View File

@@ -0,0 +1,157 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdbool.h>
#include "file.h"
#include "utils.h"
static struct buffer b;
static uint32_t remain;
static uint32_t file_size;
static ev_tstamp start_time;
static struct ev_io iow;
static struct ev_io ior;
static struct ev_signal sw;
static bool canceled;
static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents)
{
canceled = true;
ev_signal_stop (loop, w);
}
static void on_net_write(struct ev_loop *loop, struct ev_io *w, int revents)
{
int ret = buffer_pull_to_fd (&b, w->fd, -1, NULL, NULL);
if (ret < 0) {
fprintf(stderr,"socket write error: %s\n", strerror(errno));
return;
}
if (buffer_length (&b) < 1)
ev_io_stop (loop, w);
}
static void on_file_read(struct ev_loop *loop, struct ev_io *w, int revents)
{
uint8_t buf[4096];
int ret;
if (canceled) {
ev_io_stop (loop, w);
buffer_put_u8 (&b, RTTY_FILE_MSG_CANCELED);
buffer_put_u16 (&b, 0);
ev_io_start (loop, &iow);
return;
}
if (buffer_length (&b) > 4096 * 10)
return;
ret = read(w->fd, buf, sizeof (buf));
buffer_put_u8 (&b, RTTY_FILE_MSG_DATA);
buffer_put_u16 (&b, ret);
buffer_put_data (&b, buf, ret);
ev_io_start (loop, &iow);
remain -= ret;
fprintf (stdout, "%100c\r", ' ');
fprintf(stdout," %lu%% %s %.3lfs\r",
(file_size - remain) * 100UL / file_size,
format_size(file_size - remain), ev_now(loop) - start_time);
fflush (stdout);
if (ret == 0) {
ev_io_stop (loop, w);
ev_signal_stop (loop, &sw);
}
}
void upload_file(const char *name)
{
struct ev_loop *loop = EV_DEFAULT;
const char *bname = basename(name);
struct stat st;
int sock = -1;
int fd = -1;
fd = open(name, O_RDONLY);
if (fd < 0) {
fprintf (stderr, "open '%s' failed: ", name);
if (errno == ENOENT)
fprintf(stderr,"No such file\n");
else
fprintf(stderr,"%s\n", strerror (errno));
return;
}
fstat(fd, &st);
if (!(st.st_mode & S_IFREG)) {
fprintf(stderr,"'%s' is not a regular file\n", name);
goto done;
}
remain = file_size = st.st_size;
sock = connect_rtty_file_service();
if (sock < 0)
goto done;
detect_sid('u');
buffer_put_u8 (&b, RTTY_FILE_MSG_INFO);
buffer_put_u16 (&b, strlen(bname));
buffer_put_string (&b, bname);
ev_signal_init(&sw, signal_cb, SIGINT);
ev_signal_start(loop, &sw);
ev_io_init(&iow, on_net_write, sock, EV_WRITE);
ev_io_start(loop, &iow);
ev_io_init(&ior, on_file_read, fd, EV_READ);
ev_io_start (loop, &ior);
printf("Transferring '%s'...Press Ctrl+C to cancel\n", bname);
start_time = ev_now (loop);
ev_run(loop, 0);
puts("");
done:
if (sock > -1)
close(sock);
if (fd > -1)
close(fd);
}

30
src/upfile.h Normal file
View File

@@ -0,0 +1,30 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef RTTY_UPFILE_H_
#define RTTY_UPFILE_H_
void upload_file(const char *name);
#endif

View File

@@ -29,35 +29,7 @@
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <uwsc/log.h>
/* blen is the size of buf; slen is the length of src. The input-string need
** not be, and the output string will not be, null-terminated. Returns the
** length of the encoded string, or -1 on error (buffer overflow) */
int urlencode(char *buf, int blen, const char *src, int slen)
{
int i;
int len = 0;
static const char hex[] = "0123456789abcdef";
for (i = 0; (i < slen) && (len < blen); i++) {
if (isalnum(src[i]) || (src[i] == '-') || (src[i] == '_') ||
(src[i] == '.') || (src[i] == '~')) {
buf[len++] = src[i];
} else if (src[i] == ' ') {
buf[len++] = '+';
} else if ((len + 3) <= blen) {
buf[len++] = '%';
buf[len++] = hex[(src[i] >> 4) & 15];
buf[len++] = hex[ src[i] & 15];
} else {
len = -1;
break;
}
}
return (i == slen) ? len : -1;
}
#include <sys/time.h>
int find_login(char *buf, int len)
{
@@ -86,3 +58,60 @@ bool valid_id(const char *id)
return true;
}
/* reference from https://tools.ietf.org/html/rfc4648#section-4 */
int b64_encode(const void *src, size_t srclen, void *dest, size_t destsize)
{
char *Base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
const uint8_t *input = src;
char *output = dest;
while (srclen > 0) {
int skip = 1;
int i0 = input[0] >> 2;
int i1 = (input[0] & 0x3) << 4;
int i2 = 64;
int i3 = 64;
if (destsize < 5)
return -1;
if (srclen > 1) {
skip++;
i1 += input[1] >> 4;
i2 = (input[1] & 0xF) << 2;
if (srclen > 2) {
i2 += input[2] >> 6;
i3 = input[2] & 0x3F;
skip++;
}
}
*output++ = Base64[i0];
*output++ = Base64[i1];
*output++ = Base64[i2];
*output++ = Base64[i3];
input += skip;
srclen -= skip;
destsize -= 4;
}
*output++ = 0;
return output - (char *)dest - 1;
}
const char *format_size(size_t size)
{
static char str[64];
if (size < 1024)
sprintf(str,"%zu B", size);
else if (size < 1024 * 1024)
sprintf(str,"%.2f KB", size / 1024.0);
else
sprintf(str,"%.2f MB", size / 1024.0 / 1024.0);
return str;
}

View File

@@ -28,10 +28,12 @@
#include <stdbool.h>
#include <sys/types.h>
int urlencode(char *buf, int blen, const char *src, int slen);
int find_login(char *buf, int len);
bool valid_id(const char *id);
int b64_encode(const void *src, size_t srclen, void *dest, size_t destsize);
const char *format_size(size_t size);
#endif

View File

@@ -99,23 +99,11 @@ rm -rf /tmp/rtty-build
mkdir /tmp/rtty-build
pushd /tmp/rtty-build
git clone --recursive https://github.com/zhaojh329/libuwsc.git || {
echo "Clone libuwsc failed"
exit 1
}
sleep 2
git clone https://github.com/zhaojh329/rtty.git || {
echo "Clone rtty failed"
exit 1
}
# libuwsc
rm -f /usr/local/lib/libuwsc.*
cd libuwsc && cmake . && make install && cd -
[ $? -eq 0 ] || exit 1
# rtty
cd rtty && cmake . && make install
[ $? -eq 0 ] || exit 1