1d9e397b599b13d642138480a28c14db7a136bf0Adam Langley/* Copyright (c) 2014, Google Inc.
2d9e397b599b13d642138480a28c14db7a136bf0Adam Langley *
3d9e397b599b13d642138480a28c14db7a136bf0Adam Langley * Permission to use, copy, modify, and/or distribute this software for any
4d9e397b599b13d642138480a28c14db7a136bf0Adam Langley * purpose with or without fee is hereby granted, provided that the above
5d9e397b599b13d642138480a28c14db7a136bf0Adam Langley * copyright notice and this permission notice appear in all copies.
6d9e397b599b13d642138480a28c14db7a136bf0Adam Langley *
7d9e397b599b13d642138480a28c14db7a136bf0Adam Langley * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8d9e397b599b13d642138480a28c14db7a136bf0Adam Langley * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9d9e397b599b13d642138480a28c14db7a136bf0Adam Langley * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10d9e397b599b13d642138480a28c14db7a136bf0Adam Langley * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11d9e397b599b13d642138480a28c14db7a136bf0Adam Langley * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12d9e397b599b13d642138480a28c14db7a136bf0Adam Langley * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13d9e397b599b13d642138480a28c14db7a136bf0Adam Langley * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
15d9e397b599b13d642138480a28c14db7a136bf0Adam Langley#include "async_bio.h"
16d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
17d9e397b599b13d642138480a28c14db7a136bf0Adam Langley#include <errno.h>
18d9e397b599b13d642138480a28c14db7a136bf0Adam Langley#include <string.h>
19d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
20d9e397b599b13d642138480a28c14db7a136bf0Adam Langley#include <openssl/mem.h>
21d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
22d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
23d9e397b599b13d642138480a28c14db7a136bf0Adam Langleynamespace {
24d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
25e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langleyextern const BIO_METHOD g_async_bio_method;
26d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
27e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langleystruct AsyncBio {
28d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  bool datagram;
29d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  size_t read_quota;
30d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  size_t write_quota;
31d9e397b599b13d642138480a28c14db7a136bf0Adam Langley};
32d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
33e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam LangleyAsyncBio *GetData(BIO *bio) {
34e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langley  if (bio->method != &g_async_bio_method) {
35d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    return NULL;
36d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  }
37e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langley  return (AsyncBio *)bio->ptr;
38d9e397b599b13d642138480a28c14db7a136bf0Adam Langley}
39d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
40e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langleystatic int AsyncWrite(BIO *bio, const char *in, int inl) {
41e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langley  AsyncBio *a = GetData(bio);
42d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  if (a == NULL || bio->next_bio == NULL) {
43d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    return 0;
44d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  }
45d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
46d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  if (a->datagram) {
47d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    // Perform writes synchronously; the DTLS implementation drops any packets
48d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    // that failed to send.
49d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    return BIO_write(bio->next_bio, in, inl);
50d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  }
51d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
52d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  BIO_clear_retry_flags(bio);
53d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
54d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  if (a->write_quota == 0) {
55d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    BIO_set_retry_write(bio);
56d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    errno = EAGAIN;
57d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    return -1;
58d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  }
59d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
60d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  if (!a->datagram && (size_t)inl > a->write_quota) {
61d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    inl = a->write_quota;
62d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  }
63d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  int ret = BIO_write(bio->next_bio, in, inl);
64d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  if (ret <= 0) {
65d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    BIO_copy_next_retry(bio);
66d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  } else {
67d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    a->write_quota -= (a->datagram ? 1 : ret);
68d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  }
69d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  return ret;
70d9e397b599b13d642138480a28c14db7a136bf0Adam Langley}
71d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
72e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langleystatic int AsyncRead(BIO *bio, char *out, int outl) {
73e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langley  AsyncBio *a = GetData(bio);
74d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  if (a == NULL || bio->next_bio == NULL) {
75d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    return 0;
76d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  }
77d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
78d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  BIO_clear_retry_flags(bio);
79d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
80d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  if (a->read_quota == 0) {
81d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    BIO_set_retry_read(bio);
82d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    errno = EAGAIN;
83d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    return -1;
84d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  }
85d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
86d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  if (!a->datagram && (size_t)outl > a->read_quota) {
87d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    outl = a->read_quota;
88d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  }
89d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  int ret = BIO_read(bio->next_bio, out, outl);
90d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  if (ret <= 0) {
91d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    BIO_copy_next_retry(bio);
92d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  } else {
93d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    a->read_quota -= (a->datagram ? 1 : ret);
94d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  }
95d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  return ret;
96d9e397b599b13d642138480a28c14db7a136bf0Adam Langley}
97d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
98e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langleystatic long AsyncCtrl(BIO *bio, int cmd, long num, void *ptr) {
99d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  if (bio->next_bio == NULL) {
100d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    return 0;
101d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  }
102d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  BIO_clear_retry_flags(bio);
103d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  int ret = BIO_ctrl(bio->next_bio, cmd, num, ptr);
104d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  BIO_copy_next_retry(bio);
105d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  return ret;
106d9e397b599b13d642138480a28c14db7a136bf0Adam Langley}
107d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
108e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langleystatic int AsyncNew(BIO *bio) {
109e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langley  AsyncBio *a = (AsyncBio *)OPENSSL_malloc(sizeof(*a));
110d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  if (a == NULL) {
111d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    return 0;
112d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  }
113d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  memset(a, 0, sizeof(*a));
114d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  bio->init = 1;
115d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  bio->ptr = (char *)a;
116d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  return 1;
117d9e397b599b13d642138480a28c14db7a136bf0Adam Langley}
118d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
119e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langleystatic int AsyncFree(BIO *bio) {
120d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  if (bio == NULL) {
121d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    return 0;
122d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  }
123d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
124d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  OPENSSL_free(bio->ptr);
125d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  bio->ptr = NULL;
126d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  bio->init = 0;
127d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  bio->flags = 0;
128d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  return 1;
129d9e397b599b13d642138480a28c14db7a136bf0Adam Langley}
130d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
131e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langleystatic long AsyncCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) {
132d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  if (bio->next_bio == NULL) {
133d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    return 0;
134d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  }
135d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  return BIO_callback_ctrl(bio->next_bio, cmd, fp);
136d9e397b599b13d642138480a28c14db7a136bf0Adam Langley}
137d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
138e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langleyconst BIO_METHOD g_async_bio_method = {
139d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  BIO_TYPE_FILTER,
140d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  "async bio",
141e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langley  AsyncWrite,
142e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langley  AsyncRead,
143d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  NULL /* puts */,
144d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  NULL /* gets */,
145e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langley  AsyncCtrl,
146e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langley  AsyncNew,
147e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langley  AsyncFree,
148e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langley  AsyncCallbackCtrl,
149d9e397b599b13d642138480a28c14db7a136bf0Adam Langley};
150d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
151d9e397b599b13d642138480a28c14db7a136bf0Adam Langley}  // namespace
152d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
153e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam LangleyScopedBIO AsyncBioCreate() {
154e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langley  return ScopedBIO(BIO_new(&g_async_bio_method));
155d9e397b599b13d642138480a28c14db7a136bf0Adam Langley}
156d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
157e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam LangleyScopedBIO AsyncBioCreateDatagram() {
158e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langley  ScopedBIO ret(BIO_new(&g_async_bio_method));
159d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  if (!ret) {
160e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langley    return nullptr;
161d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  }
162e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langley  GetData(ret.get())->datagram = true;
163d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  return ret;
164d9e397b599b13d642138480a28c14db7a136bf0Adam Langley}
165d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
166e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langleyvoid AsyncBioAllowRead(BIO *bio, size_t count) {
167e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langley  AsyncBio *a = GetData(bio);
168d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  if (a == NULL) {
169d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    return;
170d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  }
171d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  a->read_quota += count;
172d9e397b599b13d642138480a28c14db7a136bf0Adam Langley}
173d9e397b599b13d642138480a28c14db7a136bf0Adam Langley
174e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langleyvoid AsyncBioAllowWrite(BIO *bio, size_t count) {
175e9ada863a7b3e81f5d2b1e3bdd2305da902a87f5Adam Langley  AsyncBio *a = GetData(bio);
176d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  if (a == NULL) {
177d9e397b599b13d642138480a28c14db7a136bf0Adam Langley    return;
178d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  }
179d9e397b599b13d642138480a28c14db7a136bf0Adam Langley  a->write_quota += count;
180d9e397b599b13d642138480a28c14db7a136bf0Adam Langley}
181