aboutsummaryrefslogtreecommitdiff
path: root/comp3331/server/src/shared/net.hh
blob: b17ca1fcd0d5406b3545d15d868e39a20e85e8a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#ifndef SHARED_NET_HH_
#define SHARED_NET_HH_

#include <algorithm>
#include <arpa/inet.h>
#include <chrono>
#include <cstdint>
#include <cstring>
#include <errno.h>
#include <fcntl.h>
#include <memory>
#include <netdb.h>
#include <optional>
#include <stdexcept>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <thread>
#include <type_traits>
#include <unistd.h>
#include <unordered_map>
#include <vector>

#include "shared/shared.hh"

// Functions in this namespace are common network-related wrappers with error
// checking.
namespace shared {

using addrinfo_t = std::shared_ptr<addrinfo>; // for automatic freeaddrinfo call
addrinfo_t make_addrinfo(const char* const address, const char* const port,
                         const addrinfo&& hints);

using socket_t = int;
int make_socket(const addrinfo_t& info);
int accept_socket(const socket_t& sock);
void bind_socket(const socket_t sock, const addrinfo_t& info);
void connect_socket(const socket_t sock, const addrinfo_t& info);
void listen_socket(const socket_t sock);
void close_socket(const socket_t sock);

struct header {
    std::uint32_t size = 0; // size of packet, including header.
    std::uint32_t sequence; // sequence number of packet
    char command[3];        // command type, if \0\0\0 then it's an ack
};
static_assert(std::is_trivially_copyable<header>::value,
              "header must be memcpy-able");
struct packet {
    header header;
    std::vector<char> contents;
};

// Our packets contents consist of data entries like so:
// std::uint32_t | char[] | DATA
// ^ size of data  ^ name   ^ data, which repeats for size length
// Any packet may contain multiple, or zero, entries. The name can be any
// length. This way we don't have to define structs with hardcoded length
// limits, and we can use this format when sending anything, even files.
// Numeric values will be encoded as strings and converted when necessary.
using contents_t = std::unordered_map<std::string, std::string>;
void send_packet(const packet& packet, const socket_t& sock,
                 const sockaddr_in& dest);
void send_packet(const packet& packet, const socket_t& sock);

packet contents_to_packet(const contents_t& contents,
                          const char* const command);
contents_t packet_to_contents(const packet& packet);

// Recv's sockets, might return nullptr if no packet available.
struct recv_packet_ret {
    sockaddr_in origin;
    struct packet packet;
};

// non-blocking
std::shared_ptr<recv_packet_ret> maybe_urecv_packet(const socket_t& sock);
// blocking, will throw if timeout is elapsed
std::shared_ptr<recv_packet_ret> urecv_packet(const socket_t& rsock,
                                              const bool& timeout = true);
std::shared_ptr<packet> rrecv_packet(const socket_t& rsock,
                                     const bool& timeout = true);

} // namespace shared

#endif