2 * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
4 * Licensed under the Apache License 2.0 (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
10 #include "packeted_bio.h"
17 #include <openssl/crypto.h>
22 const uint8_t kOpcodePacket
= 'P';
23 const uint8_t kOpcodeTimeout
= 'T';
24 const uint8_t kOpcodeTimeoutAck
= 't';
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
));
34 bool HasTimeout() const {
35 return timeout
.tv_sec
!= 0 || timeout
.tv_usec
!= 0;
38 bool CanRead() const {
39 if (read_deadline
.tv_sec
== 0 && read_deadline
.tv_usec
== 0) {
43 if (clock
.tv_sec
== read_deadline
.tv_sec
) {
44 return clock
.tv_usec
< read_deadline
.tv_usec
;
46 return clock
.tv_sec
< read_deadline
.tv_sec
;
51 timeval read_deadline
;
55 PacketedBio
*GetData(BIO
*bio
) {
56 return (PacketedBio
*)BIO_get_data(bio
);
59 const PacketedBio
*GetData(const BIO
*bio
) {
60 return GetData(const_cast<BIO
*>(bio
));
63 // ReadAll reads |len| bytes from |bio| into |out|. It returns 1 on success and
65 static int ReadAll(BIO
*bio
, uint8_t *out
, size_t len
) {
67 int chunk_len
= INT_MAX
;
71 int ret
= BIO_read(bio
, out
, chunk_len
);
81 static int PacketedWrite(BIO
*bio
, const char *in
, int inl
) {
82 if (BIO_next(bio
) == NULL
) {
86 BIO_clear_retry_flags(bio
);
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
));
97 BIO_copy_next_retry(bio
);
102 ret
= BIO_write(BIO_next(bio
), in
, inl
);
103 if (ret
< 0 || (inl
> 0 && ret
== 0)) {
104 BIO_copy_next_retry(bio
);
111 static int PacketedRead(BIO
*bio
, char *out
, int outl
) {
112 PacketedBio
*data
= GetData(bio
);
113 if (BIO_next(bio
) == NULL
) {
117 BIO_clear_retry_flags(bio
);
120 // Check if the read deadline has passed.
121 if (!data
->CanRead()) {
122 BIO_set_retry_read(bio
);
128 int ret
= ReadAll(BIO_next(bio
), &opcode
, sizeof(opcode
));
130 BIO_copy_next_retry(bio
);
134 if (opcode
== kOpcodeTimeout
) {
135 // The caller is required to advance any pending timeouts before
137 if (data
->HasTimeout()) {
138 fprintf(stderr
, "Unprocessed timeout!\n");
142 // Process the timeout.
144 ret
= ReadAll(BIO_next(bio
), buf
, sizeof(buf
));
146 BIO_copy_next_retry(bio
);
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.
159 data
->timeout
.tv_usec
= timeout
% 1000000;
160 data
->timeout
.tv_sec
= timeout
/ 1000000;
162 // Send an ACK to the peer.
163 ret
= BIO_write(BIO_next(bio
), &kOpcodeTimeoutAck
, 1);
169 if (!data
->advance_clock
) {
170 // Signal to the caller to retry the read, after advancing the clock.
171 BIO_set_retry_read(bio
);
175 PacketedBioAdvanceClock(bio
);
179 if (opcode
!= kOpcodePacket
) {
180 fprintf(stderr
, "Unknown opcode, %u\n", opcode
);
184 // Read the length prefix.
185 uint8_t len_bytes
[4];
186 ret
= ReadAll(BIO_next(bio
), len_bytes
, sizeof(len_bytes
));
188 BIO_copy_next_retry(bio
);
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
);
198 ret
= ReadAll(BIO_next(bio
), buf
, len
);
200 fprintf(stderr
, "Packeted BIO was truncated\n");
204 if (outl
> (int)len
) {
207 memcpy(out
, buf
, outl
);
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
));
219 if (BIO_next(bio
) == NULL
) {
222 BIO_clear_retry_flags(bio
);
223 int ret
= BIO_ctrl(BIO_next(bio
), cmd
, num
, ptr
);
224 BIO_copy_next_retry(bio
);
228 static int PacketedNew(BIO
*bio
) {
229 BIO_set_init(bio
, 1);
233 static int PacketedFree(BIO
*bio
) {
239 BIO_set_init(bio
, 0);
243 static long PacketedCallbackCtrl(BIO
*bio
, int cmd
, BIO_info_cb fp
)
245 if (BIO_next(bio
) == NULL
)
247 return BIO_callback_ctrl(BIO_next(bio
), cmd
, fp
);
250 static BIO_METHOD
*g_packeted_bio_method
= NULL
;
252 static const BIO_METHOD
*PacketedMethod(void)
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
))
266 return g_packeted_bio_method
;
270 bssl::UniquePtr
<BIO
> PacketedBioCreate(bool advance_clock
) {
271 bssl::UniquePtr
<BIO
> bio(BIO_new(PacketedMethod()));
275 BIO_set_data(bio
.get(), new PacketedBio(advance_clock
));
279 timeval
PacketedBioGetClock(const BIO
*bio
) {
280 return GetData(bio
)->clock
;
283 bool PacketedBioAdvanceClock(BIO
*bio
) {
284 PacketedBio
*data
= GetData(bio
);
285 if (data
== nullptr) {
289 if (!data
->HasTimeout()) {
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
));