Древний костыль на старом костыле

от автора

Начну без обиняков, как-то раз меня постигло откровение (ну не сильно мощное скажу по-честному) и возникла идея напечатать программу которая передает изображение с клиента на сервер. Достаточно просто да? Ну для программиста со стажем так и будет. Условия просты — не использовать сторонние библиотеки. В принципе немного сложнее, но если учесть что придется разбираться и искать примеры, ну такое себе занятие. Я решил, что эта задача мне по плечу. Плюс желательно чтобы было кода столько, чтобы его можно было запостить на форуме, в случае если понадобится помощь. В первую очередь мой взгляд пал на FTP, к слову ОС в которой разрабатывается Windows. Плюс FTP в том, что можно через него передать не только изображение, а любой файл. Скачав Filezilla Server, расшарив одну директорию на чтение/запись и создав юзера с паролем, попробовал подключится Filezilla Client все работало. Создал простенький пример кода на С/С++:

#include <iostream> void main() { 	FILE* fs; 	fopen_s(&fs, "1.txt", "w"); 	if (fs) 	{     fwrite("user\r\npassword\r\nsend D:\\share\\1.txt\r\nbye", 1, sizeof("user\r\npassword\r\nsend D:\\share\\1.txt\r\nbye"), fs);     fwrite("\000", 1, sizeof("\000"), fs);     fclose(fs); 	} 	system("ftp -s:1.txt 127.0.0.1"); }

Если мне не изменяет память, то на локалхосте все работало, а при передаче по сети возникала ошибка в строчке с send. Что здесь удобно а)коротко б)не нужно устанавливать клиент, а использовать уже встроенную тулзу для ftp от майкрософта. Хотя по-мойму ее надо активировать через программы и компоненты. Если вы разберетесь в чем проблема данного метода и напишите в комментарии, будет отлично.

Не найдя ответа на куче форумов, я оставил данный код и решил использовать интерфейс для сетей сокеты. У меня уже был опыт передачи массива char’ов для другой программы. Кстати можете почитать у Таненбаума, Компьютерные сети, в главе про транспортный уровень. Там есть пример клиента и сервера, правда не для соединения «много клиентов — один сервер», а только «один клиент — один сервер». Поскольку передача идет через Интернет, то нужно зашифровать хоть как-то данные. Для этого используется блочный шифр — сеть Фейстеля. Плюсом на сервере надо сделать несколько(больше одного клиента) клиентов. Для этого воспользуемся Thread’ами, изображение для передачи будет брать скриншот экрана с клиента шифроваться и передаваться на сервер, на котором будет расшифровано и сразу же выведено на экран через дефолтную программу для открытия *.tga изображения.

Код сервера:

#include <iostream> #include <WinSock.h> #pragma comment (lib,"WS2_32.lib")  #include <fstream> #include <algorithm> #include <string> #include <iterator> #include <vector> void error(const char* msg) {     //perror(msg);     std::cout<<'\n'<<WSAGetLastError();     WSACleanup();     std::cin.ignore();     exit(1); } void bzero(char*buf, int l) {     for (int i = 0; i < l; i++)         buf[i] = '\0'; } struct arg_s {     unsigned char* buffer2;     bool exit; }; char** buffer; struct arg_sa {     struct arg_s* lalk;     int current; }; #define type struct arg_sa int sockfd, * newsockfd;//слушающий и массив клиентских сокетов int buflen2 = 10292000;//максимальный размер изображения в байтах для RGBA*Width*Height struct sockaddr_in *cli_addr; int* clilen; int currentclient,cc;//сс-клиент по счету(для записи инкремента имени файла клиента изображения)  typedef unsigned long long uint64_t; typedef unsigned int uint32_t; #define N 8//размер блока #define F32 0xFFFFFFFF uint32_t RK[N];//раундовые ключи #define size64 sizeof(uint64_t) #define ROR(x,n,xsize)((x>>n)|(x<<(xsize-n))) #define ROL(x,n,xsize)((x<<n)|(x>>(xsize-n))) #define RKEY(r)((ROR(K,r*3,size64*8))&F32) const uint64_t K = 0x96EA704CFB1CF671;//ключ шифрования struct hostent* server; uint32_t F(uint32_t subblk, uint32_t key) {     return subblk + key;//функция шифрования } void createRoundKeys() {     for (int i = 0; i < N; i++)         RK[i] = (ROR(K, i * 8, size64 * 8)) & F32; } uint64_t decrypt(uint64_t c_block)//расшифровка блоков сетью фейстеля {     //select subblocks     uint32_t left = (c_block >> 32) & F32;     uint32_t right = c_block & F32;     uint32_t left_, right_;//subblock in the end of round     for (int r = N - 1; r >= 0; r--)     {         uint32_t fk = F(left, RK[r]);         left_ = left;         right_ = right ^ fk;         if (r > 0)//swap places to next round         {             left = right_;             right = left_;         }         else //last round not swap         {             left = left_;             right = right_;         }     }     //collect subblock in block     uint64_t block = left;     block = (block << 32) | (right & F32);     return block; } void session_(LPVOID args)//функция потока ля каждого клиента {     int current = currentclient++;     bzero((char*)&(cli_addr[current]), sizeof(&(cli_addr[current])));     newsockfd[current] = accept(sockfd, (struct sockaddr*)&(cli_addr[current]), &(clilen[current]));     if (newsockfd[current] < 0)     {         error("Error on accept\n");     }     char* s = new char[100];     int n = recv(newsockfd[current], s, 100, 0);     int buflen2 = atoi(s);//получаем число байтов изображения     FILE* f;     std::string name = "Screen";     cc++;     _itoa_s(cc, s, 100, 10);     name += s;     name += ".tga";     fopen_s(&f,name.c_str(), "wb");//создаем файл изображения с увеличиваещимся на 1 именем, чтобы не перезаписать     if (f != NULL)     {         unsigned char tgaHeader[12] = { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };         unsigned char header[6];         n = recv(newsockfd[current], buffer[current], sizeof(tgaHeader), 0);         fwrite((unsigned char*)buffer[current], 1, sizeof(tgaHeader), f);         bzero(buffer[current], buflen2);         n = recv(newsockfd[current], buffer[current],sizeof(header), 0);         fwrite((unsigned char*)buffer[current], 1, sizeof(header), f);//записали хидеры         bzero(buffer[current], buflen2);         n = recv(newsockfd[current], buffer[current], buflen2, 0);//получили байты самого изображения         //         //расшифровка байтов         createRoundKeys();         unsigned long long id;         std::vector<uint64_t>* plaintext = new std::vector<uint64_t>();         int i = 0;         while (i<buflen2)         {             memcpy(&id, (buffer[current]) + i, N);             plaintext->push_back(decrypt(id));             i += 8;         }         std::cout << "i=" << i << std::endl;         i = 0;         char str_[N + 1];         memset(str_, 0, N);         str_[N] = '\0';         for (std::vector<uint64_t>::iterator it = plaintext->begin(); it != plaintext->end(); ++it)         {             memcpy(str_, &*it, N);             fwrite((unsigned char*)str_, sizeof(unsigned char), N/*strlen(str_)*/, f);             i += 8;         }         std::cout << "i=" << i << std::endl;         //конец рашифровки байтов         //fwrite((unsigned char*)buffer[current], sizeof(char), buflen2, f);         fclose(f);     }     system(name.c_str());//открываем изображение *.tga встроенным редактором } int main() {     cc = 0;     WSADATA ws = { 0 };     if (WSAStartup(MAKEWORD(2, 2), &ws) == 0)     {         currentclient = 0;         int maxclients = 2;//максимальное число клиентов         cli_addr = new struct sockaddr_in[maxclients];         clilen = new int[maxclients];         buffer = new char* [maxclients];         for (int i = 0; i < maxclients; i++)         {             clilen[i] = sizeof(cli_addr[i]);         }         sockfd = socket(AF_INET, SOCK_STREAM, 0);//tcp сокет         if (sockfd < 0)             error("ERROR opening socket");         struct sockaddr_in serv_addr;         bzero((char*)&serv_addr, sizeof(serv_addr));         serv_addr.sin_family = AF_INET;         serv_addr.sin_addr.s_addr = INADDR_ANY;         int port = 30000;//порт         serv_addr.sin_port = htons(port);         if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)             error("ERROR on binding");         if (listen(sockfd, 10) < 0)             error("ERROR listen");         HANDLE* thread;//массив потоков для каждого клиента отдельный         struct arg_sa* args;         while (true)         {             newsockfd = new int[maxclients];             thread = (HANDLE*)malloc(sizeof(HANDLE) * maxclients);             args = new struct arg_sa[maxclients];             for (int i = 0; i < maxclients; i++)             {                 args[i].lalk = new struct arg_s();                 buffer[i] = new char[buflen2];             }             int i = -1;             while (++i < maxclients)             {                 Sleep(1);                 args[i].current = i;                 args[i].lalk->exit = false;                 thread[i] = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)(session_), args, 0, 0);             }                 for (int i = 0; i < maxclients; i++)                     WaitForSingleObject(thread[i], INFINITE);//ждем завершения всех потоков             i = -1;             while (++i < maxclients)             {                 shutdown(newsockfd[i], 0);                 TerminateThread(thread[i], 0);             }             //delete[] newsockfd;             //free(thread);             currentclient = 0;             for (int i = 0; i < maxclients; i++)             {                 //delete args[i].lalk;                 //delete[] args[i].lalk->buffer;             }             //delete[] args;         }         shutdown(sockfd, 0);         WSACleanup();         return 0;     }     std::cin.ignore(); }

Вкратце в вечном цикле создаются потоки для каждого клиента и ждут accept пока клиенты подключится. После чего WaitForSingleObject ждет пока они все передадут. У каждого клиента свой сокет и свой буфер передачи. То есть на сервере M+1 сокет, где M количество клиентов. После завершения всех передач, всё повторяется.

Теперь рассмотрим клиент:

#include <iostream> #include <WinSock.h> #include <vector> #pragma comment (lib,"WS2_32.lib") void error(const char* msg) {     //perror(msg);     std::cout << '\n' << WSAGetLastError();     WSACleanup();     std::cin.ignore();     exit(1); } void bzero(char* buf, int l) {     for (int i = 0; i < l; i++)         buf[i] = '\0'; } typedef unsigned long long uint64_t; typedef unsigned int uint32_t; #define N 8 #define F32 0xFFFFFFFF uint32_t RK[N];//раундовые ключи #define size64 sizeof(uint64_t) #define ROR(x,n,xsize)((x>>n)|(x<<(xsize-n))) #define ROL(x,n,xsize)((x<<n)|(x>>(xsize-n))) #define RKEY(r)((ROR(K,r*3,size64*8))&F32) const uint64_t K = 0x96EA704CFB1CF671;//ключ шифрования void createRoundKeys() {     for (int i = 0; i < N; i++)         RK[i] = (ROR(K, i * 8, size64 * 8)) & F32; } uint32_t F(uint32_t subblk, uint32_t key) {     return subblk + key;//функция шифрования } uint64_t encrypt(uint64_t block)//зашифровка блоков сетью Фейстеля {     //select subblocks     uint32_t left = (block >> 32) & F32;     uint32_t right = block & F32;     uint32_t left_, right_;//subblock in the end of round     for (int r = 0; r < N; r++)     {         uint32_t fk = F(left, RK[r]);         left_ = left;         right_ = right ^ fk;         if (r < N - 1)//swap places to next round         {             left = right_;             right = left_;         }         else//last round not swap         {             left = left_;             right = right_;         }     }     //collect subblock in block     uint64_t c_block = left;     c_block = (c_block << 32) | (right & F32);     return c_block; } int main() {     keybd_event(VK_LWIN, 0, 0, 0);     keybd_event('M', 0, 0, 0);     keybd_event('M', 0, KEYEVENTF_KEYUP, 0);     keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP, 0);//эти строки сворачивают все приложения     Sleep(1000);//чтобы сделать скриншот рабочего стола     WSADATA ws = { 0 };     if (WSAStartup(MAKEWORD(2, 2), &ws) == 0)     {         int sockfd;         sockfd = socket(AF_INET, SOCK_STREAM, 0);         struct sockaddr_in serv_addr, cli_addr;         bzero((char*)&serv_addr, sizeof(serv_addr));         bzero((char*)&cli_addr, sizeof(cli_addr));         serv_addr.sin_family = AF_INET;          const char* add = "127.0.0.1";//адрес сервера         serv_addr.sin_addr.s_addr = inet_addr(add);         int port = 30000;//порт         serv_addr.sin_port = htons(port);         int servlen = sizeof(serv_addr);         int n = connect(sockfd, (struct sockaddr*)&serv_addr, servlen);                  //ниже код делает скриншот         HDC ScreenDC = GetDC(0);         HDC MemoryDC = CreateCompatibleDC(ScreenDC);         int ScreenHeight = GetSystemMetrics(SM_CYSCREEN);         int ScreenWidth = GetSystemMetrics(SM_CXSCREEN);         ScreenWidth = ((ScreenWidth - 1) / 4 + 1) * 4;         BITMAPINFO BMI;         BMI.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);         BMI.bmiHeader.biWidth = ScreenWidth;         BMI.bmiHeader.biHeight = ScreenHeight;         BMI.bmiHeader.biSizeImage = ScreenWidth * ScreenHeight * 3;         BMI.bmiHeader.biCompression = BI_RGB;         BMI.bmiHeader.biBitCount = 24;         BMI.bmiHeader.biPlanes = 1;         DWORD ScreenshotSize;         ScreenshotSize = BMI.bmiHeader.biSizeImage;         unsigned char* ImageBuffer;         HBITMAP hBitmap = CreateDIBSection(ScreenDC, &BMI, DIB_RGB_COLORS, (void**)&ImageBuffer, 0, 0);         SelectObject(MemoryDC, hBitmap);         BitBlt(MemoryDC, 0, 0, ScreenWidth, ScreenHeight, ScreenDC, 0, 0, SRCCOPY);         DeleteDC(MemoryDC);         ReleaseDC(NULL, ScreenDC);         FILE* sFile = 0;         unsigned char tgaHeader[12] = { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };         unsigned char header[6];         unsigned char tempColors = 0;         fopen_s(&sFile, "S.tga", "wb");         if (!sFile) {             exit(1);         }         header[0] = ScreenWidth % 256;         header[1] = ScreenWidth / 256;         header[2] = ScreenHeight % 256;         header[3] = ScreenHeight / 256;         header[4] = BMI.bmiHeader.biBitCount;         header[5] = 0;         fwrite(tgaHeader, 1, sizeof(tgaHeader), sFile);         fwrite(header, sizeof(header), 1, sFile);         //конец записали изображение в файл                  //шифруем блоками полезную нагрузку изображения кроме хидеров         createRoundKeys();         std::vector<uint64_t>* msg = new std::vector<uint64_t>(),*crpt = new std::vector<uint64_t>();         unsigned long long id;         int i = 0;         while (i < BMI.bmiHeader.biSizeImage)         {             memcpy(&id, (ImageBuffer + i), N);             msg->push_back(id);             i += 8;         }         std::cout << "i=" << i << std::endl;          uint64_t cipher;         i = 0;         char str_[N + 1];         memset(str_, 0, N);         str_[N] = '\0';         for (std::vector<uint64_t>::iterator it = msg->begin(); it != msg->end(); ++it)         {             cipher = encrypt(*it);             memcpy(str_, &cipher, N);             fwrite((unsigned char*)str_, sizeof(unsigned char), N, sFile);             i += 8;         }         std::cout << "i=" << i << std::endl;         //         //fwrite(ImageBuffer, BMI.bmiHeader.biSizeImage, 1, sFile);         std::cout << BMI.bmiHeader.biSizeImage << std::endl;         fclose(sFile);         DeleteObject(hBitmap);         FILE* f;         fopen_s(&f, "S.tga", "rb");         int count = 0;         if (f != NULL)         {             while (getc(f) != EOF)                 count++;//считаем байты изображения в счетчик чтобы потом передать             fclose(f);         }         count -= 18;         std::cout << count<< std::endl;         char* s = new char[100];         _itoa_s(count, s, 100, 10);         n = send(sockfd, s, 100, 0);//передаем счетчик         char* buffer = new char[count];         fopen_s(&f, "S.tga", "rb");         size_t bytes;         if (f != NULL)         {             memcpy(buffer, tgaHeader, sizeof(tgaHeader));             n = send(sockfd, buffer, sizeof(tgaHeader), 0);             bzero(buffer, count);             memcpy(buffer, header, sizeof(header));             n = send(sockfd, buffer, sizeof(header), 0);             bzero(buffer, count);//передаем хидеры             for(int i=0;i<18;i++)                 fgetc(f);             bzero(buffer, count);             bytes = fread(buffer, sizeof(unsigned char), count, f);             n = send(sockfd,buffer, count, 0);//передаем шифрованные байты изображения             fclose(f);         }         Sleep(1000);         shutdown(sockfd, 0);         WSACleanup();         //system("del S.tga");         delete[] buffer,s;         return 0;     }     //std::cin.ignore(); }

Вот результат работы клиента файл скриншота S.tga, зашифрованный

Видно, что это рабочий стол

А вот результат который был передан на сервер и расшифрован Screen.tga

Как видите обычная сеть Фейстеля не подходит для шифрования, но можно воспользоваться CBC и CFB методами, возможно будет лучше зашифровано, если честно не проверял.

Спасибо за внимание!

ссылка на оригинал статьи https://habr.com/ru/post/520400/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *