• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2014 The BoringSSL Authors
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 <inttypes.h>
19 #include <limits.h>
20 #include <stdio.h>
21 #include <string.h>
22 
23 #include <functional>
24 #include <utility>
25 #include <vector>
26 
27 #include <openssl/mem.h>
28 
29 #include "../../crypto/internal.h"
30 
31 
32 namespace {
33 
34 extern const BIO_METHOD g_packeted_bio_method;
35 
36 constexpr uint8_t kOpcodePacket = 'P';
37 constexpr uint8_t kOpcodeTimeout = 'T';
38 constexpr uint8_t kOpcodeTimeoutAck = 't';
39 constexpr uint8_t kOpcodeMTU = 'M';
40 constexpr uint8_t kOpcodeExpectNextTimeout = 'E';
41 
42 struct PacketedBio {
PacketedBio__anon982380be0111::PacketedBio43   PacketedBio(timeval *clock_arg,
44               std::function<bool(timeval *)> get_timeout_arg,
45               std::function<bool(uint32_t)> set_mtu_arg)
46       : clock(clock_arg),
47         get_timeout(std::move(get_timeout_arg)),
48         set_mtu(std::move(set_mtu_arg)) {
49     OPENSSL_memset(&timeout, 0, sizeof(timeout));
50   }
51 
HasTimeout__anon982380be0111::PacketedBio52   bool HasTimeout() const {
53     return timeout.tv_sec != 0 || timeout.tv_usec != 0;
54   }
55 
56   timeval timeout;
57   timeval *clock;
58   std::function<bool(timeval *)> get_timeout;
59   std::function<bool(uint32_t)> set_mtu;
60 };
61 
GetData(BIO * bio)62 PacketedBio *GetData(BIO *bio) {
63   if (bio->method != &g_packeted_bio_method) {
64     return NULL;
65   }
66   return (PacketedBio *)bio->ptr;
67 }
68 
69 // ReadAll reads |len| bytes from |bio| into |out|. It returns 1 on success and
70 // 0 or -1 on error.
ReadAll(BIO * bio,uint8_t * out,size_t len)71 static int ReadAll(BIO *bio, uint8_t *out, size_t len) {
72   while (len > 0) {
73     int chunk_len = INT_MAX;
74     if (len <= INT_MAX) {
75       chunk_len = (int)len;
76     }
77     int ret = BIO_read(bio, out, chunk_len);
78     if (ret <= 0) {
79       return ret;
80     }
81     out += ret;
82     len -= ret;
83   }
84   return 1;
85 }
86 
PacketedWrite(BIO * bio,const char * in,int inl)87 static int PacketedWrite(BIO *bio, const char *in, int inl) {
88   if (bio->next_bio == NULL) {
89     return 0;
90   }
91 
92   BIO_clear_retry_flags(bio);
93 
94   // Write the header.
95   uint8_t header[5];
96   header[0] = kOpcodePacket;
97   header[1] = (inl >> 24) & 0xff;
98   header[2] = (inl >> 16) & 0xff;
99   header[3] = (inl >> 8) & 0xff;
100   header[4] = inl & 0xff;
101   int ret = BIO_write(bio->next_bio, header, sizeof(header));
102   if (ret <= 0) {
103     BIO_copy_next_retry(bio);
104     return ret;
105   }
106 
107   // Write the buffer.
108   ret = BIO_write(bio->next_bio, in, inl);
109   if (ret < 0 || (inl > 0 && ret == 0)) {
110     BIO_copy_next_retry(bio);
111     return ret;
112   }
113   assert(ret == inl);
114   return ret;
115 }
116 
PacketedRead(BIO * bio,char * out,int outl)117 static int PacketedRead(BIO *bio, char *out, int outl) {
118   PacketedBio *data = GetData(bio);
119   if (bio->next_bio == NULL) {
120     return 0;
121   }
122 
123   BIO_clear_retry_flags(bio);
124 
125   for (;;) {
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 = CRYPTO_load_u64_be(buf);
150       timeout /= 1000;  // Convert nanoseconds to microseconds.
151 
152       data->timeout.tv_usec = timeout % 1000000;
153       data->timeout.tv_sec = timeout / 1000000;
154 
155       // Send an ACK to the peer.
156       ret = BIO_write(bio->next_bio, &kOpcodeTimeoutAck, 1);
157       if (ret <= 0) {
158         return ret;
159       }
160       assert(ret == 1);
161 
162       // Signal to the caller to retry the read, after advancing the clock.
163       BIO_set_retry_read(bio);
164       return -1;
165     }
166 
167     if (opcode == kOpcodeMTU) {
168       uint8_t buf[4];
169       ret = ReadAll(bio->next_bio, buf, sizeof(buf));
170       if (ret <= 0) {
171         BIO_copy_next_retry(bio);
172         return ret;
173       }
174       uint32_t mtu = CRYPTO_load_u32_be(buf);
175       if (!data->set_mtu(mtu)) {
176         fprintf(stderr, "Error setting MTU\n");
177         return -1;
178       }
179       // Continue reading.
180       continue;
181     }
182 
183     if (opcode == kOpcodeExpectNextTimeout) {
184       uint8_t buf[8];
185       ret = ReadAll(bio->next_bio, buf, sizeof(buf));
186       if (ret <= 0) {
187         BIO_copy_next_retry(bio);
188         return ret;
189       }
190       uint64_t expected = CRYPTO_load_u64_be(buf);
191       timeval timeout;
192       bool has_timeout = data->get_timeout(&timeout);
193       if (expected == UINT64_MAX) {
194         if (has_timeout) {
195           fprintf(stderr,
196                   "Expected no timeout, but got %" PRIu64 ".%06" PRIu64 "s.\n",
197                   static_cast<uint64_t>(timeout.tv_sec),
198                   static_cast<uint64_t>(timeout.tv_usec));
199           return -1;
200         }
201       } else {
202         expected /= 1000;  // Convert nanoseconds to microseconds.
203         uint64_t expected_sec = expected / 1000000;
204         uint64_t expected_usec = expected % 1000000;
205         if (!has_timeout) {
206           fprintf(stderr,
207                   "Expected timeout of %" PRIu64 ".%06" PRIu64
208                   "s, but got none.\n",
209                   expected_sec, expected_usec);
210           return -1;
211         }
212         if (static_cast<uint64_t>(timeout.tv_sec) != expected_sec ||
213             static_cast<uint64_t>(timeout.tv_usec) != expected_usec) {
214           fprintf(stderr,
215                   "Expected timeout of %" PRIu64 ".%06" PRIu64
216                   "s, but got %" PRIu64 ".%06" PRIu64 "s.\n",
217                   expected_sec, expected_usec,
218                   static_cast<uint64_t>(timeout.tv_sec),
219                   static_cast<uint64_t>(timeout.tv_usec));
220           return -1;
221         }
222       }
223       // Continue reading.
224       continue;
225     }
226 
227     if (opcode != kOpcodePacket) {
228       fprintf(stderr, "Unknown opcode, %u\n", opcode);
229       return -1;
230     }
231 
232     // Read the length prefix.
233     uint8_t len_bytes[4];
234     ret = ReadAll(bio->next_bio, len_bytes, sizeof(len_bytes));
235     if (ret <= 0) {
236       BIO_copy_next_retry(bio);
237       return ret;
238     }
239 
240     std::vector<uint8_t> buf(CRYPTO_load_u32_be(len_bytes), 0);
241     ret = ReadAll(bio->next_bio, buf.data(), buf.size());
242     if (ret <= 0) {
243       fprintf(stderr, "Packeted BIO was truncated\n");
244       return -1;
245     }
246 
247     if (static_cast<size_t>(outl) > buf.size()) {
248       outl = static_cast<int>(buf.size());
249     }
250     OPENSSL_memcpy(out, buf.data(), outl);
251     return outl;
252   }
253 }
254 
PacketedCtrl(BIO * bio,int cmd,long num,void * ptr)255 static long PacketedCtrl(BIO *bio, int cmd, long num, void *ptr) {
256   if (bio->next_bio == NULL) {
257     return 0;
258   }
259 
260   BIO_clear_retry_flags(bio);
261   long ret = BIO_ctrl(bio->next_bio, cmd, num, ptr);
262   BIO_copy_next_retry(bio);
263   return ret;
264 }
265 
PacketedNew(BIO * bio)266 static int PacketedNew(BIO *bio) {
267   bio->init = 1;
268   return 1;
269 }
270 
PacketedFree(BIO * bio)271 static int PacketedFree(BIO *bio) {
272   if (bio == NULL) {
273     return 0;
274   }
275 
276   delete GetData(bio);
277   bio->init = 0;
278   return 1;
279 }
280 
PacketedCallbackCtrl(BIO * bio,int cmd,bio_info_cb fp)281 static long PacketedCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) {
282   if (bio->next_bio == NULL) {
283     return 0;
284   }
285   return BIO_callback_ctrl(bio->next_bio, cmd, fp);
286 }
287 
288 const BIO_METHOD g_packeted_bio_method = {
289   BIO_TYPE_FILTER,
290   "packeted bio",
291   PacketedWrite,
292   PacketedRead,
293   NULL /* puts */,
294   NULL /* gets */,
295   PacketedCtrl,
296   PacketedNew,
297   PacketedFree,
298   PacketedCallbackCtrl,
299 };
300 
301 }  // namespace
302 
PacketedBioCreate(timeval * clock,std::function<bool (timeval *)> get_timeout,std::function<bool (uint32_t)> set_mtu)303 bssl::UniquePtr<BIO> PacketedBioCreate(
304     timeval *clock, std::function<bool(timeval *)> get_timeout,
305     std::function<bool(uint32_t)> set_mtu) {
306   bssl::UniquePtr<BIO> bio(BIO_new(&g_packeted_bio_method));
307   if (!bio) {
308     return nullptr;
309   }
310   bio->ptr = new PacketedBio(clock, std::move(get_timeout), std::move(set_mtu));
311   return bio;
312 }
313 
PacketedBioAdvanceClock(BIO * bio)314 bool PacketedBioAdvanceClock(BIO *bio) {
315   PacketedBio *data = GetData(bio);
316   if (data == nullptr) {
317     return false;
318   }
319 
320   if (!data->HasTimeout()) {
321     return false;
322   }
323 
324   data->clock->tv_usec += data->timeout.tv_usec;
325   data->clock->tv_sec += data->clock->tv_usec / 1000000;
326   data->clock->tv_usec %= 1000000;
327   data->clock->tv_sec += data->timeout.tv_sec;
328   OPENSSL_memset(&data->timeout, 0, sizeof(data->timeout));
329   return true;
330 }
331 
PacketedBioGetClock(BIO * bio)332 timeval *PacketedBioGetClock(BIO *bio) {
333   PacketedBio *data = GetData(bio);
334   if (data == nullptr) {
335     return nullptr;
336   }
337   return data->clock;
338 }
339