Хранить выделенную структуру (используя "struct hack") в std :: vector - PullRequest
0 голосов
/ 14 января 2020

В нашем назначении мы должны построить наш собственный обработчик TCP (предоставляется структура). Сейчас я нахожусь в той точке, когда мне нужно собрать все сегменты TCP и сохранить их в векторе, чтобы я мог собрать поток после получения последнего сегмента и переслать его на следующий уровень (например, HTTP). Для этого мы должны реализовать void handle_packet(). В нашей структуре есть несколько структур и классов, которые должны «помочь» нам.

Это то, что я до сих пор придумал:

tcp.h

#include <iostream>
#include <cstdint>
#include <vector>
#include <netinet/tcp.h> // struct tcphdr

using Header = struct tcphdr;

struct Segment {

    const Header * hdr;
    uint8_t payload[];
};

struct Stream {

  std::vector<Segment> segments;

  void add_segment(Segment new_segment);

};

void Stream::add_segment(Segment new_segment) {

    segments.push_back(new_segment);

    // testing: print all sequence numbers of each segment
    for(size_t i = 0; i < segments.size(); i++)
        std::cout << ntohl(segments[i].hdr->seq) << std::endl;
}

class Protocol {
private:
  Stream streams;

public:
  void handle_packet(const Header* tcp_segment_hdr, uint8_t* tcp_payload, size_t tcp_payload_len);
};

tcp. cpp

#include "tcp.h"

void handle_packet(const Header* tcp_segment_hdr, uint8_t* tcp_payload, size_t tcp_payload_len) {

    // "struct hack" to copy content 
    Segment* segment = reinterpret_cast<Segment *>(malloc(sizeof(struct Segment) + buffer_len * sizeof(uint8_t)) );

    // store the header
    segment->hdr = tcp_segment_hdr; // add the header

    // store the payload
    for(size_t i = 0; i < tcp_payload_len; i++)
        segment->payload[i] = tcp_payload[i];

    streams.add_segment(*segment);

    free(segment);
}

Я копирую заголовок и полезную нагрузку, используя "взлом структуры", в структуру Segment * (это был единственный рабочий способ, который я придумал). После этого я сохраняю новый сегмент в streams (член класса Protocol) переменной-члена vector<Segments>. Затем я освобождаю память.

После этого я хотел посмотреть, сохранено ли все в segments (для тестирования я попытался напечатать каждый порядковый номер сегмента) - и это не так. Однако размер по-прежнему правильный.

Я полагаю, это потому, что я free'd структура в handle_packet() впоследствии, и это также влияет на мою запись в std::vector<Segment>segments .. Но как я могу добиться, чтобы записи все еще были в там без каких-либо утечек памяти? Я погуглил, но не нашел удовлетворительного решения, как решить мою проблему.

Любая помощь приветствуется! Спасибо

Редактировать : Я также получаю следующую ошибку в AddressSanitizer:

==22658==ERROR: AddressSanitizer: stack-buffer-overflow on address 
0x7fff5288b66c at pc 0x559511b2f7bc bp 0x7fff5288b2b0 sp 0x7fff5288b2a0
WRITE of size 1 at 0x7fff5288b66c thread T0

В IPv4 у нас была следующая структура:

struct Fragment : public std::vector<uint8_t> {
  using std::vector<uint8_t>::vector;
  const Header *getHeader() { return (const Header *)data(); }
};

Но я этого не делал, так как не мог понять, как «заполнить» значения и добавить запись. Любые идеи, как я могу принять struct Fragment до struct Segment и заполнить / добавить (постоянные) значения?

Редактировать 2: В качестве временного результата, я дал полезную нагрузку фиксированного размера (1500) и удалите «struct hack» и отредактируйте его обратно до простого Segment segment.

Edit 3: Хорошо, он по-прежнему не работает даже с фиксированным размером.

Редактировать 4: Я нашел рабочее решение! Однако я опишу свое решение в субботу (после крайнего срока), чтобы избежать плагиата.

1 Ответ

0 голосов
/ 18 января 2020

Как и было обещано, вот мое решение. Я взял следующее определение структуры:

/**
* Container for a segment of a TCP stream.
*/
struct Segment : public std::vector<uint8_t> {
    using std::vector<uint8_t>::vector; 
};

Чтобы скопировать данные segment в структуру, которую я сделал:

Segment segment;

// fill the segment with the data
for(unsigned int i = 0; i < buffer_len; i++)
  segment.push_back(buffer[i]);

И для доступа к данным вы можете использовать определенные data() член из std::vector (от которого наследуется структура). У меня также был еще один struct Stream с участником по имени Segment allSegments, в который были добавлены все segment (всего всех сегментов). Получившийся доступ выглядел так:

for(unsigned int i = 0; i < allSegments.size(); i++)
{
  for(unsigned int j = 0; j < allSegments[i].size(); j++)
    printf( "%x",allSegments[i].data()[j] );
}

Я не знал, что вы можете наследовать std::vector. struct Segment был заранее определен нашими ассистентами и не реализован мной. Сначала я не понимал, как вы можете получить доступ к данным, и хотел реализовать другой способ - вот почему я придумал struct hack.

...