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

View Raw

More Information

⬅️ Previous capture (2023-01-29)

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

tuyau

Log

Files

Refs

README

sclient.c (15265B)

     1 #! /usr/bin/env sheepy
     2 /* or direct path to sheepy: #! /usr/local/bin/sheepy */
     3 /* Libsheepy documentation: https://spartatek.se/libsheepy/ */
     4 
     5 /*
     6 usage
     7 - create server config
     8 - start server
     9 - create client config
    10 - run client
    11 
    12 Upload files:
    13 ./sclient.c files serverName
    14 ./sclient.c files serverName:path/
    15 ./sclient.c file serverName:path/newfilename
    16 
    17 Downdload files:
    18 ./sclient.c serverName:path/files
    19   >> downloads to current directory
    20 ./sclient.c serverName:path/files path/
    21 ./sclient.c serverName:path/file path/newfilename
    22 
    23 When the client sends files to the server, client decides when
    24 to close the connection and the client closes the connection
    25 When the client receives files from the server, the server decides when
    26 to close the connection and the client closes the connection
    27 
    28 */
    29 
    30 #include <netdb.h>
    31 #include <netinet/in.h>
    32 #include <string.h>
    33 #include <stdlib.h>
    34 #include <unistd.h>
    35 
    36 // open
    37 #include <sys/types.h>
    38 #include <sys/stat.h>
    39 #include <fcntl.h>
    40 
    41 // basename
    42 #include <libgen.h>
    43 
    44 #include <openssl/err.h>
    45 //#include <openssl/pem.h>
    46 #include <openssl/ssl.h>
    47 //#include <openssl/x509v3.h>
    48 
    49 #include "netFrame.h"
    50 #include "libsheepyObject.h"
    51 #include "shpPackages/short/short.h"
    52 #include "makeHeader.h"
    53 
    54 #define CONFIG "~/.tuyau/config.yml"
    55 
    56 /* enable/disable logging */
    57 /* #undef pLog */
    58 /* #define pLog(...) */
    59 
    60 bool saveReceivedData(void *receiveB, size_t sz, void *context);
    61 
    62 int main(int ARGC, char** ARGV) {
    63 
    64   initLibsheepy(ARGV[0]);
    65   setLogMode(LOG_FUNC);
    66 
    67   // Steps
    68   //
    69   // load configuration
    70   // check if there is a server name in argv 1
    71   //   search for server name in last argument
    72   // if receive and 2 args, arg 2 must be a valid path in client
    73   // open connection to server
    74   // receive files in receivePath
    75   // send files
    76 
    77   // load configuration
    78   cleanCharP(config) = expandHome(CONFIG);
    79   cleanAllocateSmallJson(cfg);
    80   readFileG(cfg, config);
    81 
    82   if (ARGC < 2) {
    83     logE("Missing arguments");
    84     ret 1;
    85   }
    86 
    87   // check if there is a server name in argv 1
    88   // if yes, the user wants to receive data from the server
    89   //    destPath is the path in the server to download from
    90   //    set server name
    91   // if no, the user wants to upload to the server
    92 
    93   // send or receive data to/from server
    94   bool isReceive = no;
    95   const char *serverName = null;
    96   char *destPath         = null;
    97   char *receivePath      = null;
    98   iter(cfg, D) {
    99     cast(smallDictt*,d,D);
   100     //lv(d);
   101     if (startsWithG(ARGV[1], iK(cfg)) and ARGV[1][lenG(iK(cfg))] == ':' and ARGV[1][lenG(iK(cfg))+1] != 0) {
   102       serverName = iK(cfg);
   103       destPath   = ARGV[1] + lenG(iK(cfg)) + 1;
   104       isReceive  = yes;
   105       break;
   106     }
   107   }
   108 
   109   if (not isReceive) {
   110     // search for server name in last argument
   111     if (ARGC == 2) {
   112       logE("Missing arguments");
   113       ret 1;
   114     }
   115     logD("path in client");
   116     // check last arg
   117     bool foundDest = no;
   118     iter(cfg, D) {
   119       cast(smallDictt*,d,D);
   120       //lv(d);
   121       if (startsWithG(ARGV[ARGC-1], iK(cfg))) {
   122         if (ARGV[ARGC-1][lenG(iK(cfg))] == 0) {
   123           logD("no dest path");
   124           serverName = iK(cfg);
   125           foundDest  = yes;
   126           break;
   127         }
   128         elif (ARGV[ARGC-1][lenG(iK(cfg))] == ':' and ARGV[ARGC-1][lenG(iK(cfg))+1] != 0) {
   129           logD("found destination");
   130           serverName = iK(cfg);
   131           destPath   = ARGV[ARGC-1] + lenG(iK(cfg)) + 1;
   132           foundDest  = yes;
   133           break;
   134         }
   135       }
   136     }
   137   } // not isReceive
   138   elif (ARGC > 2) {
   139     // receive
   140     // arg 2 must be a valid path in client
   141     receivePath           = ARGV[2];
   142     cleanCharP(localPath) = normalizePathG(ARGV[2]);
   143     if (not isEmptyG(localPath)/*ignore "." -> ""*/) {
   144       logD("check if receivePath is dir or dir with filename");
   145       if (not isPath(localPath)) {
   146         if (endsWithG(receivePath,'/')) {
   147           logE("Directory doesn't exist %s", localPath);
   148           ret 1;
   149         }
   150         cleanCharP(dir) = shDirname(localPath);
   151         if (not isPath(dir)) {
   152           logE("Directory doesn't exist %s", dir);
   153           ret 1;
   154         }
   155       }
   156     }
   157   }
   158 
   159   if (not serverName) {
   160     logE("server name not found");
   161     ret 1;
   162   }
   163 
   164   cleanFinishSmallDictP(svrInfo) = getG(cfg, rtSmallDictt, serverName);
   165 
   166   lv(svrInfo);
   167 
   168   // open connection to server
   169   int sock;
   170   struct sockaddr_in server;
   171   struct hostent *hp;
   172 
   173 
   174   cleanAllocateNetFrame(netframe);
   175 
   176 
   177   sock = socket(AF_INET, SOCK_STREAM, 0);
   178   if (sock < 0){
   179     perror("Failed to create socket");
   180     ret 1;
   181   }
   182 
   183   server.sin_family = AF_INET;
   184 
   185   hp = gethostbyname($(svrInfo, "hostname"));
   186   if (hp==0) {
   187     perror("gethostbyname failed");
   188     close(sock);
   189     XFailure;
   190   }
   191 
   192   memcpy(&server.sin_addr, hp->h_addr, hp->h_length);
   193   server.sin_port = htons(u$(svrInfo, "port"));
   194 
   195   if (connect(sock,(struct sockaddr *) &server, sizeof(server))){
   196     perror("connect failed");
   197     close(sock);
   198     XFailure;
   199   }
   200 
   201   SSL_CTX *ssl_ctx;
   202   SSL *ssl;
   203   ssl_ctx = SSL_CTX_new(TLS_method());
   204   BIO *sbio = BIO_new(BIO_f_ssl());
   205   ssl = SSL_new(ssl_ctx);
   206   // attach the socket descriptor
   207   SSL_set_fd(ssl, sock);
   208 
   209   // perform the connection
   210   if (SSL_connect(ssl) == -1) {
   211     logE("Couldn't establish the TLS connection");
   212     ret 1;
   213   }
   214 
   215   // buffer for network packets
   216   u8 buf[1024*1024]   = init0Var;
   217 
   218   if (isReceive) {
   219     // receive files in receivePath
   220 
   221     u32 bufi      = 0;
   222     makeHeader(buf, &bufi, 2 /* command */, svrInfo, null /*sendFile*/, destPath, null /*filesize*/);
   223 
   224     o(netframe,send , ssl, buf, bufi);
   225 
   226     o(netframe, setCallback, saveReceivedData, receivePath /*context*/);
   227 
   228     var r = o(netframe,receive, ssl);
   229     logVarG(r);
   230 
   231   }
   232   else {
   233     // send files
   234 
   235     cleanAllocateSmallArray(pathToSend);
   236 
   237     // verify that paths in client are valid
   238     rangeFrom(i, 1, ARGC-1) {
   239       if (not isPath(ARGV[i])) {
   240         logE("Incorrect path: "BLD YLW"%s"RST,ARGV[i]);
   241         continue;
   242       }
   243       pushG(pathToSend, ARGV[i]);
   244     }
   245 
   246     cleanAllocateSmallDict(recusive);
   247 
   248     // send files in command line arguments
   249 
   250     iter(pathToSend, P) {
   251       castS(p, P);
   252       char *sendFile = ssGet(P);
   253 
   254       // create local directory in server
   255       // add files from local directory to recusive dict, the last element is the local path
   256       if (isDirG(p)) {
   257         logD("mkdir in server");
   258 
   259         char *dirname = basename(ssGet(p));
   260         cleanCharP(dest) = null;
   261         if (destPath)
   262           dest = catS(destPath, "/", dirname);
   263         else
   264           dest = strdup(dirname);
   265 
   266         u32 bufi      = 0;
   267         u64 *filesize = null;
   268         makeHeader(buf, &bufi, 1 /* command */, svrInfo, ssGet(p), dest, &filesize);
   269 
   270         o(netframe,send , ssl, buf, bufi);
   271 
   272         var dir = readDirAllG(rtSmallArrayt, p);
   273         //lv(dir);
   274         if (not isEmptyG(dir)) {
   275           // save local path in last element
   276           pushG(dir, ssGet(p));
   277           setNFreeG(recusive, dirname, dir);
   278         }
   279         lv(recusive);
   280         continue;
   281       }
   282 
   283       // send
   284       u32 bufi      = 0;
   285       u64 *filesize = null;
   286       makeHeader(buf, &bufi, 0 /* command */, svrInfo, sendFile, destPath, &filesize);
   287 
   288       if (*filesize < (sizeof(buf) - bufi)) {
   289         // the file is smaller than the network buffer, send all in one go
   290         pError0(bReadFile(sendFile, buf + bufi));
   291         o(netframe,send , ssl, buf, bufi + *filesize);
   292       }
   293       else {
   294         logD("big file");
   295         // loop to send big file in chunks
   296         u64 szToSend = *filesize;
   297         u8 *b        = buf + bufi;
   298         u64 bSz      = sizeof(buf) - bufi;
   299         int fd       = open(sendFile, O_RDONLY);
   300         do {
   301           if (szToSend <= bSz) {
   302             read(fd, b, szToSend);
   303             o(netframe,send , ssl, b, szToSend);
   304             szToSend = 0;
   305           }
   306           else {
   307             read(fd, b, bSz);
   308             if (szToSend == *filesize) {
   309               o(netframe,send , ssl, buf, bufi + bSz);
   310             }
   311             else {
   312               o(netframe,send , ssl, b, bSz);
   313             }
   314             szToSend -= bSz;
   315           }
   316         } while(szToSend);
   317         close(fd);
   318       }
   319     } // loop on arguments
   320 
   321 
   322     // send files recursively
   323     // each key is a path in server corresponding to a local directory
   324     // the element is the list of files in the directory
   325     // it is similar to the iter(pathToSend, P) loop
   326 
   327     iter(recusive, A) {
   328       cast(smallArrayt*,a,A);
   329       cleanCharP(localPath) = cropElemG(a, rtChar, -1);
   330       lv(localPath);
   331       iter(a, P) {
   332         castS(p, P);
   333         char *sendFile = ssGet(P);
   334 
   335         // create local directory in server
   336         // add files from local directory to recusive dict, the last element is the local path
   337         cleanCharP(localp)  = catS(localPath, "/", ssGet(p));
   338         if (isDirG(localp)) {
   339           logD("mkdir in server");
   340           cleanCharP(dirname) = catS(iK(recusive), "/", ssGet(p));
   341           cleanCharP(dest) = null;
   342           if (destPath)
   343             dest = catS(destPath, "/", dirname);
   344           else
   345             dest = strdup(dirname);
   346 
   347           u32 bufi      = 0;
   348           u64 *filesize = null;
   349           makeHeader(buf, &bufi, 1 /* command */, svrInfo, localp, dest, &filesize);
   350 
   351           o(netframe,send , ssl, buf, bufi);
   352 
   353           var dir = readDirAllG(rtSmallArrayt, localp);
   354           lv(dir);
   355           if (not isEmptyG(dir)) {
   356             // save local path in last element
   357             pushG(dir, localp);
   358             setNFreeG(recusive, dirname, dir);
   359           }
   360           //lv(recusive);
   361           continue;
   362         }
   363 
   364         logD("Send files from localPath/p to destPath/iK(recusive)/");
   365         // send
   366         cleanCharP(dest) = null;
   367         if (destPath)
   368           dest = catS(destPath, "/", iK(recusive));
   369         else
   370           dest = strdup(iK(recusive));
   371         u32 bufi         = 0;
   372         u64 *filesize    = null;
   373         makeHeader(buf, &bufi, 0 /* command */, svrInfo, localp, dest, &filesize);
   374 
   375         if (*filesize < (sizeof(buf) - bufi)) {
   376           // the file is smaller than the network buffer, send all in one go
   377           pError0(bReadFile(localp, buf + bufi));
   378           o(netframe,send , ssl, buf, bufi + *filesize);
   379         }
   380         else {
   381           logD("big file");
   382           // loop to send big file in chunks
   383           u64 szToSend = *filesize;
   384           u8 *b        = buf + bufi;
   385           u64 bSz      = sizeof(buf) - bufi;
   386           int fd       = open(localp, O_RDONLY);
   387           do {
   388             if (szToSend <= bSz) {
   389               read(fd, b, szToSend);
   390               o(netframe,send , ssl, b, szToSend);
   391               szToSend = 0;
   392             }
   393             else {
   394               read(fd, b, bSz);
   395               if (szToSend == *filesize) {
   396                 o(netframe,send , ssl, buf, bufi + bSz);
   397               }
   398               else {
   399                 o(netframe,send , ssl, b, bSz);
   400               }
   401               szToSend -= bSz;
   402             }
   403           } while(szToSend);
   404           close(fd);
   405         }
   406       }
   407     }
   408 
   409     lv(recusive);
   410 
   411     // last frame size 0
   412     o(netframe,end, ssl);
   413 
   414   } // isReceive
   415 
   416   // release SSL state
   417   SSL_free(ssl);
   418   SSL_CTX_free(ssl_ctx);
   419   if (!isReceive) {
   420     // 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?
   421     // 478 bytes are received. It seems to be encrypted.
   422     // SSL_shutdown() and shutdown() don't help
   423     int r = recv(sock, buf, sizeof(buf), 0);
   424     if (r == -1) {
   425       logE("recv before closing the socket");
   426     }
   427   }
   428   close(sock);
   429 }
   430 
   431 
   432 int packetCount = 0;
   433 int fd          = -1;
   434 u64 transferSz  = 0;
   435 
   436 bool saveReceivedData(void *receiveB, size_t sz, void *context) {
   437   logVarG(sz);
   438   if (packetCount == 0) {
   439     // receive a new command
   440     // header
   441     u8 *command               = (u8*) receiveB;
   442 
   443     char *root                = context;
   444 
   445     if (*command == 0) {
   446       // receive file
   447 
   448       u16 *filenameLength       = (u16*)(receiveB + 9);
   449       // receiveB + 11 = filename
   450       cleanCharP(filename)      = malloc(*filenameLength + 1);
   451       filename[*filenameLength] = 0;
   452       memcpy(filename, receiveB + 11, *filenameLength);
   453       u32 bufi                  = 11 + *filenameLength;
   454       u16 *filemode             = (u16*)(receiveB + bufi);
   455       bufi += 2;
   456       u16 *pathLength           = (u16*)(receiveB + bufi);
   457       bufi += 2;
   458       cleanCharP(destPath)      = null;
   459       if (*pathLength) {
   460         destPath              = malloc(*pathLength + 1);
   461         memcpy(destPath, receiveB + bufi, *pathLength);
   462         destPath[*pathLength] = 0;
   463         bufi                 += *pathLength;
   464       }
   465       // file size
   466       u64 *filesize             = (u64*)(receiveB + bufi);
   467       bufi += 8;
   468 
   469       logD("bufi %d fsz %d  + %d, filename %s, root %s, destPath %s",bufi, *filesize, bufi + *filesize, filename, root,destPath);
   470 
   471       // local filename
   472       cleanCharP(path) = null;
   473 
   474       if (not root) {
   475         // save file in current directory
   476         if (not destPath) {
   477           path = strdup(filename);
   478         }
   479         else {
   480           path = catS(destPath, "/", filename);
   481         }
   482       }
   483       elif (isDir(root)) {
   484         path = catS(root, "/", nS(destPath), "/", filename);
   485       }
   486       else {
   487         // root has a filename
   488         path = strdup(root);
   489       }
   490 
   491       pErrorNULL(normalizePathG(&path));
   492       lv(path);
   493 
   494       if (*filesize <= sz - bufi) {
   495         // the file is smaller than the network buffer, the complete file is already here
   496         pError0(writeFile(path, receiveB + bufi, *filesize));
   497         pError0(fileChmod(path, *filemode));
   498       }
   499       else {
   500         logD("open fd");
   501         // increase packetCount to receive big file in chunks
   502         inc packetCount;
   503         fd = open(path, O_WRONLY | O_CREAT, *filemode);
   504         if (fd == -1) {
   505           logE("cant open %s", path);
   506           ret yes;
   507         }
   508         int r = write(fd, receiveB + bufi, sz - bufi);
   509         if (r == -1) {
   510           logE("cant write %s", path);
   511           ret yes;
   512         }
   513         transferSz = *filesize - (sz - bufi);
   514       }
   515     }
   516     elif (*command == 1) {
   517       // mkdir
   518       if (root and not isDir(root)) {
   519         logE("Cant create directory if root is not a directory %s", root);
   520         ret yes;
   521       }
   522       logD("mkdir");
   523       u16 *filemode         = (u16*)(receiveB + 9);
   524       u16 *pathLength       = (u16*)(receiveB + 11);
   525       cleanCharP(destPath)  = malloc(*pathLength + 1);
   526       memcpy(destPath, receiveB + 13, *pathLength);
   527       destPath[*pathLength] = 0;
   528       cleanCharP(path)      = null;
   529       if (not root) {
   530         path = strdup(destPath);
   531       }
   532       else {
   533         path = catS(root, "/", destPath);
   534       }
   535       pErrorNULL(normalizePathG(&path));
   536       pErrorNULL(trimG(&path));
   537       logD(BLD GRN"mkdir" RST);
   538       lv(path);
   539       pError0(mkdirParents(path));
   540       pError0(fileChmod(path, *filemode));
   541     }
   542   }
   543   else {
   544     // receive large files packetCount > 0
   545     if (fd == -1) ret yes;
   546     // big file
   547     int r = write(fd, receiveB, sz);
   548     if (r == -1) {
   549       logE("cant write");
   550       ret yes;
   551     }
   552     if (transferSz <= sz) {
   553       transferSz  = 0;
   554       close(fd);
   555       fd          = -1;
   556       packetCount = 0;
   557     }
   558     else transferSz -= sz;
   559   }
   560   ret yes;
   561 }
   562 // vim: set expandtab ts=2 sw=2: