commit 26b22a3bc1e20e1a4021cad7461848080d038c03 Author: Denis Tereshkin Date: Wed Jul 9 23:14:35 2025 +0700 eblapp proof-of-concept diff --git a/eblapp-client/CMakeLists.txt b/eblapp-client/CMakeLists.txt new file mode 100644 index 0000000..7a4a8f4 --- /dev/null +++ b/eblapp-client/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.10) +project(eblapp-client LANGUAGES C) + +include_directories(include) + +file(GLOB SOURCE_FILES src/*.c) + +add_executable(eblapp-client + ${SOURCE_FILES}) diff --git a/eblapp-client/include/eblapp.h b/eblapp-client/include/eblapp.h new file mode 100644 index 0000000..e82dab6 --- /dev/null +++ b/eblapp-client/include/eblapp.h @@ -0,0 +1,40 @@ +#ifndef EBLAPP_H +#define EBLAPP_H + +#include + +#define EBLAPP_REQUEST_FILE 0x11 +#define EBLAPP_TRANSFER_START 0x12 +#define EBLAPP_TRANSFER_DATA 0x13 +#define EBLAPP_TRANSFER_END 0x14 +#define EBLAPP_ACK 0x15 +#define EBLAPP_WTF 0x16 + +struct Eblapp_request { + uint32_t opcode; + uint32_t filename_size; + uint8_t filename[]; +}; + +struct Eblapp_transfer_start { + uint32_t opcode; + uint32_t file_size; + uint32_t crc; +}; + +struct Eblapp_transfer_data { + uint32_t opcode; + uint32_t block_start; + uint8_t payload[]; +}; + +struct Eblapp_transfer_end { + uint32_t opcode; +}; + +struct Eblapp_ack { + uint32_t opcode; + uint32_t ack_byte; +}; + +#endif /* EBLAPP_H */ diff --git a/eblapp-client/src/main.c b/eblapp-client/src/main.c new file mode 100644 index 0000000..f5bcc7a --- /dev/null +++ b/eblapp-client/src/main.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "eblapp.h" + +#define PORT 1069 +#define MAX_PACKET_SIZE 1024 +#define TIMEOUT 5 + +void error(const char *msg) { + perror(msg); + exit(1); +} + +int main(int argc, char *argv[]) { + if (argc < 3) { + fprintf(stderr, "Usage: %s hostname filename\n", argv[0]); + exit(1); + } + + char buffer[MAX_PACKET_SIZE]; + uint32_t current_offset = 0; + int sockfd; + struct sockaddr_in serv_addr; + struct hostent *server; + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + error("ERROR opening socket"); + + server = gethostbyname(argv[1]); + if (server == NULL) { + fprintf(stderr, "ERROR, no such host\n"); + exit(1); + } + + memset((char *)&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + memcpy((char *)&serv_addr.sin_addr.s_addr, (char *)server->h_addr, + server->h_length); + serv_addr.sin_port = htons(PORT); + + struct Eblapp_request *request = (struct Eblapp_request *)buffer; + request->opcode = EBLAPP_REQUEST_FILE; + request->filename_size = strlen(argv[2]); + memcpy(request + 1, argv[2], request->filename_size); + + int packet_len = strlen(argv[2]) + sizeof(*request); + + if (sendto(sockfd, buffer, packet_len, 0, (struct sockaddr *)&serv_addr, + sizeof(serv_addr)) < 0) { + error("ERROR sending RRQ"); + } + + printf("Requesting file: %s\n", argv[2]); + + // Open output file + int fd = open(argv[3], O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + error("ERROR creating file"); + } + + int wtf = 0; + while (1) { + fd_set readfds; + struct timeval tv; + struct Eblapp_transfer_data *data = (struct Eblapp_transfer_data *)buffer; + + FD_ZERO(&readfds); + FD_SET(sockfd, &readfds); + tv.tv_sec = TIMEOUT; + tv.tv_usec = 0; + + int rv = select(sockfd + 1, &readfds, NULL, NULL, &tv); + if (rv > 0) { + socklen_t serv_len = sizeof(serv_addr); + int n = recvfrom(sockfd, buffer, MAX_PACKET_SIZE, 0, + (struct sockaddr *)&serv_addr, &serv_len); + if (n < 4) + continue; + + printf("opcode %d\n", data->opcode); + if (data->opcode == EBLAPP_TRANSFER_DATA) { // DATA + struct Eblapp_ack ack = {0}; + if (data->block_start != current_offset) { + if (!wtf) { + printf("Invalid order (%d/%d)\n", data->block_start, + current_offset); + ack.opcode = EBLAPP_WTF; + ack.ack_byte = current_offset; + if (sendto(sockfd, &ack, sizeof(ack), 0, + (struct sockaddr *)&serv_addr, serv_len) < 0) { + error("ERROR sending ACK"); + } + wtf = 1; + } + } else { + wtf = 0; + printf("got %d\n", current_offset); + current_offset += (n - 8); + ack.opcode = EBLAPP_ACK; + ack.ack_byte = current_offset; + if (sendto(sockfd, &ack, sizeof(ack), 0, + (struct sockaddr *)&serv_addr, serv_len) < 0) { + error("ERROR sending ACK"); + } + write(fd, buffer + 8, (n - 8)); + } + } else if (data->opcode == EBLAPP_TRANSFER_END) { + printf("got transfer end\n"); + break; + } + } + } + + close(sockfd); + close(fd); + return 0; +} diff --git a/eblapp-server/.clang-format b/eblapp-server/.clang-format new file mode 100644 index 0000000..49a3889 --- /dev/null +++ b/eblapp-server/.clang-format @@ -0,0 +1,165 @@ +--- +Language: C +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveMacros: None +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: true +BreakBeforeBraces: Allman +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 160 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: false +DerivePointerAlignment: false +DisableFormat: false +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +StatementAttributeLikeMacros: + - Q_EMIT +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentCaseLabels: false +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentRequires: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: Never +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard: Latest +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME +... diff --git a/eblapp-server/CMakeLists.txt b/eblapp-server/CMakeLists.txt new file mode 100644 index 0000000..efece80 --- /dev/null +++ b/eblapp-server/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.10) +project(eblapp-server LANGUAGES C) + +include_directories(include) + +file(GLOB SOURCE_FILES src/*.c) + +add_executable(eblapp-server + ${SOURCE_FILES}) diff --git a/eblapp-server/include/crc32.h b/eblapp-server/include/crc32.h new file mode 100644 index 0000000..b9c7b08 --- /dev/null +++ b/eblapp-server/include/crc32.h @@ -0,0 +1,104 @@ +// Polynomial = 0xEDB88320 + +#ifndef __CRC_32__ +#define __CRC_32__ + +typedef unsigned int crc32_t; + +static crc32_t crc32_table[256] = +{ + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + +crc32_t crc32(const void *data, unsigned int length) { + crc32_t crc = 0xFFFFFFFF; + + for (unsigned int index = 0; index< length; index++) { + crc = (crc >> 8) ^ crc32_table[(crc ^ ((unsigned char*)data)[index]) & 0xFF]; + } + + return crc ^ 0xFFFFFFFF; +} + +crc32_t crc32_init() { + return 0xFFFFFFFF; +} + +crc32_t crc32_update(unsigned int crc, void *data, unsigned int length) { + crc32_t _crc = crc; + + for (unsigned int index = 0; index < length; index++) { + _crc = (_crc >> 8) ^ crc32_table[(_crc ^ ((unsigned char*)data)[index]) & 0xFF]; + } + + return _crc; +} + +crc32_t crc32_final(unsigned int _crc) { + return _crc ^ 0XFFFFFFFF; +} + +#endif diff --git a/eblapp-server/include/eblapp.h b/eblapp-server/include/eblapp.h new file mode 100644 index 0000000..a7c485e --- /dev/null +++ b/eblapp-server/include/eblapp.h @@ -0,0 +1,46 @@ +#ifndef EBLAPP_H +#define EBLAPP_H + +#include + +#define EBLAPP_REQUEST_FILE 0x11 +#define EBLAPP_TRANSFER_START 0x12 +#define EBLAPP_TRANSFER_DATA 0x13 +#define EBLAPP_TRANSFER_END 0x14 +#define EBLAPP_ACK 0x15 +#define EBLAPP_WTF 0x16 + +struct Eblapp_request +{ + uint32_t opcode; + uint32_t filename_size; + uint8_t filename[]; +}; + +struct Eblapp_transfer_start +{ + uint32_t opcode; + uint32_t file_size; + uint32_t crc; +}; + +struct Eblapp_transfer_data +{ + uint32_t opcode; + uint32_t block_start; + uint8_t payload[]; +}; + +struct Eblapp_transfer_end +{ + uint32_t opcode; +}; + +struct Eblapp_ack +{ + uint32_t opcode; + uint32_t ack_byte; +}; + + +#endif /* EBLAPP_H */ diff --git a/eblapp-server/include/ring_buffer.h b/eblapp-server/include/ring_buffer.h new file mode 100644 index 0000000..ddc818c --- /dev/null +++ b/eblapp-server/include/ring_buffer.h @@ -0,0 +1,20 @@ +#ifndef RING_BUFFER_H +#define RING_BUFFER_H + +#include +#include + +struct ring_buffer; + +int ring_buffer__init(struct ring_buffer **buffer, size_t size); +int ring_buffer__fini(struct ring_buffer *buffer); +int ring_buffer__write(struct ring_buffer *buffer, uint8_t *data, + size_t datalen); +int ring_buffer__read(struct ring_buffer *buffer, uint8_t *data, + size_t *datalen); +int ring_buffer__unread(struct ring_buffer *buffer, size_t bytes); +int ring_buffer__ack(struct ring_buffer *buffer, size_t acked_bytes); +int ring_buffer__get_unread_bytes(struct ring_buffer *buffer, size_t *bytes); +int ring_buffer__get_free_bytes(struct ring_buffer *buffer, size_t *bytes); + +#endif /* RING_BUFFER_H */ diff --git a/eblapp-server/src/main.c b/eblapp-server/src/main.c new file mode 100644 index 0000000..655d13b --- /dev/null +++ b/eblapp-server/src/main.c @@ -0,0 +1,215 @@ +// Eltex BoolLoader Application Provisioning Protocol Server +#include +#include +#include +#include +#include +#include +#include +#include + +#include "eblapp.h" +#include "ring_buffer.h" + +#define BUFFER_SIZE 65536 +#define PORT 1069 +#define MAX_PACKET_SIZE 1024 +#define TIMEOUT 5 + +void error(const char* msg) +{ + perror(msg); + exit(1); +} + +int main() +{ + int ret = 0; + int sockfd, newsockfd; + socklen_t clilen; + struct sockaddr_in serv_addr, cli_addr; + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if(sockfd < 0) + { + error("ERROR opening socket"); + } + + memset((char*)&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(PORT); + + if(bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) + error("ERROR on binding"); + + printf("Server started on port %d\n", PORT); + + while(1) + { + char buffer[MAX_PACKET_SIZE]; + clilen = sizeof(cli_addr); + int n = recvfrom(sockfd, buffer, MAX_PACKET_SIZE, 0, (struct sockaddr*)&cli_addr, &clilen); + if(n < 0) + { + error("ERROR reading from socket"); + } + + struct Eblapp_request* request = (struct Eblapp_request*)buffer; + if(request->opcode == EBLAPP_REQUEST_FILE) + { + size_t current_acked_byte = 0; + + struct ring_buffer* ring_buffer = NULL; + if(ring_buffer__init(&ring_buffer, BUFFER_SIZE)) + { + error("ring_buffer__init() failed"); + } + struct stat st = {}; + char filename[256] = {}; + uint32_t current_offset = 0; + struct Eblapp_request* request = (struct Eblapp_request*)buffer; + memcpy(filename, request->filename, request->filename_size); + printf("Request file: %s\n", filename); + ret = stat(filename, &st); + if(ret) + { + printf("stat failed(): %d", ret); + break; + } + + int fd = open(filename, O_RDONLY); + if(fd < 0) + { + printf("File not found: %s\n", filename); + continue; + } + + while(1) + { + size_t space_left = 0; + size_t bytes_left = 0; + ring_buffer__get_free_bytes(ring_buffer, &space_left); + printf("space_left: %zu\n", space_left); + while(space_left) + { + size_t toread = space_left > MAX_PACKET_SIZE ? MAX_PACKET_SIZE : space_left; + int bytes_read = read(fd, buffer, toread); + printf("bytes_read: %d\n", bytes_read); + if(bytes_read <= 0) + { + break; + } + + if(ring_buffer__write(ring_buffer, buffer, bytes_read) < 0) + { + error("ring_buffer__write() failed"); + } + + ring_buffer__get_free_bytes(ring_buffer, &space_left); + printf("space_left: %zu\n", space_left); + } + + ring_buffer__get_unread_bytes(ring_buffer, &bytes_left); + printf("bytes_left: %zu\n", bytes_left); + while(bytes_left) + { + struct Eblapp_transfer_data* data = (struct Eblapp_transfer_data*)buffer; + size_t toread = bytes_left; + if(toread > MAX_PACKET_SIZE - 8) + { + toread = MAX_PACKET_SIZE - 8; + } + if(ring_buffer__read(ring_buffer, buffer + 8, &toread) < 0) + { + perror("ring_buffer__read() failed"); + } + + data->opcode = EBLAPP_TRANSFER_DATA; + data->block_start = current_offset; + int n = sendto(sockfd, buffer, toread + sizeof(*data), 0, (struct sockaddr*)&cli_addr, clilen); + if(n < 0) + { + error("ERROR sending packet"); + } + + current_offset += (n - 8); + ring_buffer__get_unread_bytes(ring_buffer, &bytes_left); + printf("bytes_left: %zu (sent: %d)\n", bytes_left, current_offset); + } + + // Wait for ACKs + fd_set readfds; + struct timeval tv; + int ack_received = 0; + + FD_ZERO(&readfds); + FD_SET(sockfd, &readfds); + tv.tv_sec = TIMEOUT; + tv.tv_usec = 0; + + int rv = select(sockfd + 1, &readfds, NULL, NULL, &tv); + if(rv > 0) + { + n = recvfrom(sockfd, buffer, MAX_PACKET_SIZE, 0, (struct sockaddr*)&cli_addr, &clilen); + if(n >= 4) + { + struct Eblapp_ack* ack_packet = (struct Eblapp_ack*)buffer; + if(ack_packet->opcode == EBLAPP_ACK) + { + uint32_t acked_byte = ack_packet->ack_byte; + printf("ack %d/%zu\n", acked_byte, st.st_size); + if(acked_byte >= st.st_size) + { + printf("Transfer done\n"); + break; + } + + if(acked_byte >= current_acked_byte) + { + size_t delta = acked_byte - current_acked_byte; + ring_buffer__ack(ring_buffer, delta); + current_acked_byte = acked_byte; + } + ack_received = 1; + } + else if(ack_packet->opcode == EBLAPP_WTF) + { + printf("Wtf received: %d/%d\n", ack_packet->ack_byte, current_acked_byte); + ack_received = 0; + } + } + } + + if(!ack_received) + { + // Timeout, resend all packets in window + printf("!ack recevied: %d/%d/(%d)\n", current_acked_byte, current_offset, current_offset - current_acked_byte); + ring_buffer__unread(ring_buffer, current_offset - current_acked_byte); + current_offset = current_acked_byte; + } + } + + close(fd); + printf("File %s sent successfully\n", filename); + + { + struct Eblapp_transfer_end transfer_end = {}; + transfer_end.opcode = EBLAPP_TRANSFER_END; + + int n = sendto(sockfd, &transfer_end, sizeof(transfer_end), 0, (struct sockaddr*)&cli_addr, clilen); + if(n < 0) + { + error("ERROR sending transfer end"); + } + else + { + printf("sent %d\n", n); + } + } + } + } + + close(sockfd); + return 0; +} diff --git a/eblapp-server/src/ring_buffer.c b/eblapp-server/src/ring_buffer.c new file mode 100644 index 0000000..1b9e2cf --- /dev/null +++ b/eblapp-server/src/ring_buffer.c @@ -0,0 +1,190 @@ +#include "ring_buffer.h" +#include +#include +#include + +struct ring_buffer +{ + uint8_t* buffer; + size_t size; + size_t write_ptr; + size_t read_ptr; + size_t ack_ptr; +}; + +static size_t min_size_t(size_t a, size_t b) +{ + return a < b ? a : b; +} + +int ring_buffer__init(struct ring_buffer** buffer, size_t size) +{ + if(!buffer || size == 0) + { + return -1; + } + + struct ring_buffer* rb = (struct ring_buffer*)calloc(1, sizeof(struct ring_buffer)); + if(!rb) + { + return -1; + } + + rb->buffer = (uint8_t*)malloc(size); + if(!rb->buffer) + { + free(rb); + return -1; + } + + rb->size = size; + rb->write_ptr = 0; + rb->read_ptr = 0; + rb->ack_ptr = 0; + + *buffer = rb; + return 0; +} + +int ring_buffer__fini(struct ring_buffer* buffer) +{ + if(!buffer) + { + return -1; + } + + if(buffer->buffer) + { + free(buffer->buffer); + } + free(buffer); + return 0; +} + +int ring_buffer__write(struct ring_buffer* buffer, uint8_t* data, size_t datalen) +{ + if(!buffer || !data || datalen == 0) + { + return -1; + } + + size_t free_space = (buffer->size - 1 - (buffer->write_ptr - buffer->ack_ptr)) % buffer->size; + if(free_space < datalen) + { + return -1; // Недостаточно места + } + + size_t part1 = buffer->size - (buffer->write_ptr % buffer->size); + if(datalen <= part1) + { + memcpy(buffer->buffer + (buffer->write_ptr % buffer->size), data, datalen); + } + else + { + memcpy(buffer->buffer + (buffer->write_ptr % buffer->size), data, part1); + memcpy(buffer->buffer, data + part1, datalen - part1); + } + + buffer->write_ptr = (buffer->write_ptr + datalen) % buffer->size; + return 0; +} + +int ring_buffer__read(struct ring_buffer* buffer, uint8_t* data, size_t* datalen) +{ + if(!buffer || !data || !datalen || *datalen == 0) + { + return -1; + } + + size_t available = (buffer->write_ptr - buffer->read_ptr + buffer->size) % buffer->size; + if(available == 0) + { + *datalen = 0; + return -1; // Нет данных для чтения + } + + size_t to_read = min_size_t(*datalen, available); + size_t part1 = buffer->size - (buffer->read_ptr % buffer->size); + if(to_read <= part1) + { + memcpy(data, buffer->buffer + (buffer->read_ptr % buffer->size), to_read); + } + else + { + memcpy(data, buffer->buffer + (buffer->read_ptr % buffer->size), part1); + memcpy(data + part1, buffer->buffer, to_read - part1); + } + + buffer->read_ptr = (buffer->read_ptr + to_read) % buffer->size; + + *datalen = to_read; + return 0; +} + +int ring_buffer__unread(struct ring_buffer* buffer, size_t bytes) +{ + if(!buffer) + { + return -1; + } + + buffer->read_ptr = (buffer->read_ptr - bytes) % buffer->size; +} + +int ring_buffer__ack(struct ring_buffer* buffer, size_t acked_bytes) +{ + if(!buffer || acked_bytes == 0) + { + return -1; + } + + size_t available = (buffer->read_ptr - buffer->ack_ptr + buffer->size) % buffer->size; + if(acked_bytes > available) + { + acked_bytes = available; // Защита от переполнения + } + + buffer->ack_ptr = (buffer->ack_ptr + acked_bytes) % buffer->size; + return 0; +} + +int ring_buffer__get_unread_bytes(struct ring_buffer* buffer, size_t* bytes) +{ + if(!buffer || !bytes) + { + return -1; + } + + size_t free_space = 0; + if(buffer->write_ptr >= buffer->read_ptr) + { + free_space = buffer->write_ptr - buffer->read_ptr; + } + else + { + free_space = buffer->size - (buffer->read_ptr - buffer->write_ptr); + } + + *bytes = free_space; + return 0; +} +int ring_buffer__get_free_bytes(struct ring_buffer* buffer, size_t* bytes) +{ + if(!buffer || !bytes) + { + return -1; + } + + size_t free_space = 0; + if(buffer->write_ptr >= buffer->ack_ptr) + { + free_space = buffer->size - 1 - (buffer->write_ptr - buffer->ack_ptr); + } + else + { + free_space = (buffer->ack_ptr - buffer->write_ptr) - 1; + } + + *bytes = free_space; + return 0; +}