tuyau

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

sserver.c (22826B)


      1 #! /usr/bin/env sheepy
      2 /* or direct path to sheepy: #! /usr/local/bin/sheepy */
      3 
      4 /* Libsheepy documentation: https://spartatek.se/libsheepy/ */
      5 #include <netdb.h> // for gethostbyname
      6 #include <netinet/in.h>
      7 #include <string.h>
      8 #include <stdlib.h>
      9 #include <unistd.h>
     10 
     11 // open
     12 #include <sys/types.h>
     13 #include <sys/stat.h>
     14 #include <fcntl.h>
     15 
     16 #include <glob.h>
     17 
     18 #include <openssl/err.h>
     19 //#include <openssl/pem.h>
     20 #include <openssl/ssl.h>
     21 //#include <openssl/x509v3.h>
     22 
     23 // inet_ntoa
     24 #include <sys/socket.h>
     25 #include <netinet/in.h>
     26 #include <arpa/inet.h>
     27 
     28 // basename
     29 #include <libgen.h>
     30 
     31 // statvfs
     32 #include <sys/statvfs.h>
     33 
     34 #include "libsheepyObject.h"
     35 #include "netFrame.h"
     36 #include "shpPackages/short/short.h"
     37 #include "makeHeader.h"
     38 
     39 #include "rateLimiter/rateLimiter.h"
     40 
     41 #define CONFIG "~/.tuyau/serverConfig.yml"
     42 
     43 int packetCount = 0;
     44 // file description for writing received chunks of big files
     45 int fd          = -1;
     46 u64 transferSz  = 0;
     47 // client socket
     48 int mysock      = 0;
     49 SSL *ssl;
     50 rateLimitert *rateLim = null;
     51 u32 clientIp          = 0;
     52 
     53 smallJsont *tokens    = null;
     54 smallJsont *relays    = null;
     55 
     56 // relay variables
     57 SSL_CTX *relay_ssl_ctx    = null;
     58 SSL *relay_ssl            = null;
     59 int relay_sock            = -1;
     60 netFramet *relay_netframe = null;
     61 // when the client sends files to the destination
     62 // and the client closes the connection to this relay server,
     63 // close the connection to the destination normaly.
     64 // when the destination closes the connection only cleanup of the destination
     65 // connection is needed
     66 // This is needed because when the client closes the connection, the receive loop
     67 // breaks.
     68 bool closeDestConnection  = no;
     69 
     70 /* enable/disable logging */
     71 /* #undef pLog */
     72 /* #define pLog(...) */
     73 
     74 void closeRelayConnectionToDest(void);
     75 void saveReceivedData(void *receiveB, size_t sz);
     76 bool relayForwardDataToClient(void *receiveB, size_t sz, void *context);
     77 
     78 // TODO return error from netframe callback
     79 
     80 bool cb(void *buf, size_t size, void *context) {
     81   saveReceivedData(buf, size);
     82   ret yes;
     83 }
     84 
     85 void loadCertificates(SSL_CTX* ctx, char* certFilename, char* keyFilename)
     86 {
     87   // set the local certificate
     88   if (SSL_CTX_use_certificate_file(ctx, certFilename, SSL_FILETYPE_PEM) <= 0) {
     89     logC("Error loadind cert file %s", certFilename);
     90     XFailure;
     91   }
     92   // set the private key
     93   if (SSL_CTX_use_PrivateKey_file(ctx, keyFilename, SSL_FILETYPE_PEM) <= 0) {
     94     logC("Error loadind key file %s", keyFilename);
     95     XFailure;
     96   }
     97   /* verify private key */
     98   if (!SSL_CTX_check_private_key(ctx)) {
     99     logC("Private key does not match the public certificate", keyFilename);
    100     XFailure;
    101   }
    102 }
    103 
    104 int main(int ARGC, char** ARGV) {
    105 
    106   initLibsheepy(ARGV[0]);
    107   setLogMode(LOG_FUNC);
    108 
    109   // block ips for 10mn when token is wrong
    110   initiateAllocateRateLimiter(&rateLim);
    111   setupG(rateLim, 1000000 /* max clients */, 600 /* window 10mn */, 0 /* max access count */);
    112 
    113   SSL_CTX *ctx;
    114   ctx = SSL_CTX_new(TLS_server_method());
    115   int r = SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION);
    116   if (!SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION)) {
    117     logE("Can't force minimum TLS 1.3");
    118     ret 1;
    119   }
    120   if (!SSL_CTX_set_cipher_list(ctx,
    121     "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:"
    122     "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:"
    123     "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:"
    124     "DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:"
    125     "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:"
    126     "TLS_CHACHA20_POLY1305_SHA256")) {
    127     logE("Can't set cipher list");
    128     ret 1;
    129   }
    130 
    131   SSL_CTX_set_options(ctx, SSL_OP_NO_RENEGOTIATION);
    132 
    133   loadCertificates(ctx, "cert.pem", "key.pem");
    134 
    135 
    136   // Steps
    137   // load configuration
    138   // create tokens dict
    139   // open listen socket
    140   // start event loop
    141 
    142   char *c = ARGV[1] ? ARGV[1] : CONFIG;
    143 
    144   // load configuration
    145   cleanCharP(config) = expandHome(c);
    146   cleanAllocateSmallJson(cfg);
    147   readFileG(cfg, config);
    148 
    149   lv(cfg);
    150 
    151   if (isEmptyG(cfg)) {
    152     logC("Empty configuration %s", c);
    153     ret 1;
    154   }
    155 
    156   tokens = allocG(rtSmallJsont);
    157   relays = allocG(rtSmallJsont);
    158 
    159   iter(cfg, D) {
    160     if (!isOSmallDictG(D)) continue;
    161     cast(smallDictt*,d,D);
    162     if (hasG(d, "root")) {
    163       // local home
    164       setG(tokens, $(d,"token"), $(d,"root"));
    165     }
    166     elif (hasG(d, "hostname")) {
    167       // relay
    168       setG(relays, $(d,"token"), d);
    169     }
    170   }
    171 
    172   lv(tokens);
    173   lv(relays);
    174 
    175   // open listen socket
    176   int sock;
    177   struct sockaddr_in server;
    178   int rval;
    179 
    180   sock = socket(AF_INET, SOCK_STREAM, 0);
    181   if (sock < 0){
    182     perror("Failed to create socket");
    183     XFailure;
    184   }
    185 
    186   u16 port = hasG(cfg, "port") ? u$(cfg, "port") : 1032;
    187   lv(port);
    188   server.sin_family = AF_INET;
    189   server.sin_addr.s_addr = INADDR_ANY;
    190   server.sin_port = htons(port);
    191 
    192   if (bind(sock, (struct sockaddr *) &server, sizeof(server))){
    193     perror("bind failed");
    194     XFailure;
    195   }
    196 
    197   cleanAllocateNetFrame(netframe);
    198 
    199   o(netframe, setCallback, cb, NULL /*context*/);
    200 
    201   // start event loop
    202   listen(sock, 5);
    203   forever {
    204     struct sockaddr_in addr;
    205     socklen_t len = sizeof(addr);
    206     mysock = accept(sock, (struct sockaddr *)&addr, &len);
    207     if (mysock == 1)
    208       perror("accept failed");
    209     else {
    210       logI("Connection: %s:%d",inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
    211       clientIp = addr.sin_addr.s_addr;
    212       if (o(rateLim,has, clientIp)) {
    213         logI("Ip %s is blocked.", inet_ntoa(addr.sin_addr));
    214         close(mysock);
    215         continue;
    216       }
    217       // get new SSL state with context
    218       ssl = SSL_new(ctx);
    219       // set connection socket to SSL state
    220       SSL_set_fd(ssl, mysock);
    221       // do SSL-protocol accept
    222       if (SSL_accept(ssl) == -1) {
    223         logE("Accept SSL connection");
    224       }
    225       else {
    226         var r = o(netframe,receive, ssl);
    227         logVarG(r);
    228         if (closeDestConnection) {
    229           // close client connection
    230           o(netframe,end, relay_ssl);
    231         }
    232       }
    233       // release SSL state
    234       SSL_free(ssl);
    235       close(mysock);
    236       if (relay_sock != -1) {
    237         if (closeDestConnection) {
    238           // 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?
    239           // 478 bytes are received. It seems to be encrypted.
    240           // SSL_shutdown() and shutdown() don't help
    241           u8 buf[1024*1024] = init0Var;
    242           int r             = recv(relay_sock, buf, sizeof(buf), 0);
    243           if (r == -1) {
    244             logE("recv before closing the socket");
    245           }
    246         }
    247         closeRelayConnectionToDest();
    248       }
    249     }
    250   }
    251 
    252   SSL_CTX_free(ctx);
    253   terminateG(relays);
    254   terminateG(tokens);
    255   terminateO(rateLim);
    256 }
    257 
    258 // connect to destination, this server is a relay
    259 bool connectToDest(smallDictt *svrInfo) {
    260 
    261   relay_sock = socket(AF_INET, SOCK_STREAM, 0);
    262   if (relay_sock < 0){
    263     perror("Failed to create socket");
    264     ret no;
    265   }
    266 
    267   struct sockaddr_in server;
    268   struct hostent *hp;
    269 
    270   server.sin_family = AF_INET;
    271 
    272   hp = gethostbyname($(svrInfo, "hostname"));
    273   if (hp==0) {
    274     perror("gethostbyname failed");
    275     close(relay_sock);
    276     relay_sock = -1;
    277     ret no;
    278   }
    279 
    280   memcpy(&server.sin_addr, hp->h_addr, hp->h_length);
    281   server.sin_port = htons(u$(svrInfo, "port"));
    282 
    283   if (connect(relay_sock,(struct sockaddr *) &server, sizeof(server))){
    284     perror("connect failed");
    285     close(relay_sock);
    286     relay_sock = -1;
    287     ret no;
    288   }
    289 
    290   relay_ssl_ctx = SSL_CTX_new(TLS_method());
    291   BIO *sbio = BIO_new(BIO_f_ssl());
    292   relay_ssl = SSL_new(relay_ssl_ctx);
    293   // attach the socket descriptor
    294   SSL_set_fd(relay_ssl, relay_sock);
    295 
    296   // perform the connection
    297   if (SSL_connect(relay_ssl) == -1) {
    298     logE("Couldn't establish the TLS connection");
    299     // clean state
    300     closeRelayConnectionToDest();
    301     ret no;
    302   }
    303 
    304   ret yes;
    305 }
    306 
    307 void closeRelayConnectionToDest(void) {
    308   // release SSL state
    309   SSL_free(relay_ssl);
    310   SSL_CTX_free(relay_ssl_ctx);
    311   // TODO add recv to send all data?
    312   close(relay_sock);
    313   // clear relay state
    314   relay_ssl     = null;
    315   relay_ssl_ctx = null;
    316   relay_sock    = -1;
    317   terminateG(relay_netframe);
    318 }
    319 
    320 void saveReceivedData(void *receiveB, size_t sz) {
    321   logVarG(sz);
    322 
    323   if (relay_sock != -1) {
    324     // this server is a relay and the connection to the destination
    325     // is already established
    326     // forward data from client to destination
    327     o(relay_netframe,send , relay_ssl, receiveB, sz);
    328     ret;
    329   }
    330 
    331   if (packetCount == 0) {
    332     // check packet size
    333     if (sz < (1/*command*/ + 8/*token*/)) ret;
    334     // receive a new command
    335     // header
    336     u8 *command               = (u8*) receiveB;
    337     u8 *token                 = (u8*) (receiveB + 1);
    338     char tk[9]                = init0Var;
    339     memcpy(tk, token, 8);
    340 
    341     cleanFinishSmallDictP(svrInfo) = null;
    342 
    343     if (not hasG(tokens, tk)) {
    344       if (hasG(relays, tk)) {
    345         // relay packets to destination
    346         svrInfo = getG(relays, rtSmallDictt, tk);
    347         logD("Relay to");
    348         lv(svrInfo);
    349       }
    350       else {
    351         // invalid connection, block ip
    352         incomingG(rateLim, clientIp);
    353         logE("token not found");
    354         ret;
    355       }
    356     }
    357 
    358     char *root                = $(tokens, tk);
    359 
    360     if (*command == 0) {
    361       // receive file
    362 
    363       if (svrInfo) {
    364         // this server is a relay, connect to destination
    365         if (not connectToDest(svrInfo)) {
    366           logE("Relay: Can't connect to destination %m", svrInfo);
    367           ret;
    368         }
    369         relay_netframe = allocNetFrame();
    370         // forward data from client to destination
    371         closeDestConnection = yes;
    372         o(relay_netframe,send , relay_ssl, receiveB, sz);
    373         ret;
    374       }
    375 
    376       // check packet size
    377       if (sz < (1/*command*/ + 8/*token*/ + 2 /*filenameLength*/ + 1 /*filename*/)) ret;
    378       u16 *filenameLength       = (u16*)(receiveB + 9);
    379       if (*filenameLength > 4096) ret;
    380       // check that filenameLength is correct
    381       if (sz < (1/*command*/ + 8/*token*/ + 2 /*filenameLength*/ + *filenameLength /*filename*/)) ret;
    382       // receiveB + 11 = filename
    383       cleanCharP(filename)      = malloc(*filenameLength + 1);
    384       filename[*filenameLength] = 0;
    385       memcpy(filename, receiveB + 11, *filenameLength);
    386       u32 bufi                  = 11 + *filenameLength;
    387       // check that filemode and path are in the buffer
    388       if (sz < (bufi + 2 /*filemode*/ + 2 /*pathLength*/)) ret;
    389       u16 *filemode             = (u16*)(receiveB + bufi);
    390       bufi += 2;
    391       u16 *pathLength           = (u16*)(receiveB + bufi);
    392       if (*pathLength > 4096) ret;
    393       bufi += 2;
    394       // check path length is correct and that filesize is in the buffer
    395       if (sz < (bufi + *pathLength + 8 /*filesize*/)) ret;
    396       cleanCharP(destPath)      = null;
    397       if (*pathLength) {
    398         destPath              = malloc(*pathLength + 1);
    399         memcpy(destPath, receiveB + bufi, *pathLength);
    400         destPath[*pathLength] = 0;
    401         bufi                 += *pathLength;
    402       }
    403       // file size
    404       u64 *filesize             = (u64*)(receiveB + bufi);
    405       bufi += 8;
    406 
    407       logD("bufi %d fsz %d  + %d, filename %s, destPath %s",bufi, *filesize, bufi + *filesize, filename, destPath);
    408 
    409       // local filename
    410       char *path = catS(root, "/", nS(destPath));
    411       pErrorNULL(normalizePathG(&path));
    412       pErrorNULL(trimG(&path));
    413       cleanCharP(nroot) = normalizePathG(root);
    414       //lv(path);
    415       // make sure path is inside root
    416       if (not startsWithG(path, nroot)) {
    417         logE("Incorrect path");
    418         ret;
    419       }
    420 
    421       // check isdir or if there is a filename
    422       bool filenameInDestPath = no;
    423       if (destPath and not isPath(path)) {
    424         if (endsWithG(destPath,'/')) {
    425           logE("Directory doesn't exist %s", path);
    426           ret;
    427         }
    428         cleanCharP(dir) = shDirname(path);
    429         if (not isPath(dir)) {
    430           logE("Directory doesn't exist %s", dir);
    431           ret;
    432         }
    433         filenameInDestPath = yes;
    434         // check if there is enough free space on disk
    435         struct statvfs fs;
    436         if (statvfs(dir, &fs) == -1) {
    437           shperror("statvfs check free space");
    438           ret;
    439         }
    440         if (*filesize >= fs.f_bsize * fs.f_bavail) {
    441           logE("Not enough free space available in path %s", path);
    442         }
    443       }
    444       //lv(filenameInDestPath);
    445       if (not filenameInDestPath) {
    446         // check if there is enough free space on disk
    447         struct statvfs fs;
    448         if (statvfs(path, &fs) == -1) {
    449           shperror("statvfs check free space");
    450           ret;
    451         }
    452         if (*filesize >= fs.f_bsize * fs.f_bavail) {
    453           logE("Not enough free space available in path %s", path);
    454         }
    455         pErrorNULL(iAppendManyS(&path, "/", filename));
    456       }
    457       lv(path);
    458 
    459       if (*filesize <= sz - bufi) {
    460         // the file is smaller than the network buffer, the complete file is already here
    461         pError0(writeFile(path, receiveB + bufi, *filesize));
    462         pError0(fileChmod(path, *filemode));
    463       }
    464       else {
    465         logD("open fd");
    466         // increase packetCount to receive big file in chunks
    467         inc packetCount;
    468         fd = open(path, O_WRONLY | O_CREAT, *filemode);
    469         if (fd == -1) {
    470           logE("cant open %s", path);
    471           ret;
    472         }
    473         int r = write(fd, receiveB + bufi, sz - bufi);
    474         if (r == -1) {
    475           logE("cant write %s", path);
    476           ret;
    477         }
    478         transferSz = *filesize - (sz - bufi);
    479       }
    480     }
    481     elif (*command == 1) {
    482       // mkdir
    483 
    484       if (svrInfo) {
    485         // this server is a relay, connect to destination
    486         if (not connectToDest(svrInfo)) {
    487           logE("Relay: Can't connect to destination %m", svrInfo);
    488           ret;
    489         }
    490         relay_netframe = allocNetFrame();
    491         // forward data from client to destination
    492         closeDestConnection = yes;
    493         o(relay_netframe,send , relay_ssl, receiveB, sz);
    494         ret;
    495       }
    496 
    497       // check packet size
    498       if (sz < (1/*command*/ + 8/*token*/ + 2 /*filemode*/ + 2 /*pathLength*/ + 1 /*path*/)) ret;
    499       u16 *filemode         = (u16*)(receiveB + 9);
    500       u16 *pathLength       = (u16*)(receiveB + 11);
    501       if (*pathLength > 4096) ret;
    502       // check that pathLength is correct, path has to be at least 1 byte
    503       if (sz < (1/*command*/ + 8/*token*/ + 2 /*filemode*/ + 2 /*pathLength*/ + *pathLength /*path*/)) ret;
    504       cleanCharP(destPath)  = malloc(*pathLength + 1);
    505       memcpy(destPath, receiveB + 13, *pathLength);
    506       destPath[*pathLength] = 0;
    507       cleanCharP(path)      = catS(root, "/", destPath);
    508       pErrorNULL(normalizePathG(&path));
    509       pErrorNULL(trimG(&path));
    510       logD(BLD GRN"mkdir" RST);
    511       lv(path);
    512       pError0(mkdirParents(path));
    513       pError0(fileChmod(path, *filemode));
    514     }
    515     elif (*command == 2) {
    516       // send files to client
    517 
    518       if (svrInfo) {
    519         // this server is a relay, connect to destination
    520         if (not connectToDest(svrInfo)) {
    521           logE("Relay: Can't connect to destination %m", svrInfo);
    522           ret;
    523         }
    524         relay_netframe = allocNetFrame();
    525         // forward data from client to destination
    526         closeDestConnection = no;
    527         o(relay_netframe,send , relay_ssl, receiveB, sz);
    528 
    529         cleanAllocateNetFrame(netframe);
    530 
    531         o(relay_netframe, setCallback, relayForwardDataToClient, netframe /*context*/);
    532 
    533         var r = o(relay_netframe,receive, relay_ssl);
    534         logVarG(r);
    535 
    536         // close client connection
    537         o(netframe,end, ssl);
    538         ret;
    539       }
    540 
    541       // check packet size, sendFile path has to be at least 1 byte
    542       if (sz < (1/*command*/ + 8/*token*/ + 2 /*sendFileLength*/ + 1 /*sendFile*/)) ret;
    543       logD("send files to client");
    544       u16 *sendFileLength       = (u16*)(receiveB + 9);
    545       if (*sendFileLength > 4096) ret;
    546       // check that sendFileLength is correct
    547       if (sz < (1/*command*/ + 8/*token*/ + 2 /*sendFileLength*/ + *sendFileLength)) ret;
    548       cleanCharP(sendFile)      = malloc(*sendFileLength + 1);
    549       memcpy(sendFile, receiveB + 11, *sendFileLength);
    550       sendFile[*sendFileLength] = 0;
    551 
    552       pErrorNULL(prependG(&sendFile, '/'));
    553       pErrorNULL(prependG(&sendFile, root));
    554       pErrorNULL(normalizePathG(&sendFile));
    555       pErrorNULL(trimG(&sendFile));
    556       lv(sendFile);
    557       cleanCharP(nroot) = normalizePathG(root);
    558       if (not startsWithG(sendFile, nroot)) {
    559         logE("Incorrect path, outside home directory: "BLD YLW"%s"RST,sendFile);
    560         ret;
    561       }
    562 
    563       // send a file or glob
    564 
    565       cleanAllocateSmallArray(pathToSend);
    566 
    567       if (isPath(sendFile)) {
    568         pushG(pathToSend, sendFile);
    569       }
    570       else {
    571         // check if sendFile is a glob
    572         glob_t globbuf = init0Var;
    573         glob(sendFile, 0, NULL, &globbuf);
    574         if (!globbuf.gl_pathv) {
    575           logE("Incorrect path: "BLD YLW"%s"RST,sendFile);
    576           ret;
    577         }
    578         forEachS(globbuf.gl_pathv, s) {
    579           pushG(pathToSend, s);
    580         }
    581         globfree(&globbuf);
    582       }
    583 
    584       cleanAllocateNetFrame(netframe);
    585 
    586       // network buffer
    587       u8 buf[1024*1024]   = init0Var;
    588 
    589       cleanAllocateSmallDict(recusive);
    590 
    591       iter(pathToSend, P) {
    592         castS(p, P);
    593         char *sendFile = ssGet(P);
    594 
    595         // create local directory in client
    596         // add files from local directory to recusive dict, the last element is the local path
    597         if (isDirG(p)) {
    598           logD("mkdir in client");
    599 
    600           char *dirname = basename(ssGet(p));
    601 
    602           u32 bufi      = 0;
    603           u64 *filesize = null;
    604           makeHeader(buf, &bufi, 1 /* command */, null/*svrInfo no need to authenticate the client*/, ssGet(P), dirname/*dest*/, &filesize);
    605 
    606           o(netframe,send , ssl, buf, bufi);
    607 
    608           var dir = readDirAllG(rtSmallArrayt, p);
    609           //lv(dir);
    610           if (not isEmptyG(dir)) {
    611             // save local path in last element
    612             pushG(dir, ssGet(p));
    613             setNFreeG(recusive, dirname, dir);
    614           }
    615           lv(recusive);
    616           continue;
    617         }
    618 
    619         // send
    620         u32 bufi      = 0;
    621         u64 *filesize = null;
    622         makeHeader(buf, &bufi, 0 /* command */, null/*svrInfo no need to authenticate the client*/, sendFile, null/*destPath is already selected in client*/, &filesize);
    623 
    624         if (*filesize < (sizeof(buf) - bufi)) {
    625           // the file is smaller than the network buffer, send all in one go
    626           pError0(bReadFile(sendFile, buf + bufi));
    627           o(netframe,send , ssl, buf, bufi + *filesize);
    628         }
    629         else {
    630           logD("big file");
    631           // loop to send big file in chunks
    632           u64 szToSend = *filesize;
    633           u8 *b        = buf + bufi;
    634           u64 bSz      = sizeof(buf) - bufi;
    635           int fd       = open(sendFile, O_RDONLY);
    636           do {
    637             if (szToSend <= bSz) {
    638               read(fd, b, szToSend);
    639               o(netframe,send , ssl, b, szToSend);
    640               szToSend = 0;
    641             }
    642             else {
    643               read(fd, b, bSz);
    644               if (szToSend == *filesize) {
    645                 o(netframe,send , ssl, buf, bufi + bSz);
    646               }
    647               else {
    648                 o(netframe,send , ssl, b, bSz);
    649               }
    650               szToSend -= bSz;
    651             }
    652           } while(szToSend);
    653           close(fd);
    654         }
    655       } // sendFile or globbing
    656 
    657       // send files recursively
    658       // each key is a path in server corresponding to a local directory
    659       // the element is the list of files in the directory
    660       // it is similar to the iter(pathToSend, P) loop
    661 
    662       iter(recusive, A) {
    663         cast(smallArrayt*,a,A);
    664         cleanCharP(localPath) = cropElemG(a, rtChar, -1);
    665         lv(localPath);
    666         iter(a, P) {
    667           castS(p, P);
    668           char *sendFile = ssGet(P);
    669 
    670           // create local directory in server
    671           // add files from local directory to recusive dict, the last element is the local path
    672           cleanCharP(localp)  = catS(localPath, "/", ssGet(p));
    673           if (isDirG(localp)) {
    674             logD("mkdir in client");
    675             cleanCharP(dirname) = catS(iK(recusive), "/", ssGet(p));
    676 
    677             u32 bufi      = 0;
    678             u64 *filesize = null;
    679             makeHeader(buf, &bufi, 1 /* command */, null/*svrInfo no need to authenticate the client*/, localp, dirname, &filesize);
    680 
    681             o(netframe,send , ssl, buf, bufi);
    682 
    683             var dir = readDirAllG(rtSmallArrayt, localp);
    684             lv(dir);
    685             if (not isEmptyG(dir)) {
    686               // save local path in last element
    687               pushG(dir, localp);
    688               setNFreeG(recusive, dirname, dir);
    689             }
    690             //lv(recusive);
    691             continue;
    692           }
    693 
    694           logD("Send localp file to iK(recusive) client directory");
    695           // send
    696           u32 bufi         = 0;
    697           u64 *filesize    = null;
    698           makeHeader(buf, &bufi, 0 /* command */, null/*svrInfo no need to authenticate the client*/, localp, (char*)iK(recusive) /*dest*/, &filesize);
    699 
    700           if (*filesize < (sizeof(buf) - bufi)) {
    701             // the file is smaller than the network buffer, send all in one go
    702             pError0(bReadFile(localp, buf + bufi));
    703             o(netframe,send , ssl, buf, bufi + *filesize);
    704           }
    705           else {
    706             logD("big file");
    707             // loop to send big file in chunks
    708             u64 szToSend = *filesize;
    709             u8 *b        = buf + bufi;
    710             u64 bSz      = sizeof(buf) - bufi;
    711             int fd       = open(localp, O_RDONLY);
    712             do {
    713               if (szToSend <= bSz) {
    714                 read(fd, b, szToSend);
    715                 o(netframe,send , ssl, b, szToSend);
    716                 szToSend = 0;
    717               }
    718               else {
    719                 read(fd, b, bSz);
    720                 if (szToSend == *filesize) {
    721                   o(netframe,send , ssl, buf, bufi + bSz);
    722                 }
    723                 else {
    724                   o(netframe,send , ssl, b, bSz);
    725                 }
    726                 szToSend -= bSz;
    727               }
    728             } while(szToSend);
    729             close(fd);
    730           }
    731         }
    732       }
    733 
    734       lv(recusive);
    735 
    736       // last frame size 0
    737       // close connection
    738       o(netframe,end, ssl);
    739     }
    740   }
    741   else {
    742     // receive large files packetCount > 0
    743     if (fd == -1) ret;
    744     // big file
    745     int r = write(fd, receiveB, sz);
    746     if (r == -1) {
    747       logE("cant write");
    748       ret;
    749     }
    750     if (transferSz <= sz) {
    751       transferSz  = 0;
    752       close(fd);
    753       fd          = -1;
    754       packetCount = 0;
    755     }
    756     else transferSz -= sz;
    757   }
    758 }
    759 
    760 bool relayForwardDataToClient(void *receiveB, size_t sz, void *context) {
    761   cast(netFramet*, netframe, context);
    762   // forward data from destination to client
    763   o(netframe,send , ssl, receiveB, sz);
    764   ret yes;
    765 }
    766 // vim: set expandtab ts=2 sw=2: