struct timeval include guards
[openssl.git] / test / ossl_shim / packeted_bio.cc
1 /*
2  * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the OpenSSL license (the "License").  You may not use
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  */
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';
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) {
56   return (PacketedBio *)BIO_get_data(bio);
57 }
58
59 const PacketedBio *GetData(const BIO *bio) {
60   return GetData(const_cast<BIO*>(bio));
61 }
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) {
112   PacketedBio *data = GetData(bio);
113   if (BIO_next(bio) == NULL) {
114     return 0;
115   }
116
117   BIO_clear_retry_flags(bio);
118
119   for (;;) {
120     // Check if the read deadline has passed.
121     if (!data->CanRead()) {
122       BIO_set_retry_read(bio);
123       return -1;
124     }
125
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     }
133
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       }
141
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.
158
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     }
178
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;
210   }
211 }
212
213 static long PacketedCtrl(BIO *bio, int cmd, long num, void *ptr) {
214   if (cmd == BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT) {
215     memcpy(&GetData(bio)->read_deadline, ptr, sizeof(timeval));
216     return 1;
217   }
218
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
238   delete GetData(bio);
239   BIO_set_init(bio, 0);
240   return 1;
241 }
242
243 static long PacketedCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) {
244   if (BIO_next(bio) == NULL) {
245     return 0;
246   }
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
270 bssl::UniquePtr<BIO> PacketedBioCreate(bool advance_clock) {
271   bssl::UniquePtr<BIO> bio(BIO_new(PacketedMethod()));
272   if (!bio) {
273     return nullptr;
274   }
275   BIO_set_data(bio.get(), new PacketedBio(advance_clock));
276   return bio;
277 }
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 }