💾 Archived View for gmi.noulin.net › gitRepositories › tuyau › file › sserver.c.gmi captured on 2023-07-10 at 18:14:20. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-01-29)

🚧 View Differences

-=-=-=-=-=-=-

tuyau

Log

Files

Refs

README

sserver.c (22827B)

     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: