]>
Commit | Line | Data |
---|---|---|
92ab7db6 MC |
1 | /* |
2 | * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. | |
eef977aa | 3 | * |
909f1a2e | 4 | * Licensed under the Apache License 2.0 (the "License"). You may not use |
92ab7db6 MC |
5 | * this file except in compliance with the License. You can obtain a copy |
6 | * in the file LICENSE in the source distribution or at | |
7 | * https://www.openssl.org/source/license.html | |
8 | */ | |
eef977aa MC |
9 | |
10 | #include "packeted_bio.h" | |
11 | ||
12 | #include <assert.h> | |
13 | #include <limits.h> | |
14 | #include <stdio.h> | |
15 | #include <string.h> | |
16 | ||
17 | #include <openssl/crypto.h> | |
18 | ||
19 | ||
20 | namespace { | |
21 | ||
22 | const uint8_t kOpcodePacket = 'P'; | |
23 | const uint8_t kOpcodeTimeout = 'T'; | |
7b73b7be MC |
24 | const uint8_t kOpcodeTimeoutAck = 't'; |
25 | ||
26 | struct PacketedBio { | |
27 | explicit PacketedBio(bool advance_clock_arg) | |
28 | : advance_clock(advance_clock_arg) { | |
29 | memset(&timeout, 0, sizeof(timeout)); | |
30 | memset(&clock, 0, sizeof(clock)); | |
31 | memset(&read_deadline, 0, sizeof(read_deadline)); | |
32 | } | |
33 | ||
34 | bool HasTimeout() const { | |
35 | return timeout.tv_sec != 0 || timeout.tv_usec != 0; | |
36 | } | |
37 | ||
38 | bool CanRead() const { | |
39 | if (read_deadline.tv_sec == 0 && read_deadline.tv_usec == 0) { | |
40 | return true; | |
41 | } | |
42 | ||
43 | if (clock.tv_sec == read_deadline.tv_sec) { | |
44 | return clock.tv_usec < read_deadline.tv_usec; | |
45 | } | |
46 | return clock.tv_sec < read_deadline.tv_sec; | |
47 | } | |
48 | ||
49 | timeval timeout; | |
50 | timeval clock; | |
51 | timeval read_deadline; | |
52 | bool advance_clock; | |
53 | }; | |
54 | ||
55 | PacketedBio *GetData(BIO *bio) { | |
7b73b7be MC |
56 | return (PacketedBio *)BIO_get_data(bio); |
57 | } | |
58 | ||
59 | const PacketedBio *GetData(const BIO *bio) { | |
60 | return GetData(const_cast<BIO*>(bio)); | |
61 | } | |
eef977aa MC |
62 | |
63 | // ReadAll reads |len| bytes from |bio| into |out|. It returns 1 on success and | |
64 | // 0 or -1 on error. | |
65 | static int ReadAll(BIO *bio, uint8_t *out, size_t len) { | |
66 | while (len > 0) { | |
67 | int chunk_len = INT_MAX; | |
68 | if (len <= INT_MAX) { | |
69 | chunk_len = (int)len; | |
70 | } | |
71 | int ret = BIO_read(bio, out, chunk_len); | |
72 | if (ret <= 0) { | |
73 | return ret; | |
74 | } | |
75 | out += ret; | |
76 | len -= ret; | |
77 | } | |
78 | return 1; | |
79 | } | |
80 | ||
81 | static int PacketedWrite(BIO *bio, const char *in, int inl) { | |
82 | if (BIO_next(bio) == NULL) { | |
83 | return 0; | |
84 | } | |
85 | ||
86 | BIO_clear_retry_flags(bio); | |
87 | ||
88 | // Write the header. | |
89 | uint8_t header[5]; | |
90 | header[0] = kOpcodePacket; | |
91 | header[1] = (inl >> 24) & 0xff; | |
92 | header[2] = (inl >> 16) & 0xff; | |
93 | header[3] = (inl >> 8) & 0xff; | |
94 | header[4] = inl & 0xff; | |
95 | int ret = BIO_write(BIO_next(bio), header, sizeof(header)); | |
96 | if (ret <= 0) { | |
97 | BIO_copy_next_retry(bio); | |
98 | return ret; | |
99 | } | |
100 | ||
101 | // Write the buffer. | |
102 | ret = BIO_write(BIO_next(bio), in, inl); | |
103 | if (ret < 0 || (inl > 0 && ret == 0)) { | |
104 | BIO_copy_next_retry(bio); | |
105 | return ret; | |
106 | } | |
107 | assert(ret == inl); | |
108 | return ret; | |
109 | } | |
110 | ||
111 | static int PacketedRead(BIO *bio, char *out, int outl) { | |
7b73b7be | 112 | PacketedBio *data = GetData(bio); |
eef977aa MC |
113 | if (BIO_next(bio) == NULL) { |
114 | return 0; | |
115 | } | |
116 | ||
117 | BIO_clear_retry_flags(bio); | |
118 | ||
7b73b7be MC |
119 | for (;;) { |
120 | // Check if the read deadline has passed. | |
121 | if (!data->CanRead()) { | |
122 | BIO_set_retry_read(bio); | |
123 | return -1; | |
124 | } | |
eef977aa | 125 | |
7b73b7be MC |
126 | // Read the opcode. |
127 | uint8_t opcode; | |
128 | int ret = ReadAll(BIO_next(bio), &opcode, sizeof(opcode)); | |
129 | if (ret <= 0) { | |
130 | BIO_copy_next_retry(bio); | |
131 | return ret; | |
132 | } | |
eef977aa | 133 | |
7b73b7be MC |
134 | if (opcode == kOpcodeTimeout) { |
135 | // The caller is required to advance any pending timeouts before | |
136 | // continuing. | |
137 | if (data->HasTimeout()) { | |
138 | fprintf(stderr, "Unprocessed timeout!\n"); | |
139 | return -1; | |
140 | } | |
eef977aa | 141 | |
7b73b7be MC |
142 | // Process the timeout. |
143 | uint8_t buf[8]; | |
144 | ret = ReadAll(BIO_next(bio), buf, sizeof(buf)); | |
145 | if (ret <= 0) { | |
146 | BIO_copy_next_retry(bio); | |
147 | return ret; | |
148 | } | |
149 | uint64_t timeout = (static_cast<uint64_t>(buf[0]) << 56) | | |
150 | (static_cast<uint64_t>(buf[1]) << 48) | | |
151 | (static_cast<uint64_t>(buf[2]) << 40) | | |
152 | (static_cast<uint64_t>(buf[3]) << 32) | | |
153 | (static_cast<uint64_t>(buf[4]) << 24) | | |
154 | (static_cast<uint64_t>(buf[5]) << 16) | | |
155 | (static_cast<uint64_t>(buf[6]) << 8) | | |
156 | static_cast<uint64_t>(buf[7]); | |
157 | timeout /= 1000; // Convert nanoseconds to microseconds. | |
eef977aa | 158 | |
7b73b7be MC |
159 | data->timeout.tv_usec = timeout % 1000000; |
160 | data->timeout.tv_sec = timeout / 1000000; | |
161 | ||
162 | // Send an ACK to the peer. | |
163 | ret = BIO_write(BIO_next(bio), &kOpcodeTimeoutAck, 1); | |
164 | if (ret <= 0) { | |
165 | return ret; | |
166 | } | |
167 | assert(ret == 1); | |
168 | ||
169 | if (!data->advance_clock) { | |
170 | // Signal to the caller to retry the read, after advancing the clock. | |
171 | BIO_set_retry_read(bio); | |
172 | return -1; | |
173 | } | |
174 | ||
175 | PacketedBioAdvanceClock(bio); | |
176 | continue; | |
177 | } | |
eef977aa | 178 | |
7b73b7be MC |
179 | if (opcode != kOpcodePacket) { |
180 | fprintf(stderr, "Unknown opcode, %u\n", opcode); | |
181 | return -1; | |
182 | } | |
183 | ||
184 | // Read the length prefix. | |
185 | uint8_t len_bytes[4]; | |
186 | ret = ReadAll(BIO_next(bio), len_bytes, sizeof(len_bytes)); | |
187 | if (ret <= 0) { | |
188 | BIO_copy_next_retry(bio); | |
189 | return ret; | |
190 | } | |
191 | ||
192 | uint32_t len = (len_bytes[0] << 24) | (len_bytes[1] << 16) | | |
193 | (len_bytes[2] << 8) | len_bytes[3]; | |
194 | uint8_t *buf = (uint8_t *)OPENSSL_malloc(len); | |
195 | if (buf == NULL) { | |
196 | return -1; | |
197 | } | |
198 | ret = ReadAll(BIO_next(bio), buf, len); | |
199 | if (ret <= 0) { | |
200 | fprintf(stderr, "Packeted BIO was truncated\n"); | |
201 | return -1; | |
202 | } | |
203 | ||
204 | if (outl > (int)len) { | |
205 | outl = len; | |
206 | } | |
207 | memcpy(out, buf, outl); | |
208 | OPENSSL_free(buf); | |
209 | return outl; | |
eef977aa | 210 | } |
eef977aa MC |
211 | } |
212 | ||
213 | static long PacketedCtrl(BIO *bio, int cmd, long num, void *ptr) { | |
7b73b7be MC |
214 | if (cmd == BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT) { |
215 | memcpy(&GetData(bio)->read_deadline, ptr, sizeof(timeval)); | |
216 | return 1; | |
217 | } | |
218 | ||
eef977aa MC |
219 | if (BIO_next(bio) == NULL) { |
220 | return 0; | |
221 | } | |
222 | BIO_clear_retry_flags(bio); | |
223 | int ret = BIO_ctrl(BIO_next(bio), cmd, num, ptr); | |
224 | BIO_copy_next_retry(bio); | |
225 | return ret; | |
226 | } | |
227 | ||
228 | static int PacketedNew(BIO *bio) { | |
229 | BIO_set_init(bio, 1); | |
230 | return 1; | |
231 | } | |
232 | ||
233 | static int PacketedFree(BIO *bio) { | |
234 | if (bio == NULL) { | |
235 | return 0; | |
236 | } | |
237 | ||
7b73b7be | 238 | delete GetData(bio); |
eef977aa MC |
239 | BIO_set_init(bio, 0); |
240 | return 1; | |
241 | } | |
242 | ||
fce78bd4 BE |
243 | static long PacketedCallbackCtrl(BIO *bio, int cmd, BIO_info_cb fp) |
244 | { | |
245 | if (BIO_next(bio) == NULL) | |
eef977aa | 246 | return 0; |
eef977aa MC |
247 | return BIO_callback_ctrl(BIO_next(bio), cmd, fp); |
248 | } | |
249 | ||
250 | static BIO_METHOD *g_packeted_bio_method = NULL; | |
251 | ||
252 | static const BIO_METHOD *PacketedMethod(void) | |
253 | { | |
254 | if (g_packeted_bio_method == NULL) { | |
255 | g_packeted_bio_method = BIO_meth_new(BIO_TYPE_FILTER, "packeted bio"); | |
256 | if ( g_packeted_bio_method == NULL | |
257 | || !BIO_meth_set_write(g_packeted_bio_method, PacketedWrite) | |
258 | || !BIO_meth_set_read(g_packeted_bio_method, PacketedRead) | |
259 | || !BIO_meth_set_ctrl(g_packeted_bio_method, PacketedCtrl) | |
260 | || !BIO_meth_set_create(g_packeted_bio_method, PacketedNew) | |
261 | || !BIO_meth_set_destroy(g_packeted_bio_method, PacketedFree) | |
262 | || !BIO_meth_set_callback_ctrl(g_packeted_bio_method, | |
263 | PacketedCallbackCtrl)) | |
264 | return NULL; | |
265 | } | |
266 | return g_packeted_bio_method; | |
267 | } | |
268 | } // namespace | |
269 | ||
7b73b7be MC |
270 | bssl::UniquePtr<BIO> PacketedBioCreate(bool advance_clock) { |
271 | bssl::UniquePtr<BIO> bio(BIO_new(PacketedMethod())); | |
eef977aa MC |
272 | if (!bio) { |
273 | return nullptr; | |
274 | } | |
7b73b7be | 275 | BIO_set_data(bio.get(), new PacketedBio(advance_clock)); |
eef977aa MC |
276 | return bio; |
277 | } | |
7b73b7be MC |
278 | |
279 | timeval PacketedBioGetClock(const BIO *bio) { | |
280 | return GetData(bio)->clock; | |
281 | } | |
282 | ||
283 | bool PacketedBioAdvanceClock(BIO *bio) { | |
284 | PacketedBio *data = GetData(bio); | |
285 | if (data == nullptr) { | |
286 | return false; | |
287 | } | |
288 | ||
289 | if (!data->HasTimeout()) { | |
290 | return false; | |
291 | } | |
292 | ||
293 | data->clock.tv_usec += data->timeout.tv_usec; | |
294 | data->clock.tv_sec += data->clock.tv_usec / 1000000; | |
295 | data->clock.tv_usec %= 1000000; | |
296 | data->clock.tv_sec += data->timeout.tv_sec; | |
297 | memset(&data->timeout, 0, sizeof(data->timeout)); | |
298 | return true; | |
299 | } |