Add support KCP protocol

https://github.com/skywind3000/kcp/blob/master/README.en.md

Signed-off-by: Jianhui Zhao <zhaojh329@gmail.com>
This commit is contained in:
Jianhui Zhao
2025-09-10 10:36:35 +08:00
committed by Jianhui Zhao
parent 1cfc93852c
commit d1fa4efd86
15 changed files with 969 additions and 30 deletions
+3
View File
@@ -7,3 +7,6 @@
[submodule "src/log"]
path = src/log
url = https://github.com/zhaojh329/log.git
[submodule "src/kcp"]
path = src/kcp
url = https://github.com/skywind3000/kcp.git
+21 -3
View File
@@ -3,25 +3,43 @@ set(RTTY_VERSION_MAJOR 9)
set(RTTY_VERSION_MINOR 0)
set(RTTY_VERSION_PATCH 2)
option(KCP_SUPPORT "KCP support" ON)
# Check the third party Libraries
find_package(Libev REQUIRED)
aux_source_directory(. SRCS)
aux_source_directory(log SRCS)
aux_source_directory(buffer SRCS)
add_subdirectory(ssl)
list(APPEND SRCS main.c rtty.c net.c utils.c file.c filectl.c command.c http.c)
if(KCP_SUPPORT)
if (NOT SSL_SUPPORT)
message(WARNING "SSL Not enabled, disable KCP")
set(KCP_SUPPORT OFF)
else()
list(APPEND SRCS kcp.c crc32.c crypt.c)
endif()
endif()
add_executable(rtty ${SRCS})
target_compile_definitions(rtty PRIVATE _GNU_SOURCE)
target_compile_options(rtty PRIVATE -O -Wall -Werror --std=gnu99)
target_include_directories(rtty PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/buffer ${LIBEV_INCLUDE_DIR})
target_link_libraries(rtty PRIVATE ${LIBEV_LIBRARY} util crypt m)
add_subdirectory(ssl)
if(SSL_SUPPORT)
target_compile_definitions(rtty PRIVATE ${SSL_DEFINE})
target_link_libraries(rtty PRIVATE ${SSL_TARGET})
endif()
if(KCP_SUPPORT)
add_subdirectory(kcp)
target_link_libraries(rtty PRIVATE kcp)
endif()
# configure a header file to pass some of the CMake settings to the source code
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
+1
View File
@@ -31,5 +31,6 @@
#define RTTY_VERSION_STRING "@RTTY_VERSION_MAJOR@.@RTTY_VERSION_MINOR@.@RTTY_VERSION_PATCH@"
#cmakedefine SSL_SUPPORT
#cmakedefine KCP_SUPPORT
#endif
+120
View File
@@ -0,0 +1,120 @@
/*
* 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/types.h>
#include <stdbool.h>
#include <string.h>
#include <stdint.h>
#define IEEE 0xedb88320
#define slicing8_cutoff 16
typedef uint32_t table[256];
typedef struct slicing8_table {
table tables[8];
} slicing8_table;
static slicing8_table tab;
static void simple_populate_table(uint32_t poly, table t)
{
int i, j;
for (i = 0; i < 256; i++) {
uint32_t crc = i;
for (j = 0; j < 8; j++) {
if (crc & 1) {
crc = (crc >> 1) ^ poly;
} else {
crc >>= 1;
}
}
t[i] = crc;
}
}
static void slicing_make_table(uint32_t poly)
{
int i, j;
simple_populate_table(poly, tab.tables[0]);
for (i = 0; i < 256; i++) {
uint32_t crc = tab.tables[0][i];
for (j = 1; j < 8; j++) {
crc = tab.tables[0][crc & 0xFF] ^ (crc >> 8);
tab.tables[j][i] = crc;
}
}
}
static uint32_t simple_update(uint32_t crc, const table tab, const uint8_t *p, size_t len)
{
size_t i;
crc = ~crc;
for (i = 0; i < len; i++)
crc = tab[(uint8_t)crc ^ p[i]] ^ (crc >> 8);
return ~crc;
}
uint32_t crc32_checksum_ieee(const uint8_t* p, size_t len)
{
static bool inited;
uint32_t crc = 0;
if (!inited) {
inited = true;
slicing_make_table(IEEE);
}
if (len >= slicing8_cutoff) {
crc = ~crc;
while (len >= 8) {
uint32_t n;
memcpy(&n, p, sizeof(n));
crc ^= n;
crc = tab.tables[0][p[7]] ^
tab.tables[1][p[6]] ^
tab.tables[2][p[5]] ^
tab.tables[3][p[4]] ^
tab.tables[4][crc >> 24] ^
tab.tables[5][(crc >> 16) & 0xFF] ^
tab.tables[6][(crc >> 8) & 0xFF] ^
tab.tables[7][crc & 0xFF];
p += 8;
len -= 8;
}
crc = ~crc;
}
if (len == 0)
return crc;
return simple_update(crc, tab.tables[0], p, len);
}
+33
View File
@@ -0,0 +1,33 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef RTTY_CRC32_H
#define RTTY_CRC32_H
#include <sys/types.h>
#include <stdint.h>
uint32_t crc32_checksum_ieee(const uint8_t *p, size_t len);
#endif
+268
View File
@@ -0,0 +1,268 @@
/*
* 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/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#ifdef HAVE_OPENSSL
#include <openssl/evp.h>
#elif defined(HAVE_WOLFSSL)
#include <wolfssl/options.h>
#include <wolfssl/openssl/evp.h>
#else
#include <mbedtls/pkcs5.h>
#include <mbedtls/aes.h>
#include <mbedtls/md.h>
#endif
#include "crypt.h"
void *cipher_init_ctx(const char *password)
{
uint8_t key[32];
#if defined(HAVE_OPENSSL) || defined(HAVE_WOLFSSL)
EVP_CIPHER_CTX *cipher = EVP_CIPHER_CTX_new();
PKCS5_PBKDF2_HMAC(password, strlen(password), NULL, 0, 1024, EVP_sha256(), 32, key);
EVP_EncryptInit(cipher, EVP_aes_256_ecb(), key, NULL);
return cipher;
#else
mbedtls_aes_context *aes_ctx;
mbedtls_md_context_t md_ctx;
mbedtls_md_init(&md_ctx);
mbedtls_md_setup(&md_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1);
mbedtls_pkcs5_pbkdf2_hmac(&md_ctx, (const uint8_t *)password, strlen(password),
NULL, 0, 1024, 32, key);
mbedtls_md_free(&md_ctx);
aes_ctx = malloc(sizeof(mbedtls_aes_context));
mbedtls_aes_init(aes_ctx);
mbedtls_aes_setkey_enc(aes_ctx, key, 256);
return aes_ctx;
#endif
}
int get_random_bytes(uint8_t *out, size_t len)
{
int ret = 0;
int fd;
fd = open("/dev/urandom", O_RDONLY);
if (fd < 0)
return -1;
if (read(fd, out, len) < 0)
ret = -1;
close(fd);
return ret;
}
static uint8_t initial_vector[] = {167, 115, 79, 156, 18, 172, 27, 1, 164, 21, 242, 193, 252, 120, 230, 107};
static void xor_bytes(uint8_t *dst, const uint8_t *a, const uint8_t *b, size_t n)
{
for (size_t i = 0; i < n; i++) {
dst[i] = a[i] ^ b[i];
}
}
static inline void aes_block_encrypt(void *ctx, uint8_t *dst, const uint8_t *src)
{
#if defined(HAVE_OPENSSL) || defined(HAVE_WOLFSSL)
int out_len;
EVP_EncryptUpdate(ctx, dst, &out_len, src, 16);
#else
mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, src, dst);
#endif
}
void encrypt16(void *ctx, uint8_t *dst, const uint8_t *src, size_t src_len)
{
size_t n = src_len / 16;
size_t repeat = n / 8;
size_t left = n % 8;
size_t remaining;
size_t base = 0;
uint8_t tbl[16];
aes_block_encrypt(ctx, tbl, initial_vector);
for (size_t i = 0; i < repeat; i++) {
const uint8_t *s = src + base;
uint8_t *d = dst + base;
for (int j = 0; j < 8; j++) {
xor_bytes(d + j*16, s + j*16, tbl, 16);
aes_block_encrypt(ctx, tbl, d + j * 16);
}
base += 128;
}
switch (left) {
case 7:
xor_bytes(dst + base, src + base, tbl, 16);
aes_block_encrypt(ctx, tbl, dst + base);
base += 16;
case 6:
xor_bytes(dst + base, src + base, tbl, 16);
aes_block_encrypt(ctx, tbl, dst + base);
base += 16;
case 5:
xor_bytes(dst + base, src + base, tbl, 16);
aes_block_encrypt(ctx, tbl, dst + base);
base += 16;
case 4:
xor_bytes(dst + base, src + base, tbl, 16);
aes_block_encrypt(ctx, tbl, dst + base);
base += 16;
case 3:
xor_bytes(dst + base, src + base, tbl, 16);
aes_block_encrypt(ctx, tbl, dst + base);
base += 16;
case 2:
xor_bytes(dst + base, src + base, tbl, 16);
aes_block_encrypt(ctx, tbl, dst + base);
base += 16;
case 1:
xor_bytes(dst + base, src + base, tbl, 16);
aes_block_encrypt(ctx, tbl, dst + base);
base += 16;
case 0:
remaining = src_len - base;
if (remaining > 0)
xor_bytes(dst + base, src + base, tbl, remaining);
break;
}
}
void decrypt16(void *ctx, uint8_t *dst, const uint8_t *src, size_t src_len)
{
size_t n = src_len / 16;
size_t repeat = n / 8;
size_t left = n % 8;
size_t base = 0;
uint8_t buf[32];
uint8_t *tbl = buf;
uint8_t *next = buf + 16;
uint8_t *temp_ptr;
aes_block_encrypt(ctx, tbl, initial_vector);
for (size_t i = 0; i < repeat; i++) {
const uint8_t *s = src + base;
uint8_t *d = dst + base;
aes_block_encrypt(ctx, next, s);
xor_bytes(d, s, tbl, 16);
aes_block_encrypt(ctx, tbl, s + 16);
xor_bytes(d + 16, s + 16, next, 16);
aes_block_encrypt(ctx, next, s + 32);
xor_bytes(d + 32, s + 32, tbl, 16);
aes_block_encrypt(ctx, tbl, s + 48);
xor_bytes(d + 48, s + 48, next, 16);
aes_block_encrypt(ctx, next, s + 64);
xor_bytes(d + 64, s + 64, tbl, 16);
aes_block_encrypt(ctx, tbl, s + 80);
xor_bytes(d + 80, s + 80, next, 16);
aes_block_encrypt(ctx, next, s + 96);
xor_bytes(d + 96, s + 96, tbl, 16);
aes_block_encrypt(ctx, tbl, s + 112);
xor_bytes(d + 112, s + 112, next, 16);
base += 128;
}
switch (left) {
case 7:
aes_block_encrypt(ctx, next, src + base);
xor_bytes(dst + base, src + base, tbl, 16);
temp_ptr = tbl;
tbl = next;
next = temp_ptr;
base += 16;
case 6:
aes_block_encrypt(ctx, next, src + base);
xor_bytes(dst + base, src + base, tbl, 16);
temp_ptr = tbl;
tbl = next;
next = temp_ptr;
base += 16;
case 5:
aes_block_encrypt(ctx, next, src + base);
xor_bytes(dst + base, src + base, tbl, 16);
temp_ptr = tbl;
tbl = next;
next = temp_ptr;
base += 16;
case 4:
aes_block_encrypt(ctx, next, src + base);
xor_bytes(dst + base, src + base, tbl, 16);
temp_ptr = tbl;
tbl = next;
next = temp_ptr;
base += 16;
case 3:
aes_block_encrypt(ctx, next, src + base);
xor_bytes(dst + base, src + base, tbl, 16);
temp_ptr = tbl;
tbl = next;
next = temp_ptr;
base += 16;
case 2:
aes_block_encrypt(ctx, next, src + base);
xor_bytes(dst + base, src + base, tbl, 16);
temp_ptr = tbl;
tbl = next;
next = temp_ptr;
base += 16;
case 1:
aes_block_encrypt(ctx, next, src + base);
xor_bytes(dst + base, src + base, tbl, 16);
temp_ptr = tbl;
tbl = next;
next = temp_ptr;
base += 16;
case 0:
size_t remaining = src_len - base;
if (remaining > 0)
xor_bytes(dst + base, src + base, tbl, remaining);
break;
}
}
+37
View File
@@ -0,0 +1,37 @@
/*
* 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_CRYPT_H
#define RTTY_CRYPT_H
#include <stdint.h>
void *cipher_init_ctx(const char *password);
int get_random_bytes(uint8_t *out, size_t len);
void encrypt16(void *ctx, uint8_t *dst, const uint8_t *src, size_t src_len);
void decrypt16(void *ctx, uint8_t *dst, const uint8_t *src, size_t src_len);
#endif
Submodule
+1
Submodule src/kcp added at f4f3a89cc6
+241
View File
@@ -0,0 +1,241 @@
/*
* 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 <endian.h>
#include <errno.h>
#include "log/log.h"
#include "crc32.h"
#include "crypt.h"
#include "rtty.h"
#define NONCE_SIZE 16
#define CRC_SIZE 4
#define CRYPT_HEADER_SIZE (NONCE_SIZE + CRC_SIZE)
#ifdef KCP_SUPPORT
void rtty_kcp_init_cipher(struct rtty_kcp *kcp)
{
if (!kcp->password)
return;
kcp->cipher = cipher_init_ctx(kcp->password);
}
static uint32_t rtty_kcp_now(struct rtty *rtty)
{
long now = ev_now(rtty->loop) * 1000;
return now & 0xffffffff;
}
void rtty_kcp_check(struct rtty *rtty)
{
uint32_t now = rtty_kcp_now(rtty);
uint32_t next;
long delay;
next = ikcp_check(rtty->kcp.kcp, now);
delay = next - now;
if (delay <= 0)
delay = 1;
ev_timer_set(&rtty->kcp.tmr, 0, delay / 1000.0);
ev_timer_again(rtty->loop, &rtty->kcp.tmr);
}
static int rtty_kcp_encrypt(struct rtty_kcp *kcp, uint8_t *dst, const uint8_t *src, int srclen)
{
uint32_t crc32;
get_random_bytes(dst, NONCE_SIZE);
crc32 = crc32_checksum_ieee(src, srclen);
crc32 = htole32(crc32);
memcpy(dst + NONCE_SIZE, &crc32, CRC_SIZE);
memcpy(dst + CRYPT_HEADER_SIZE, src, srclen);
encrypt16(kcp->cipher, dst, dst, srclen + CRYPT_HEADER_SIZE);
return CRYPT_HEADER_SIZE + srclen;
}
static int rtty_kcp_decrypt(struct rtty_kcp *kcp, uint8_t *dst, const uint8_t *src, int srclen)
{
uint32_t crc32_your, crc32_our;
if (srclen < CRYPT_HEADER_SIZE) {
log_debug("data length too short\n");
return -1;
}
decrypt16(kcp->cipher, dst, src, srclen);
crc32_our = crc32_checksum_ieee(src + CRYPT_HEADER_SIZE, srclen - CRYPT_HEADER_SIZE);
memcpy(&crc32_your, dst + NONCE_SIZE, CRC_SIZE);
if (le32toh(crc32_your) !=crc32_our) {
log_debug("crc32 error\n");
return -1;
}
return srclen;
}
static int udp_output(const char *buf, int len, ikcpcb *kcp, void *user)
{
struct rtty *rtty = user;
if (rtty->kcp.password) {
uint8_t dst[1500];
int ret= rtty_kcp_encrypt(&rtty->kcp, dst, (const uint8_t *)buf, len);
return send(rtty->sock, dst, ret, 0);
}
return send(rtty->sock, buf, len, 0);
}
static void kcp_timer_cb(struct ev_loop *loop, struct ev_timer *w, int revents)
{
struct rtty *rtty = container_of(w, struct rtty, kcp.tmr);
uint32_t now = rtty_kcp_now(rtty);
ikcp_update(rtty->kcp.kcp, now);
rtty_kcp_check(rtty);
}
int rtty_kcp_init(struct rtty *rtty)
{
ikcpcb *kcp;
if (!rtty->kcp.on)
return 0;
kcp = ikcp_create(random(), rtty);
if (!kcp) {
log_err("malloc: %s\n", strerror(errno));
return -1;
}
ikcp_setoutput(kcp, udp_output);
ikcp_nodelay(kcp, rtty->kcp.nodelay, rtty->kcp.interval,
rtty->kcp.resend, rtty->kcp.nc);
ikcp_wndsize(kcp, rtty->kcp.sndwnd, rtty->kcp.rcvwnd);
ikcp_setmtu(kcp, rtty->kcp.mtu);
ev_timer_init(&rtty->kcp.tmr, kcp_timer_cb, 1.0, 0);
rtty->kcp.kcp = kcp;
return 0;
}
void rtty_kcp_release(struct rtty *rtty)
{
if (rtty->kcp.kcp)
return;
ikcp_release(rtty->kcp.kcp);
rtty->kcp.kcp = NULL;
}
int rtty_kcp_read(struct rtty *rtty, int fd)
{
char buf[1500];
char *in = buf;
int ret;
ret = read(fd, buf, sizeof(buf));
if (ret < 0) {
log_err("socket read error: %s\n", strerror(errno));
return -1;
}
if (rtty->kcp.password) {
ret = rtty_kcp_decrypt(&rtty->kcp, (uint8_t *)buf, (const uint8_t *)buf, ret);
if (ret < 0)
return -1;
in = buf + CRYPT_HEADER_SIZE;
ret -= CRYPT_HEADER_SIZE;
}
ret = ikcp_input(rtty->kcp.kcp, in, ret);
if (ret < 0) {
log_err("ikcp_input fail: %d\n", ret);
return -1;
}
while (true) {
ret = ikcp_recv(rtty->kcp.kcp, buf, sizeof(buf));
if (ret < 0)
break;
buffer_put_data(&rtty->rb, buf, ret);
}
rtty_kcp_check(rtty);
return 0;
}
int rtty_kcp_write(struct rtty *rtty, int fd)
{
struct buffer *b = &rtty->wb;
ikcpcb *kcp = rtty->kcp.kcp;
size_t mss = kcp->mss;
int ret;
if (rtty->kcp.password)
mss -= CRYPT_HEADER_SIZE;
while (true) {
size_t len = buffer_length(b);
if (len == 0)
break;
if (len > mss)
len = mss;
ret = ikcp_send(kcp, buffer_data(b), len);
if (ret < 0) {
log_err("ikcp_send fail: %d\n", ret);
return -1;
}
buffer_pull(b, NULL, ret);
}
rtty_kcp_check(rtty);
return 0;
}
#endif
+61
View File
@@ -0,0 +1,61 @@
/*
* 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_KCP_H
#define RTTY_KCP_H
#include <stdbool.h>
#include <ev.h>
#include "kcp/ikcp.h"
struct rtty;
struct rtty_kcp {
bool on;
ikcpcb *kcp;
struct ev_timer tmr;
const char *password;
void *cipher;
bool nodelay;
int interval;
int resend;
bool nc;
int sndwnd;
int rcvwnd;
int mtu;
};
void rtty_kcp_init_cipher(struct rtty_kcp *kcp);
void rtty_kcp_check(struct rtty *rtty);
int rtty_kcp_init(struct rtty *rtty);
void rtty_kcp_release(struct rtty *rtty);
int rtty_kcp_read(struct rtty *rtty, int fd);
int rtty_kcp_write(struct rtty *rtty, int fd);
#endif
+67 -2
View File
@@ -34,7 +34,18 @@
#include "rtty.h"
enum {
LONG_OPT_HELP = 1
LONG_OPT_HELP = 1,
#ifdef KCP_SUPPORT
LONG_OPT_KCP,
LONG_OPT_KCP_NODELAY,
LONG_OPT_KCP_INTERVAL,
LONG_OPT_KCP_RESEND,
LONG_OPT_KCP_NC,
LONG_OPT_KCP_SNDWND,
LONG_OPT_KCP_RCVWND,
LONG_OPT_KCP_MTU,
LONG_OPT_KCP_KEY,
#endif
};
#ifdef SSL_SUPPORT
@@ -70,6 +81,17 @@ static struct option long_options[] = {
{"port", required_argument, NULL, 'p'},
{"description", required_argument, NULL, 'd'},
{"token", required_argument, NULL, 't'},
#ifdef KCP_SUPPORT
{"kcp", no_argument, NULL, LONG_OPT_KCP},
{"kcp-nodelay", no_argument, NULL, LONG_OPT_KCP_NODELAY},
{"kcp-interval", required_argument, NULL, LONG_OPT_KCP_INTERVAL},
{"kcp-resend", required_argument, NULL, LONG_OPT_KCP_RESEND},
{"kcp-nc", no_argument, NULL, LONG_OPT_KCP_NC},
{"kcp-sndwnd", required_argument, NULL, LONG_OPT_KCP_SNDWND},
{"kcp-rcvwnd", required_argument, NULL, LONG_OPT_KCP_RCVWND},
{"kcp-mtu", required_argument, NULL, LONG_OPT_KCP_MTU},
{"kcp-key", required_argument, NULL, LONG_OPT_KCP_KEY},
#endif
#ifdef SSL_SUPPORT
{"cacert", required_argument, NULL, 'C'},
{"insecure", no_argument, NULL, 'x'},
@@ -92,6 +114,17 @@ static void usage(const char *prog)
" -d, --description=string Add a description to the device(Maximum 126 bytes)\n"
" -a Auto reconnect to the server\n"
" -i number Set heartbeat interval in seconds(Default is 30s)\n"
#ifdef KCP_SUPPORT
" --kcp Using KCP protocol\n"
" --kcp-nodelay Whether enable nodelay mode for KCP\n"
" --kcp-interval number KCP protocol internal work interval(Default is 100ms)\n"
" --kcp-resend number Fast retransmission mode for KCP\n"
" --kcp-nc Whether to turn off flow control for KCP\n"
" --kcp-sndwnd number Maximum send window for KCP\n"
" --kcp-rcvwnd number Maximum receive window for KCP\n"
" --kcp-mtu number Maximum transmission unit for KCP\n"
" --kcp-key Key used to encrypt data\n"
#endif
#ifdef SSL_SUPPORT
" -s SSL on\n"
" -C, --cacert CA certificate to verify peer against\n"
@@ -129,7 +162,10 @@ int main(int argc, char **argv)
.host = "localhost",
.port = 5912,
.loop = loop,
.sock = -1
.sock = -1,
#ifdef KCP_SUPPORT
.kcp.interval = -1
#endif
};
#ifdef SSL_SUPPORT
bool has_cacert = false;
@@ -194,6 +230,35 @@ int main(int argc, char **argv)
case 'a':
rtty.reconnect = true;
break;
#ifdef KCP_SUPPORT
case LONG_OPT_KCP:
rtty.kcp.on = true;
break;
case LONG_OPT_KCP_NODELAY:
rtty.kcp.nodelay = true;
break;
case LONG_OPT_KCP_INTERVAL:
rtty.kcp.interval = atoi(optarg);
break;
case LONG_OPT_KCP_RESEND:
rtty.kcp.resend = atoi(optarg);
break;
case LONG_OPT_KCP_NC:
rtty.kcp.nc = true;
break;
case LONG_OPT_KCP_SNDWND:
rtty.kcp.sndwnd = atoi(optarg);
break;
case LONG_OPT_KCP_RCVWND:
rtty.kcp.rcvwnd = atoi(optarg);
break;
case LONG_OPT_KCP_MTU:
rtty.kcp.mtu = atoi(optarg);
break;
case LONG_OPT_KCP_KEY:
rtty.kcp.password = optarg;
break;
#endif
#ifdef SSL_SUPPORT
case 's':
rtty.ssl_on = true;
+55 -15
View File
@@ -167,22 +167,17 @@ err:
return -1;
}
int tcp_connect(struct ev_loop *loop, const char *host, int port,
void (*on_connected)(int sock, void *arg), void *arg)
static int getaddr_by_name(int socktype, const char *host, int port,
struct sockaddr *dest, int *addrlen)
{
struct sockaddr *addr = NULL;
struct addrinfo *result, *rp;
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_socktype = socktype,
.ai_flags = AI_ADDRCONFIG
};
int sock = -1;
int addrlen;
int ret;
log_debug("connecting to %s:%d\n", host, port);
ret = getaddrinfo(host, port2str(port), &hints, &result);
if (ret) {
if (ret == EAI_SYSTEM) {
@@ -194,22 +189,67 @@ int tcp_connect(struct ev_loop *loop, const char *host, int port,
return -1;
}
ret = -1;
for (rp = result; rp != NULL; rp = rp->ai_next) {
if (rp->ai_family == AF_INET || rp->ai_family == AF_INET6) {
addr = rp->ai_addr;
addrlen = rp->ai_addrlen;
memcpy(dest, rp->ai_addr, rp->ai_addrlen);
*addrlen = rp->ai_addrlen;
ret = 0;
break;
}
}
if (!addr) {
freeaddrinfo(result);
return ret;
}
int tcp_connect(struct ev_loop *loop, const char *host, int port,
void (*on_connected)(int sock, void *arg), void *arg)
{
struct sockaddr addr;
int addrlen;
log_debug("connecting to %s:%d\n", host, port);
if (getaddr_by_name(SOCK_STREAM, host, port, &addr, &addrlen)) {
log_err("getaddrinfo failed: Not found addr\n");
goto free_addrinfo;
return -1;
}
sock = tcp_connect_sockaddr(loop, addr, addrlen, on_connected, arg);
return tcp_connect_sockaddr(loop, &addr, addrlen, on_connected, arg);
}
int udp_connect(struct ev_loop *loop, const char *host, int port,
void (*on_connected)(int sock, void *arg), void *arg)
{
struct sockaddr addr;
int addrlen;
int sock;
int ret;
log_debug("connecting to %s:%d\n", host, port);
if (getaddr_by_name(SOCK_DGRAM, host, port, &addr, &addrlen)) {
log_err("getaddrinfo failed: Not found addr\n");
return -1;
}
sock = socket(addr.sa_family, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (sock < 0) {
log_err("create socket failed: %s\n", strerror(errno));
return -1;
}
ret = connect(sock, &addr, addrlen);
if (ret < 0) {
close(sock);
log_err("connect '%s:%d' failed: %s\n", host, port, strerror(errno));
return -1;
}
on_connected(sock, arg);
free_addrinfo:
freeaddrinfo(result);
return sock;
}
+3
View File
@@ -34,4 +34,7 @@ int tcp_connect(struct ev_loop *loop, const char *host, int port,
int tcp_connect_sockaddr(struct ev_loop *loop, const struct sockaddr *addr, socklen_t addrlen,
void (*on_connected)(int sock, void *arg), void *arg);
int udp_connect(struct ev_loop *loop, const char *host, int port,
void (*on_connected)(int sock, void *arg), void *arg);
#endif
+51 -10
View File
@@ -302,6 +302,10 @@ void rtty_exit(struct rtty *rtty)
del_tty(tty);
}
#ifdef KCP_SUPPORT
rtty_kcp_release(rtty);
#endif
#ifdef SSL_SUPPORT
if (rtty->ssl) {
ssl_session_free(rtty->ssl);
@@ -608,10 +612,18 @@ static void on_net_read(struct ev_loop *loop, struct ev_io *w, int revents)
goto err;
#endif
} else {
ret = buffer_put_fd(&rtty->rb, w->fd, 4096, &eof);
if (ret < 0) {
log_err("socket read error: %s\n", strerror(errno));
goto err;
#ifdef KCP_SUPPORT
if (rtty->kcp.on) {
if (rtty_kcp_read(rtty, w->fd))
goto err;
} else
#endif
{
ret = buffer_put_fd(&rtty->rb, w->fd, 4096, &eof);
if (ret < 0) {
log_err("socket read error: %s\n", strerror(errno));
goto err;
}
}
}
@@ -659,10 +671,18 @@ static void on_net_write(struct ev_loop *loop, struct ev_io *w, int revents)
buffer_pull(b, NULL, ret);
#endif
} else {
ret = buffer_pull_to_fd(&rtty->wb, w->fd, -1);
if (ret < 0) {
log_err("socket write error: %s\n", strerror(errno));
goto err;
#ifdef KCP_SUPPORT
if (rtty->kcp.on) {
if (rtty_kcp_write(rtty, w->fd))
goto err;
} else
#endif
{
ret = buffer_pull_to_fd(&rtty->wb, w->fd, -1);
if (ret < 0) {
log_err("socket write error: %s\n", strerror(errno));
goto err;
}
}
}
@@ -688,12 +708,22 @@ static void on_net_connected(int sock, void *arg)
rtty->sock = sock;
#ifdef KCP_SUPPORT
if (rtty_kcp_init(rtty)) {
ev_break(rtty->loop, EVBREAK_ALL);
return;
}
#endif
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);
if (rtty->ssl_on) {
#ifdef KCP_SUPPORT
assert(!rtty->kcp.on);
#endif
#ifdef SSL_SUPPORT
rtty->ssl = ssl_session_new(rtty->ssl_ctx, sock);
if (!rtty->ssl) {
@@ -768,12 +798,23 @@ static void rtty_timer_cb(struct ev_loop *loop, struct ev_timer *w, int revents)
int rtty_start(struct rtty *rtty)
{
int ret;
rtty_run_state(RTTY_STATE_DISCONNECTED);
ev_init(&rtty->tmr, rtty_timer_cb);
if (tcp_connect(rtty->loop, rtty->host, rtty->port, on_net_connected, rtty) < 0
&& !rtty->reconnect)
#ifdef KCP_SUPPORT
if (rtty->kcp.on) {
rtty_kcp_init_cipher(&rtty->kcp);
ret = udp_connect(rtty->loop, rtty->host, rtty->port, on_net_connected, rtty);
} else
#endif
{
ret = tcp_connect(rtty->loop, rtty->host, rtty->port, on_net_connected, rtty);
}
if (ret < 0 && !rtty->reconnect)
return -1;
INIT_LIST_HEAD(&rtty->ttys);
+7
View File
@@ -37,6 +37,10 @@
#include "ssl/ssl.h"
#endif
#ifdef KCP_SUPPORT
#include "kcp.h"
#endif
#define RTTY_PROTO_VER 5
#define RTTY_MAX_TTY 10
#define RTTY_HEARTBEAT_TIMEOUT 3.0
@@ -122,6 +126,9 @@ struct rtty {
int ntty; /* tty number */
struct list_head ttys;
struct list_head http_conns;
#ifdef KCP_SUPPORT
struct rtty_kcp kcp;
#endif
};
int rtty_start(struct rtty *rtty);