824c4275c5add447a97962c89b94bf16e9eda59d
[openssl.git] / test / ossl_shim / packeted_bio.cc
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/crypto.h>
23
24
25 namespace {
26
27 const uint8_t kOpcodePacket = 'P';
28 const uint8_t kOpcodeTimeout = 'T';
29
30 // ReadAll reads |len| bytes from |bio| into |out|. It returns 1 on success and
31 // 0 or -1 on error.
32 static int ReadAll(BIO *bio, uint8_t *out, size_t len) {
33   while (len > 0) {
34     int chunk_len = INT_MAX;
35     if (len <= INT_MAX) {
36       chunk_len = (int)len;
37     }
38     int ret = BIO_read(bio, out, chunk_len);
39     if (ret <= 0) {
40       return ret;
41     }
42     out += ret;
43     len -= ret;
44   }
45   return 1;
46 }
47
48 static int PacketedWrite(BIO *bio, const char *in, int inl) {
49   if (BIO_next(bio) == NULL) {
50     return 0;
51   }
52
53   BIO_clear_retry_flags(bio);
54
55   // Write the header.
56   uint8_t header[5];
57   header[0] = kOpcodePacket;
58   header[1] = (inl >> 24) & 0xff;
59   header[2] = (inl >> 16) & 0xff;
60   header[3] = (inl >> 8) & 0xff;
61   header[4] = inl & 0xff;
62   int ret = BIO_write(BIO_next(bio), header, sizeof(header));
63   if (ret <= 0) {
64     BIO_copy_next_retry(bio);
65     return ret;
66   }
67
68   // Write the buffer.
69   ret = BIO_write(BIO_next(bio), in, inl);
70   if (ret < 0 || (inl > 0 && ret == 0)) {
71     BIO_copy_next_retry(bio);
72     return ret;
73   }
74   assert(ret == inl);
75   return ret;
76 }
77
78 static int PacketedRead(BIO *bio, char *out, int outl) {
79   if (BIO_next(bio) == NULL) {
80     return 0;
81   }
82
83   BIO_clear_retry_flags(bio);
84
85   // Read the opcode.
86   uint8_t opcode;
87   int ret = ReadAll(BIO_next(bio), &opcode, sizeof(opcode));
88   if (ret <= 0) {
89     BIO_copy_next_retry(bio);
90     return ret;
91   }
92
93   if (opcode == kOpcodeTimeout) {
94     fprintf(stderr, "Timeout simulation not supported.\n");
95     return -1;
96   }
97
98   if (opcode != kOpcodePacket) {
99     fprintf(stderr, "Unknown opcode, %u\n", opcode);
100     return -1;
101   }
102
103   // Read the length prefix.
104   uint8_t len_bytes[4];
105   ret = ReadAll(BIO_next(bio), len_bytes, sizeof(len_bytes));
106   if (ret <= 0) {
107     BIO_copy_next_retry(bio);
108     return ret;
109   }
110
111   uint32_t len = (len_bytes[0] << 24) | (len_bytes[1] << 16) |
112       (len_bytes[2] << 8) | len_bytes[3];
113   uint8_t *buf = (uint8_t *)OPENSSL_malloc(len);
114   if (buf == NULL) {
115     return -1;
116   }
117   ret = ReadAll(BIO_next(bio), buf, len);
118   if (ret <= 0) {
119     fprintf(stderr, "Packeted BIO was truncated\n");
120     return -1;
121   }
122
123   if (outl > (int)len) {
124     outl = len;
125   }
126   memcpy(out, buf, outl);
127   OPENSSL_free(buf);
128   return outl;
129 }
130
131 static long PacketedCtrl(BIO *bio, int cmd, long num, void *ptr) {
132   if (BIO_next(bio) == NULL) {
133     return 0;
134   }
135   BIO_clear_retry_flags(bio);
136   int ret = BIO_ctrl(BIO_next(bio), cmd, num, ptr);
137   BIO_copy_next_retry(bio);
138   return ret;
139 }
140
141 static int PacketedNew(BIO *bio) {
142   BIO_set_init(bio, 1);
143   return 1;
144 }
145
146 static int PacketedFree(BIO *bio) {
147   if (bio == NULL) {
148     return 0;
149   }
150
151   BIO_set_init(bio, 0);
152   return 1;
153 }
154
155 static long PacketedCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) {
156   if (BIO_next(bio) == NULL) {
157     return 0;
158   }
159   return BIO_callback_ctrl(BIO_next(bio), cmd, fp);
160 }
161
162 static BIO_METHOD *g_packeted_bio_method = NULL;
163
164 static const BIO_METHOD *PacketedMethod(void)
165 {
166   if (g_packeted_bio_method == NULL) {
167     g_packeted_bio_method = BIO_meth_new(BIO_TYPE_FILTER, "packeted bio");
168     if (   g_packeted_bio_method == NULL
169         || !BIO_meth_set_write(g_packeted_bio_method, PacketedWrite)
170         || !BIO_meth_set_read(g_packeted_bio_method, PacketedRead)
171         || !BIO_meth_set_ctrl(g_packeted_bio_method, PacketedCtrl)
172         || !BIO_meth_set_create(g_packeted_bio_method, PacketedNew)
173         || !BIO_meth_set_destroy(g_packeted_bio_method, PacketedFree)
174         || !BIO_meth_set_callback_ctrl(g_packeted_bio_method,
175                                        PacketedCallbackCtrl))
176     return NULL;
177   }
178   return g_packeted_bio_method;
179 }
180 }  // namespace
181
182 ScopedBIO PacketedBioCreate(timeval *out_timeout) {
183   ScopedBIO bio(BIO_new(PacketedMethod()));
184   if (!bio) {
185     return nullptr;
186   }
187   BIO_set_data(bio.get(), out_timeout);
188   return bio;
189 }