1 /* Copyright (c) 2014, Google Inc. 2 * 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. 6 * 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. */ 14 15 #include "packeted_bio.h" 16 17 #include <assert.h> 18 #include <limits.h> 19 #include <stdio.h> 20 #include <string.h> 21 22 #include <openssl/mem.h> 23 24 #include "../../crypto/internal.h" 25 26 27 namespace { 28 29 extern const BIO_METHOD g_packeted_bio_method; 30 31 const uint8_t kOpcodePacket = 'P'; 32 const uint8_t kOpcodeTimeout = 'T'; 33 const uint8_t kOpcodeTimeoutAck = 't'; 34 35 struct PacketedBio { 36 explicit PacketedBio(timeval *clock_arg) 37 : clock(clock_arg) { 38 OPENSSL_memset(&timeout, 0, sizeof(timeout)); 39 } 40 41 bool HasTimeout() const { 42 return timeout.tv_sec != 0 || timeout.tv_usec != 0; 43 } 44 45 timeval timeout; 46 timeval *clock; 47 }; 48 49 PacketedBio *GetData(BIO *bio) { 50 if (bio->method != &g_packeted_bio_method) { 51 return NULL; 52 } 53 return (PacketedBio *)bio->ptr; 54 } 55 56 // ReadAll reads |len| bytes from |bio| into |out|. It returns 1 on success and 57 // 0 or -1 on error. 58 static int ReadAll(BIO *bio, uint8_t *out, size_t len) { 59 while (len > 0) { 60 int chunk_len = INT_MAX; 61 if (len <= INT_MAX) { 62 chunk_len = (int)len; 63 } 64 int ret = BIO_read(bio, out, chunk_len); 65 if (ret <= 0) { 66 return ret; 67 } 68 out += ret; 69 len -= ret; 70 } 71 return 1; 72 } 73 74 static int PacketedWrite(BIO *bio, const char *in, int inl) { 75 if (bio->next_bio == NULL) { 76 return 0; 77 } 78 79 BIO_clear_retry_flags(bio); 80 81 // Write the header. 82 uint8_t header[5]; 83 header[0] = kOpcodePacket; 84 header[1] = (inl >> 24) & 0xff; 85 header[2] = (inl >> 16) & 0xff; 86 header[3] = (inl >> 8) & 0xff; 87 header[4] = inl & 0xff; 88 int ret = BIO_write(bio->next_bio, header, sizeof(header)); 89 if (ret <= 0) { 90 BIO_copy_next_retry(bio); 91 return ret; 92 } 93 94 // Write the buffer. 95 ret = BIO_write(bio->next_bio, in, inl); 96 if (ret < 0 || (inl > 0 && ret == 0)) { 97 BIO_copy_next_retry(bio); 98 return ret; 99 } 100 assert(ret == inl); 101 return ret; 102 } 103 104 static int PacketedRead(BIO *bio, char *out, int outl) { 105 PacketedBio *data = GetData(bio); 106 if (bio->next_bio == NULL) { 107 return 0; 108 } 109 110 BIO_clear_retry_flags(bio); 111 112 // Read the opcode. 113 uint8_t opcode; 114 int ret = ReadAll(bio->next_bio, &opcode, sizeof(opcode)); 115 if (ret <= 0) { 116 BIO_copy_next_retry(bio); 117 return ret; 118 } 119 120 if (opcode == kOpcodeTimeout) { 121 // The caller is required to advance any pending timeouts before continuing. 122 if (data->HasTimeout()) { 123 fprintf(stderr, "Unprocessed timeout!\n"); 124 return -1; 125 } 126 127 // Process the timeout. 128 uint8_t buf[8]; 129 ret = ReadAll(bio->next_bio, buf, sizeof(buf)); 130 if (ret <= 0) { 131 BIO_copy_next_retry(bio); 132 return ret; 133 } 134 uint64_t timeout = (static_cast<uint64_t>(buf[0]) << 56) | 135 (static_cast<uint64_t>(buf[1]) << 48) | 136 (static_cast<uint64_t>(buf[2]) << 40) | 137 (static_cast<uint64_t>(buf[3]) << 32) | 138 (static_cast<uint64_t>(buf[4]) << 24) | 139 (static_cast<uint64_t>(buf[5]) << 16) | 140 (static_cast<uint64_t>(buf[6]) << 8) | 141 static_cast<uint64_t>(buf[7]); 142 timeout /= 1000; // Convert nanoseconds to microseconds. 143 144 data->timeout.tv_usec = timeout % 1000000; 145 data->timeout.tv_sec = timeout / 1000000; 146 147 // Send an ACK to the peer. 148 ret = BIO_write(bio->next_bio, &kOpcodeTimeoutAck, 1); 149 if (ret <= 0) { 150 return ret; 151 } 152 assert(ret == 1); 153 154 // Signal to the caller to retry the read, after advancing the clock. 155 BIO_set_retry_read(bio); 156 return -1; 157 } 158 159 if (opcode != kOpcodePacket) { 160 fprintf(stderr, "Unknown opcode, %u\n", opcode); 161 return -1; 162 } 163 164 // Read the length prefix. 165 uint8_t len_bytes[4]; 166 ret = ReadAll(bio->next_bio, len_bytes, sizeof(len_bytes)); 167 if (ret <= 0) { 168 BIO_copy_next_retry(bio); 169 return ret; 170 } 171 172 uint32_t len = (len_bytes[0] << 24) | (len_bytes[1] << 16) | 173 (len_bytes[2] << 8) | len_bytes[3]; 174 uint8_t *buf = (uint8_t *)OPENSSL_malloc(len); 175 if (buf == NULL) { 176 return -1; 177 } 178 ret = ReadAll(bio->next_bio, buf, len); 179 if (ret <= 0) { 180 fprintf(stderr, "Packeted BIO was truncated\n"); 181 return -1; 182 } 183 184 if (outl > (int)len) { 185 outl = len; 186 } 187 OPENSSL_memcpy(out, buf, outl); 188 OPENSSL_free(buf); 189 return outl; 190 } 191 192 static long PacketedCtrl(BIO *bio, int cmd, long num, void *ptr) { 193 if (bio->next_bio == NULL) { 194 return 0; 195 } 196 197 BIO_clear_retry_flags(bio); 198 int ret = BIO_ctrl(bio->next_bio, cmd, num, ptr); 199 BIO_copy_next_retry(bio); 200 return ret; 201 } 202 203 static int PacketedNew(BIO *bio) { 204 bio->init = 1; 205 return 1; 206 } 207 208 static int PacketedFree(BIO *bio) { 209 if (bio == NULL) { 210 return 0; 211 } 212 213 delete GetData(bio); 214 bio->init = 0; 215 return 1; 216 } 217 218 static long PacketedCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) { 219 if (bio->next_bio == NULL) { 220 return 0; 221 } 222 return BIO_callback_ctrl(bio->next_bio, cmd, fp); 223 } 224 225 const BIO_METHOD g_packeted_bio_method = { 226 BIO_TYPE_FILTER, 227 "packeted bio", 228 PacketedWrite, 229 PacketedRead, 230 NULL /* puts */, 231 NULL /* gets */, 232 PacketedCtrl, 233 PacketedNew, 234 PacketedFree, 235 PacketedCallbackCtrl, 236 }; 237 238 } // namespace 239 240 bssl::UniquePtr<BIO> PacketedBioCreate(timeval *clock) { 241 bssl::UniquePtr<BIO> bio(BIO_new(&g_packeted_bio_method)); 242 if (!bio) { 243 return nullptr; 244 } 245 bio->ptr = new PacketedBio(clock); 246 return bio; 247 } 248 249 bool PacketedBioAdvanceClock(BIO *bio) { 250 PacketedBio *data = GetData(bio); 251 if (data == nullptr) { 252 return false; 253 } 254 255 if (!data->HasTimeout()) { 256 return false; 257 } 258 259 data->clock->tv_usec += data->timeout.tv_usec; 260 data->clock->tv_sec += data->clock->tv_usec / 1000000; 261 data->clock->tv_usec %= 1000000; 262 data->clock->tv_sec += data->timeout.tv_sec; 263 OPENSSL_memset(&data->timeout, 0, sizeof(data->timeout)); 264 return true; 265 } 266