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: