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 25 namespace { 26 27 extern const BIO_METHOD g_packeted_bio_method; 28 29 const uint8_t kOpcodePacket = 'P'; 30 const uint8_t kOpcodeTimeout = 'T'; 31 const uint8_t kOpcodeTimeoutAck = 't'; 32 33 // ReadAll reads |len| bytes from |bio| into |out|. It returns 1 on success and 34 // 0 or -1 on error. 35 static int ReadAll(BIO *bio, uint8_t *out, size_t len) { 36 while (len > 0) { 37 int chunk_len = INT_MAX; 38 if (len <= INT_MAX) { 39 chunk_len = (int)len; 40 } 41 int ret = BIO_read(bio, out, chunk_len); 42 if (ret <= 0) { 43 return ret; 44 } 45 out += ret; 46 len -= ret; 47 } 48 return 1; 49 } 50 51 static int PacketedWrite(BIO *bio, const char *in, int inl) { 52 if (bio->next_bio == NULL) { 53 return 0; 54 } 55 56 BIO_clear_retry_flags(bio); 57 58 // Write the header. 59 uint8_t header[5]; 60 header[0] = kOpcodePacket; 61 header[1] = (inl >> 24) & 0xff; 62 header[2] = (inl >> 16) & 0xff; 63 header[3] = (inl >> 8) & 0xff; 64 header[4] = inl & 0xff; 65 int ret = BIO_write(bio->next_bio, header, sizeof(header)); 66 if (ret <= 0) { 67 BIO_copy_next_retry(bio); 68 return ret; 69 } 70 71 // Write the buffer. 72 ret = BIO_write(bio->next_bio, in, inl); 73 if (ret < 0 || (inl > 0 && ret == 0)) { 74 BIO_copy_next_retry(bio); 75 return ret; 76 } 77 assert(ret == inl); 78 return ret; 79 } 80 81 static int PacketedRead(BIO *bio, char *out, int outl) { 82 if (bio->next_bio == NULL) { 83 return 0; 84 } 85 86 BIO_clear_retry_flags(bio); 87 88 // Read the opcode. 89 uint8_t opcode; 90 int ret = ReadAll(bio->next_bio, &opcode, sizeof(opcode)); 91 if (ret <= 0) { 92 BIO_copy_next_retry(bio); 93 return ret; 94 } 95 96 if (opcode == kOpcodeTimeout) { 97 // Process the timeout. 98 uint8_t buf[8]; 99 ret = ReadAll(bio->next_bio, buf, sizeof(buf)); 100 if (ret <= 0) { 101 BIO_copy_next_retry(bio); 102 return ret; 103 } 104 uint64_t timeout = (static_cast<uint64_t>(buf[0]) << 56) | 105 (static_cast<uint64_t>(buf[1]) << 48) | 106 (static_cast<uint64_t>(buf[2]) << 40) | 107 (static_cast<uint64_t>(buf[3]) << 32) | 108 (static_cast<uint64_t>(buf[4]) << 24) | 109 (static_cast<uint64_t>(buf[5]) << 16) | 110 (static_cast<uint64_t>(buf[6]) << 8) | 111 static_cast<uint64_t>(buf[7]); 112 timeout /= 1000; // Convert nanoseconds to microseconds. 113 timeval *out_timeout = reinterpret_cast<timeval *>(bio->ptr); 114 assert(out_timeout->tv_usec == 0); 115 assert(out_timeout->tv_sec == 0); 116 out_timeout->tv_usec = timeout % 1000000; 117 out_timeout->tv_sec = timeout / 1000000; 118 119 // Send an ACK to the peer. 120 ret = BIO_write(bio->next_bio, &kOpcodeTimeoutAck, 1); 121 if (ret <= 0) { 122 return ret; 123 } 124 assert(ret == 1); 125 126 // Signal to the caller to retry the read, after processing the 127 // new clock. 128 BIO_set_retry_read(bio); 129 return -1; 130 } 131 132 if (opcode != kOpcodePacket) { 133 fprintf(stderr, "Unknown opcode, %u\n", opcode); 134 return -1; 135 } 136 137 // Read the length prefix. 138 uint8_t len_bytes[4]; 139 ret = ReadAll(bio->next_bio, len_bytes, sizeof(len_bytes)); 140 if (ret <= 0) { 141 BIO_copy_next_retry(bio); 142 return ret; 143 } 144 145 uint32_t len = (len_bytes[0] << 24) | (len_bytes[1] << 16) | 146 (len_bytes[2] << 8) | len_bytes[3]; 147 uint8_t *buf = (uint8_t *)OPENSSL_malloc(len); 148 if (buf == NULL) { 149 return -1; 150 } 151 ret = ReadAll(bio->next_bio, buf, len); 152 if (ret <= 0) { 153 fprintf(stderr, "Packeted BIO was truncated\n"); 154 return -1; 155 } 156 157 if (outl > (int)len) { 158 outl = len; 159 } 160 memcpy(out, buf, outl); 161 OPENSSL_free(buf); 162 return outl; 163 } 164 165 static long PacketedCtrl(BIO *bio, int cmd, long num, void *ptr) { 166 if (bio->next_bio == NULL) { 167 return 0; 168 } 169 BIO_clear_retry_flags(bio); 170 int ret = BIO_ctrl(bio->next_bio, cmd, num, ptr); 171 BIO_copy_next_retry(bio); 172 return ret; 173 } 174 175 static int PacketedNew(BIO *bio) { 176 bio->init = 1; 177 return 1; 178 } 179 180 static int PacketedFree(BIO *bio) { 181 if (bio == NULL) { 182 return 0; 183 } 184 185 bio->init = 0; 186 return 1; 187 } 188 189 static long PacketedCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) { 190 if (bio->next_bio == NULL) { 191 return 0; 192 } 193 return BIO_callback_ctrl(bio->next_bio, cmd, fp); 194 } 195 196 const BIO_METHOD g_packeted_bio_method = { 197 BIO_TYPE_FILTER, 198 "packeted bio", 199 PacketedWrite, 200 PacketedRead, 201 NULL /* puts */, 202 NULL /* gets */, 203 PacketedCtrl, 204 PacketedNew, 205 PacketedFree, 206 PacketedCallbackCtrl, 207 }; 208 209 } // namespace 210 211 ScopedBIO PacketedBioCreate(timeval *out_timeout) { 212 ScopedBIO bio(BIO_new(&g_packeted_bio_method)); 213 if (!bio) { 214 return nullptr; 215 } 216 bio->ptr = out_timeout; 217 return bio; 218 } 219