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 { 245 if (BIO_next(bio) == NULL) 246 return 0; 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 } 300