1 /* Copyright (c) 2014, Google Inc.
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
15 #include "packeted_bio.h"
22 #include <openssl/crypto.h>
27 const uint8_t kOpcodePacket = 'P';
28 const uint8_t kOpcodeTimeout = 'T';
29 const uint8_t kOpcodeTimeoutAck = 't';
32 explicit PacketedBio(bool advance_clock_arg)
33 : advance_clock(advance_clock_arg) {
34 memset(&timeout, 0, sizeof(timeout));
35 memset(&clock, 0, sizeof(clock));
36 memset(&read_deadline, 0, sizeof(read_deadline));
39 bool HasTimeout() const {
40 return timeout.tv_sec != 0 || timeout.tv_usec != 0;
43 bool CanRead() const {
44 if (read_deadline.tv_sec == 0 && read_deadline.tv_usec == 0) {
48 if (clock.tv_sec == read_deadline.tv_sec) {
49 return clock.tv_usec < read_deadline.tv_usec;
51 return clock.tv_sec < read_deadline.tv_sec;
56 timeval read_deadline;
60 PacketedBio *GetData(BIO *bio) {
61 return (PacketedBio *)BIO_get_data(bio);
64 const PacketedBio *GetData(const BIO *bio) {
65 return GetData(const_cast<BIO*>(bio));
68 // ReadAll reads |len| bytes from |bio| into |out|. It returns 1 on success and
70 static int ReadAll(BIO *bio, uint8_t *out, size_t len) {
72 int chunk_len = INT_MAX;
76 int ret = BIO_read(bio, out, chunk_len);
86 static int PacketedWrite(BIO *bio, const char *in, int inl) {
87 if (BIO_next(bio) == NULL) {
91 BIO_clear_retry_flags(bio);
95 header[0] = kOpcodePacket;
96 header[1] = (inl >> 24) & 0xff;
97 header[2] = (inl >> 16) & 0xff;
98 header[3] = (inl >> 8) & 0xff;
99 header[4] = inl & 0xff;
100 int ret = BIO_write(BIO_next(bio), header, sizeof(header));
102 BIO_copy_next_retry(bio);
107 ret = BIO_write(BIO_next(bio), in, inl);
108 if (ret < 0 || (inl > 0 && ret == 0)) {
109 BIO_copy_next_retry(bio);
116 static int PacketedRead(BIO *bio, char *out, int outl) {
117 PacketedBio *data = GetData(bio);
118 if (BIO_next(bio) == NULL) {
122 BIO_clear_retry_flags(bio);
125 // Check if the read deadline has passed.
126 if (!data->CanRead()) {
127 BIO_set_retry_read(bio);
133 int ret = ReadAll(BIO_next(bio), &opcode, sizeof(opcode));
135 BIO_copy_next_retry(bio);
139 if (opcode == kOpcodeTimeout) {
140 // The caller is required to advance any pending timeouts before
142 if (data->HasTimeout()) {
143 fprintf(stderr, "Unprocessed timeout!\n");
147 // Process the timeout.
149 ret = ReadAll(BIO_next(bio), buf, sizeof(buf));
151 BIO_copy_next_retry(bio);
154 uint64_t timeout = (static_cast<uint64_t>(buf[0]) << 56) |
155 (static_cast<uint64_t>(buf[1]) << 48) |
156 (static_cast<uint64_t>(buf[2]) << 40) |
157 (static_cast<uint64_t>(buf[3]) << 32) |
158 (static_cast<uint64_t>(buf[4]) << 24) |
159 (static_cast<uint64_t>(buf[5]) << 16) |
160 (static_cast<uint64_t>(buf[6]) << 8) |
161 static_cast<uint64_t>(buf[7]);
162 timeout /= 1000; // Convert nanoseconds to microseconds.
164 data->timeout.tv_usec = timeout % 1000000;
165 data->timeout.tv_sec = timeout / 1000000;
167 // Send an ACK to the peer.
168 ret = BIO_write(BIO_next(bio), &kOpcodeTimeoutAck, 1);
174 if (!data->advance_clock) {
175 // Signal to the caller to retry the read, after advancing the clock.
176 BIO_set_retry_read(bio);
180 PacketedBioAdvanceClock(bio);
184 if (opcode != kOpcodePacket) {
185 fprintf(stderr, "Unknown opcode, %u\n", opcode);
189 // Read the length prefix.
190 uint8_t len_bytes[4];
191 ret = ReadAll(BIO_next(bio), len_bytes, sizeof(len_bytes));
193 BIO_copy_next_retry(bio);
197 uint32_t len = (len_bytes[0] << 24) | (len_bytes[1] << 16) |
198 (len_bytes[2] << 8) | len_bytes[3];
199 uint8_t *buf = (uint8_t *)OPENSSL_malloc(len);
203 ret = ReadAll(BIO_next(bio), buf, len);
205 fprintf(stderr, "Packeted BIO was truncated\n");
209 if (outl > (int)len) {
212 memcpy(out, buf, outl);
218 static long PacketedCtrl(BIO *bio, int cmd, long num, void *ptr) {
219 if (cmd == BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT) {
220 memcpy(&GetData(bio)->read_deadline, ptr, sizeof(timeval));
224 if (BIO_next(bio) == NULL) {
227 BIO_clear_retry_flags(bio);
228 int ret = BIO_ctrl(BIO_next(bio), cmd, num, ptr);
229 BIO_copy_next_retry(bio);
233 static int PacketedNew(BIO *bio) {
234 BIO_set_init(bio, 1);
238 static int PacketedFree(BIO *bio) {
244 BIO_set_init(bio, 0);
248 static long PacketedCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) {
249 if (BIO_next(bio) == NULL) {
252 return BIO_callback_ctrl(BIO_next(bio), cmd, fp);
255 static BIO_METHOD *g_packeted_bio_method = NULL;
257 static const BIO_METHOD *PacketedMethod(void)
259 if (g_packeted_bio_method == NULL) {
260 g_packeted_bio_method = BIO_meth_new(BIO_TYPE_FILTER, "packeted bio");
261 if ( g_packeted_bio_method == NULL
262 || !BIO_meth_set_write(g_packeted_bio_method, PacketedWrite)
263 || !BIO_meth_set_read(g_packeted_bio_method, PacketedRead)
264 || !BIO_meth_set_ctrl(g_packeted_bio_method, PacketedCtrl)
265 || !BIO_meth_set_create(g_packeted_bio_method, PacketedNew)
266 || !BIO_meth_set_destroy(g_packeted_bio_method, PacketedFree)
267 || !BIO_meth_set_callback_ctrl(g_packeted_bio_method,
268 PacketedCallbackCtrl))
271 return g_packeted_bio_method;
275 bssl::UniquePtr<BIO> PacketedBioCreate(bool advance_clock) {
276 bssl::UniquePtr<BIO> bio(BIO_new(PacketedMethod()));
280 BIO_set_data(bio.get(), new PacketedBio(advance_clock));
284 timeval PacketedBioGetClock(const BIO *bio) {
285 return GetData(bio)->clock;
288 bool PacketedBioAdvanceClock(BIO *bio) {
289 PacketedBio *data = GetData(bio);
290 if (data == nullptr) {
294 if (!data->HasTimeout()) {
298 data->clock.tv_usec += data->timeout.tv_usec;
299 data->clock.tv_sec += data->clock.tv_usec / 1000000;
300 data->clock.tv_usec %= 1000000;
301 data->clock.tv_sec += data->timeout.tv_sec;
302 memset(&data->timeout, 0, sizeof(data->timeout));