tuyau

Client/server for transfering files (like cp)
git clone https://noulin.net/git/tuyau.git
Log | Files | Refs | README

commit 524eab401a2a695dd7ae27e9fa89ecf24f3603b1
parent 0094637ea1b32c0decd88b440a121864502dae48
Author: Remy Noulin <loader2x@gmail.com>
Date:   Thu,  1 Sep 2022 21:07:03 +0200

add argument to server for a custom config, implement port setting in server configuration, add relay functionality

NOTES.md          |   4 +-
README.md         |  53 +++++++++++++-
sclient.c         |   2 +-
serverPackage.yml |   2 +-
sserver.c         | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
5 files changed, 260 insertions(+), 14 deletions(-)

Diffstat:
MNOTES.md | 4++--
MREADME.md | 53++++++++++++++++++++++++++++++++++++++++++++++++++---
Msclient.c | 2+-
MserverPackage.yml | 2+-
Msserver.c | 213++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
5 files changed, 260 insertions(+), 14 deletions(-)

diff --git a/NOTES.md b/NOTES.md @@ -76,11 +76,11 @@ port 1032 alias: hostname: ip token: QE123123 - root: /home/rlp/tmp/ + root: /home/USER/tmp/ port: 1022 ~/.tuyau/serverConfig.yml --- port: 1023 // optional user: token: QE123123 - root: /home/rlp/tmp/ + root: /home/USER/tmp/ diff --git a/README.md b/README.md @@ -10,33 +10,80 @@ spm -g install tuyau tuyauServer # configuration In server, create ~/.tuyau/serverConfig.yml +``` --- - port: 1023 // optional + port: 1033 // optional ausername(string): token: 8 random non zero 8 bit values root: apath(home) - +``` In client, create ~/.tuyau/config.yml +``` --- ausername(string, this is the server name): hostname: ip // server ip token: 8 random non zero 8 bit values, same as server config - port: 1022 + port: 1032 +``` # usage - start server - run client +Example configuration: +``` +# client +# ~/.tuyau/config.yml +--- + serverName: + hostname: ip + token: QE123123 + root: /home/USER/tmp/ + port: 1032 +# server +~/.tuyau/serverConfig.yml +--- + port: 1032 // optional + serverName: + token: QE123123 + root: /home/USER/tmp/ + relay: + hostname: ip + token: a2345678 + port: 1032 +``` + +Start server on remote machine: +``` +# create a certificate for the server (before first start) +openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes -subj '/CN=your_fqdn' +tuyauServer +``` + Upload files: +``` tuyau files serverName tuyau files serverName:path/ tuyau file serverName:path/newfilename +``` + +Upload files using `serverName` as a relay to the machine called `relay`: +``` +tuyau files relay +``` Downdload files (globbing is supported): +``` tuyau serverName:path/files >> downloads to current directory tuyau serverName:path/files path/ tuyau serverName:path/file path/newfilename +``` + +Download files for the machine called `relay` using `serverName` as a relay: +``` +tuyau relay:path/files +``` # Commits 1 - send a single file diff --git a/sclient.c b/sclient.c @@ -164,7 +164,6 @@ int main(int ARGC, char** ARGV) { int sock; struct sockaddr_in server; struct hostent *hp; - int rval; cleanAllocateNetFrame(netframe); @@ -173,6 +172,7 @@ int main(int ARGC, char** ARGV) { sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0){ perror("Failed to create socket"); + ret 1; } server.sin_family = AF_INET; diff --git a/serverPackage.yml b/serverPackage.yml @@ -1,6 +1,6 @@ --- name: tuyauServer - version: 0.0.5 + version: 0.0.6 description: "Server for copying files with tuyau" bin: ./tuyauServer.c #cflags: -DA -g3 -std=gnu11 -fPIC -pipe diff --git a/sserver.c b/sserver.c @@ -2,6 +2,7 @@ /* or direct path to sheepy: #! /usr/local/bin/sheepy */ /* Libsheepy documentation: https://spartatek.se/libsheepy/ */ +#include <netdb.h> // for gethostbyname #include <netinet/in.h> #include <string.h> #include <stdlib.h> @@ -40,6 +41,7 @@ #define CONFIG "~/.tuyau/serverConfig.yml" int packetCount = 0; +// file description for writing received chunks of big files int fd = -1; u64 transferSz = 0; // client socket @@ -49,12 +51,31 @@ rateLimitert *rateLim = null; u32 clientIp = 0; smallJsont *tokens = null; +smallJsont *relays = null; + +// relay variables +SSL_CTX *relay_ssl_ctx = null; +SSL *relay_ssl = null; +int relay_sock = -1; +netFramet *relay_netframe = null; +// when the client sends files to the destination +// and the client closes the connection to this relay server, +// close the connection to the destination normaly. +// when the destination closes the connection only cleanup of the destination +// connection is needed +// This is needed because when the client closes the connection, the receive loop +// breaks. +bool closeDestConnection = no; /* enable/disable logging */ /* #undef pLog */ /* #define pLog(...) */ +void closeRelayConnectionToDest(void); void saveReceivedData(void *receiveB, size_t sz); +bool relayForwardDataToClient(void *receiveB, size_t sz, void *context); + +// TODO return error from netframe callback bool cb(void *buf, size_t size, void *context) { saveReceivedData(buf, size); @@ -118,20 +139,38 @@ int main(int ARGC, char** ARGV) { // open listen socket // start event loop + char *c = ARGV[1] ? ARGV[1] : CONFIG; + // load configuration - cleanCharP(config) = expandHome(CONFIG); + cleanCharP(config) = expandHome(c); cleanAllocateSmallJson(cfg); readFileG(cfg, config); - // create tokens dict + lv(cfg); + + if (isEmptyG(cfg)) { + logC("Empty configuration %s", c); + ret 1; + } + tokens = allocG(rtSmallJsont); + relays = allocG(rtSmallJsont); iter(cfg, D) { + if (!isOSmallDictG(D)) continue; cast(smallDictt*,d,D); - setG(tokens, $(d,"token"), $(d,"root")); + if (hasG(d, "root")) { + // local home + setG(tokens, $(d,"token"), $(d,"root")); + } + elif (hasG(d, "hostname")) { + // relay + setG(relays, $(d,"token"), d); + } } lv(tokens); + lv(relays); // open listen socket int sock; @@ -144,9 +183,11 @@ int main(int ARGC, char** ARGV) { XFailure; } + u16 port = hasG(cfg, "port") ? u$(cfg, "port") : 1032; + lv(port); server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; - server.sin_port = htons(1032); // TODO allow changing port + server.sin_port = htons(port); if (bind(sock, (struct sockaddr *) &server, sizeof(server))){ perror("bind failed"); @@ -184,20 +225,109 @@ int main(int ARGC, char** ARGV) { else { var r = o(netframe,receive, ssl); logVarG(r); + if (closeDestConnection) { + // close client connection + o(netframe,end, relay_ssl); + } } // release SSL state SSL_free(ssl); close(mysock); + if (relay_sock != -1) { + if (closeDestConnection) { + // some data needs to be received in order to send all data to the server, SSL_write says all the data is sent but it is not true, why? + // 478 bytes are received. It seems to be encrypted. + // SSL_shutdown() and shutdown() don't help + u8 buf[1024*1024] = init0Var; + int r = recv(relay_sock, buf, sizeof(buf), 0); + if (r == -1) { + logE("recv before closing the socket"); + } + } + closeRelayConnectionToDest(); + } } } SSL_CTX_free(ctx); + terminateG(relays); terminateG(tokens); terminateO(rateLim); } +// connect to destination, this server is a relay +bool connectToDest(smallDictt *svrInfo) { + + relay_sock = socket(AF_INET, SOCK_STREAM, 0); + if (relay_sock < 0){ + perror("Failed to create socket"); + ret no; + } + + struct sockaddr_in server; + struct hostent *hp; + + server.sin_family = AF_INET; + + hp = gethostbyname($(svrInfo, "hostname")); + if (hp==0) { + perror("gethostbyname failed"); + close(relay_sock); + relay_sock = -1; + ret no; + } + + memcpy(&server.sin_addr, hp->h_addr, hp->h_length); + server.sin_port = htons(u$(svrInfo, "port")); + + if (connect(relay_sock,(struct sockaddr *) &server, sizeof(server))){ + perror("connect failed"); + close(relay_sock); + relay_sock = -1; + ret no; + } + + relay_ssl_ctx = SSL_CTX_new(TLS_method()); + BIO *sbio = BIO_new(BIO_f_ssl()); + relay_ssl = SSL_new(relay_ssl_ctx); + // attach the socket descriptor + SSL_set_fd(relay_ssl, relay_sock); + + // perform the connection + if (SSL_connect(relay_ssl) == -1) { + logE("Couldn't establish the TLS connection"); + // clean state + closeRelayConnectionToDest(); + ret no; + } + + ret yes; +} + +void closeRelayConnectionToDest(void) { + // release SSL state + SSL_free(relay_ssl); + SSL_CTX_free(relay_ssl_ctx); + // TODO add recv to send all data? + close(relay_sock); + // clear relay state + relay_ssl = null; + relay_ssl_ctx = null; + relay_sock = -1; + terminateG(relay_netframe); +} + void saveReceivedData(void *receiveB, size_t sz) { logVarG(sz); + + if (relay_sock != -1) { + // this server is a relay and the connection to the destination + // is already established + // forward data from client to destination + o(relay_netframe,send , relay_ssl, receiveB, sz); + ret; + } + if (packetCount == 0) { // check packet size if (sz < (1/*command*/ + 8/*token*/)) ret; @@ -208,10 +338,21 @@ void saveReceivedData(void *receiveB, size_t sz) { char tk[9] = init0Var; memcpy(tk, token, 8); + cleanFinishSmallDictP(svrInfo) = null; + if (not hasG(tokens, tk)) { - incomingG(rateLim, clientIp); - logE("token not found"); - ret; + if (hasG(relays, tk)) { + // relay packets to destination + svrInfo = getG(relays, rtSmallDictt, tk); + logD("Relay to"); + lv(svrInfo); + } + else { + // invalid connection, block ip + incomingG(rateLim, clientIp); + logE("token not found"); + ret; + } } char *root = $(tokens, tk); @@ -219,6 +360,19 @@ void saveReceivedData(void *receiveB, size_t sz) { if (*command == 0) { // receive file + if (svrInfo) { + // this server is a relay, connect to destination + if (not connectToDest(svrInfo)) { + logE("Relay: Can't connect to destination %m", svrInfo); + ret; + } + relay_netframe = allocNetFrame(); + // forward data from client to destination + closeDestConnection = yes; + o(relay_netframe,send , relay_ssl, receiveB, sz); + ret; + } + // check packet size if (sz < (1/*command*/ + 8/*token*/ + 2 /*filenameLength*/ + 1 /*filename*/)) ret; u16 *filenameLength = (u16*)(receiveB + 9); @@ -327,6 +481,19 @@ void saveReceivedData(void *receiveB, size_t sz) { elif (*command == 1) { // mkdir + if (svrInfo) { + // this server is a relay, connect to destination + if (not connectToDest(svrInfo)) { + logE("Relay: Can't connect to destination %m", svrInfo); + ret; + } + relay_netframe = allocNetFrame(); + // forward data from client to destination + closeDestConnection = yes; + o(relay_netframe,send , relay_ssl, receiveB, sz); + ret; + } + // check packet size if (sz < (1/*command*/ + 8/*token*/ + 2 /*filemode*/ + 2 /*pathLength*/ + 1 /*path*/)) ret; u16 *filemode = (u16*)(receiveB + 9); @@ -346,6 +513,31 @@ void saveReceivedData(void *receiveB, size_t sz) { pError0(fileChmod(path, *filemode)); } elif (*command == 2) { + // send files to client + + if (svrInfo) { + // this server is a relay, connect to destination + if (not connectToDest(svrInfo)) { + logE("Relay: Can't connect to destination %m", svrInfo); + ret; + } + relay_netframe = allocNetFrame(); + // forward data from client to destination + closeDestConnection = no; + o(relay_netframe,send , relay_ssl, receiveB, sz); + + cleanAllocateNetFrame(netframe); + + o(relay_netframe, setCallback, relayForwardDataToClient, netframe /*context*/); + + var r = o(relay_netframe,receive, relay_ssl); + logVarG(r); + + // close client connection + o(netframe,end, ssl); + ret; + } + // check packet size, sendFile path has to be at least 1 byte if (sz < (1/*command*/ + 8/*token*/ + 2 /*sendFileLength*/ + 1 /*sendFile*/)) ret; logD("send files to client"); @@ -564,4 +756,11 @@ void saveReceivedData(void *receiveB, size_t sz) { else transferSz -= sz; } } + +bool relayForwardDataToClient(void *receiveB, size_t sz, void *context) { + cast(netFramet*, netframe, context); + // forward data from destination to client + o(netframe,send , ssl, receiveB, sz); + ret yes; +} // vim: set expandtab ts=2 sw=2: