A local copy of OpenSSL from GitHub
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

299 lines
7.3 KiB

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