mirror of
https://github.com/zhaojh329/rtty.git
synced 2026-02-27 09:53:17 +08:00
Drop the depend of libuwsc
Signed-off-by: Jianhui Zhao <zhaojh329@gmail.com>
This commit is contained in:
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "src/buffer"]
|
||||
path = src/buffer
|
||||
url = https://github.com/zhaojh329/buffer.git
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
41
README.md
41
README.md
@@ -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
|
||||
|
||||
40
README_ZH.md
40
README_ZH.md
@@ -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`
|
||||
|
||||
## 传输文件
|
||||
从本地传输文件到远程设备
|
||||
|
||||
@@ -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)
|
||||
37
cmake/Modules/FindMbedTLS.cmake
Normal file
37
cmake/Modules/FindMbedTLS.cmake
Normal 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)
|
||||
62
cmake/Modules/FindWolfSSL.cmake
Normal file
62
cmake/Modules/FindWolfSSL.cmake
Normal 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()
|
||||
@@ -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
1
src/buffer
Submodule
Submodule src/buffer added at 74d566b6c4
117
src/command.c
117
src/command.c
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
198
src/downfile.c
Normal 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
30
src/downfile.h
Normal 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
|
||||
423
src/file.c
423
src/file.c
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
38
src/file.h
38
src/file.h
@@ -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
1094
src/json.c
File diff suppressed because it is too large
Load Diff
295
src/json.h
295
src/json.h
@@ -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
130
src/log.c
Normal 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
44
src/log.h
Normal 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
|
||||
525
src/main.c
525
src/main.c
@@ -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
171
src/net.c
Normal 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
33
src/net.h
Normal 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
508
src/rtty.c
Normal 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
89
src/rtty.h
Normal 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
219
src/ssl.c
Normal 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
46
src/ssl.h
Normal 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
157
src/upfile.c
Normal 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
30
src/upfile.h
Normal 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
|
||||
87
src/utils.c
87
src/utils.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user