iowrapper.h
3.85 KB
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*
This file is part of FlashMQ (https://www.flashmq.org)
Copyright (C) 2021 Wiebe Cazemier
FlashMQ is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, version 3.
FlashMQ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public
License along with FlashMQ. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef IOWRAPPER_H
#define IOWRAPPER_H
#include "unistd.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
#include <exception>
#include "forward_declarations.h"
#include "types.h"
#include "utils.h"
#include "logger.h"
#include "exceptions.h"
#define WEBSOCKET_MIN_HEADER_BYTES_NEEDED 2
#define WEBSOCKET_MAX_SENDING_HEADER_SIZE 10
#define OPENSSL_ERROR_STRING_SIZE 256 // OpenSSL requires at least 256.
#define OPENSSL_WRONG_VERSION_NUMBER 336130315
enum class IoWrapResult
{
Success = 0,
Interrupted = 1,
Wouldblock = 2,
Disconnected = 3,
Error = 4
};
enum class WebsocketOpcode
{
Continuation = 0x00,
Text = 0x1,
Binary = 0x2,
Close = 0x8,
Ping = 0x9,
Pong = 0xA,
Unknown = 0xF
};
/**
* @brief The IncompleteSslWrite struct facilities the SSL retry
*
* OpenSSL doc: "When a write function call has to be repeated because SSL_get_error(3) returned
* SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, it must be repeated with the same arguments"
*
* Note that we use SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER.
*/
struct IncompleteSslWrite
{
bool valid = false;
size_t nbytes = 0;
IncompleteSslWrite() = default;
IncompleteSslWrite(size_t nbytes);
bool hasPendingWrite() const;
void reset();
};
struct IncompleteWebsocketRead
{
size_t frame_bytes_left = 0;
char maskingKey[4];
int maskingKeyI = 0;
WebsocketOpcode opcode;
void reset();
bool sillWorkingOnFrame() const;
char getNextMaskingByte();
};
enum class WebsocketState
{
NotUpgraded,
Upgrading,
Upgraded
};
/**
* @brief provides a unified wrapper for SSL and websockets to read() and write().
*
*
*/
class IoWrapper
{
Client *parentClient;
const size_t initialBufferSize;
SSL *ssl = nullptr;
bool sslAccepted = false;
IncompleteSslWrite incompleteSslWrite;
bool sslReadWantsWrite = false;
bool sslWriteWantsRead = false;
bool websocket;
WebsocketState websocketState = WebsocketState::NotUpgraded;
CirBuf websocketPendingBytes;
IncompleteWebsocketRead incompleteWebsocketRead;
CirBuf websocketWriteRemainder;
Logger *logger = Logger::getInstance();
ssize_t websocketBytesToReadBuffer(void *buf, const size_t nbytes, IoWrapResult *error);
ssize_t readOrSslRead(int fd, void *buf, size_t nbytes, IoWrapResult *error);
ssize_t writeOrSslWrite(int fd, const void *buf, size_t nbytes, IoWrapResult *error);
ssize_t writeAsMuchOfBufAsWebsocketFrame(const void *buf, const size_t nbytes, WebsocketOpcode opcode = WebsocketOpcode::Binary);
public:
IoWrapper(SSL *ssl, bool websocket, const size_t initialBufferSize, Client *parent);
~IoWrapper();
void startOrContinueSslAccept();
bool getSslReadWantsWrite() const;
bool getSslWriteWantsRead() const;
bool isSslAccepted() const;
bool isSsl() const;
bool hasPendingWrite() const;
bool isWebsocket() const;
WebsocketState getWebsocketState() const;
#ifndef NDEBUG
void setFakeUpgraded();
#endif
ssize_t readWebsocketAndOrSsl(int fd, void *buf, size_t nbytes, IoWrapResult *error);
ssize_t writeWebsocketAndOrSsl(int fd, const void *buf, size_t nbytes, IoWrapResult *error);
void resetBuffersIfEligible();
};
#endif // IOWRAPPER_H