Packages
hackney
3.0.0
4.7.2
4.7.1
4.7.0
4.6.1
4.6.0
4.5.2
4.5.1
4.5.0
4.4.5
4.4.3
4.4.2
4.4.1
4.4.0
4.3.0
4.2.3
4.2.2
4.2.1
4.2.0
4.1.0
4.0.3
4.0.2
4.0.1
4.0.0
3.2.1
3.2.0
3.1.2
3.1.1
3.1.0
3.0.3
3.0.2
3.0.1
3.0.0
retired
2.0.1
2.0.0
2.0.0-beta.1
1.25.0
1.24.1
1.24.0
1.23.0
1.22.0
1.21.0
1.20.1
1.20.0
1.19.1
1.19.0
1.18.2
1.18.1
1.18.0
1.17.4
1.17.3
1.17.2
1.17.1
1.17.0
1.16.0
1.15.2
1.15.1
1.15.0
1.14.3
1.14.2
1.14.0
1.13.0
1.12.1
1.12.0
1.11.0
1.10.1
1.10.0
1.9.0
1.8.6
1.8.5
1.8.4
1.8.3
1.8.2
1.8.0
1.7.1
1.7.0
1.6.6
retired
1.6.5
1.6.4
retired
1.6.3
1.6.2
1.6.1
1.6.0
1.5.7
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.10
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.0
1.1.0
1.0.6
1.0.5
1.0.2
1.0.1
0.15.2
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.1
Simple HTTP client with HTTP/1.1, HTTP/2, and HTTP/3 support
Retired package: Release invalid - Use 3.0.1 instead
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
c_src/boringssl/decrepit/bio/base64_bio.cc
// Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <openssl/base64.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
#include "../../crypto/internal.h"
#include "../../crypto/bio/internal.h"
#define B64_BLOCK_SIZE 1024
#define B64_BLOCK_SIZE2 768
#define B64_NONE 0
#define B64_ENCODE 1
#define B64_DECODE 2
#define EVP_ENCODE_LENGTH(l) (((l + 2) / 3 * 4) + (l / 48 + 1) * 2 + 80)
typedef struct b64_struct {
int buf_len;
int buf_off;
int tmp_len; // used to find the start when decoding
int tmp_nl; // If true, scan until '\n'
int encode;
int start; // have we started decoding yet?
int cont; // <= 0 when finished
EVP_ENCODE_CTX base64;
char buf[EVP_ENCODE_LENGTH(B64_BLOCK_SIZE) + 10];
char tmp[B64_BLOCK_SIZE];
} BIO_B64_CTX;
static int b64_new(BIO *bio) {
BIO_B64_CTX *ctx =
reinterpret_cast<BIO_B64_CTX *>(OPENSSL_zalloc(sizeof(*ctx)));
if (ctx == nullptr) {
return 0;
}
ctx->cont = 1;
ctx->start = 1;
bio->init = 1;
bio->ptr = (char *)ctx;
return 1;
}
static int b64_free(BIO *bio) {
if (bio == nullptr) {
return 0;
}
OPENSSL_free(bio->ptr);
bio->ptr = nullptr;
bio->init = 0;
bio->flags = 0;
return 1;
}
static int b64_read(BIO *b, char *out, int outl) {
int ret = 0, i, ii, j, k, x, n, num, ret_code = 0;
BIO_B64_CTX *ctx;
uint8_t *p, *q;
if (out == nullptr) {
return 0;
}
ctx = (BIO_B64_CTX *)b->ptr;
if (ctx == nullptr || b->next_bio == nullptr) {
return 0;
}
BIO_clear_retry_flags(b);
if (ctx->encode != B64_DECODE) {
ctx->encode = B64_DECODE;
ctx->buf_len = 0;
ctx->buf_off = 0;
ctx->tmp_len = 0;
EVP_DecodeInit(&ctx->base64);
}
// First check if there are bytes decoded/encoded
if (ctx->buf_len > 0) {
assert(ctx->buf_len >= ctx->buf_off);
i = ctx->buf_len - ctx->buf_off;
if (i > outl) {
i = outl;
}
assert(ctx->buf_off + i < (int)sizeof(ctx->buf));
OPENSSL_memcpy(out, &ctx->buf[ctx->buf_off], i);
ret = i;
out += i;
outl -= i;
ctx->buf_off += i;
if (ctx->buf_len == ctx->buf_off) {
ctx->buf_len = 0;
ctx->buf_off = 0;
}
}
// At this point, we have room of outl bytes and an empty buffer, so we
// should read in some more.
ret_code = 0;
while (outl > 0) {
if (ctx->cont <= 0) {
break;
}
i = BIO_read(b->next_bio, &(ctx->tmp[ctx->tmp_len]),
B64_BLOCK_SIZE - ctx->tmp_len);
if (i <= 0) {
ret_code = i;
// Should we continue next time we are called?
if (!BIO_should_retry(b->next_bio)) {
ctx->cont = i;
// If buffer empty break
if (ctx->tmp_len == 0) {
break;
} else {
// Fall through and process what we have
i = 0;
}
} else {
// else we retry and add more data to buffer
break;
}
}
i += ctx->tmp_len;
ctx->tmp_len = i;
// We need to scan, a line at a time until we have a valid line if we are
// starting.
if (ctx->start && (BIO_test_flags(b, BIO_FLAGS_BASE64_NO_NL))) {
// ctx->start = 1;
ctx->tmp_len = 0;
} else if (ctx->start) {
q = p = (uint8_t *)ctx->tmp;
num = 0;
for (j = 0; j < i; j++) {
if (*(q++) != '\n') {
continue;
}
// due to a previous very long line, we need to keep on scanning for a
// '\n' before we even start looking for base64 encoded stuff.
if (ctx->tmp_nl) {
p = q;
ctx->tmp_nl = 0;
continue;
}
k = EVP_DecodeUpdate(&(ctx->base64), (uint8_t *)ctx->buf, &num, p,
q - p);
if (k <= 0 && num == 0 && ctx->start) {
EVP_DecodeInit(&ctx->base64);
} else {
if (p != (uint8_t *)&(ctx->tmp[0])) {
i -= (p - (uint8_t *)&(ctx->tmp[0]));
for (x = 0; x < i; x++) {
ctx->tmp[x] = p[x];
}
}
EVP_DecodeInit(&ctx->base64);
ctx->start = 0;
break;
}
p = q;
}
// we fell off the end without starting
if (j == i && num == 0) {
// Is this is one long chunk?, if so, keep on reading until a new
// line.
if (p == (uint8_t *)&(ctx->tmp[0])) {
// Check buffer full
if (i == B64_BLOCK_SIZE) {
ctx->tmp_nl = 1;
ctx->tmp_len = 0;
}
} else if (p != q) { // finished on a '\n'
n = q - p;
for (ii = 0; ii < n; ii++) {
ctx->tmp[ii] = p[ii];
}
ctx->tmp_len = n;
}
// else finished on a '\n'
continue;
} else {
ctx->tmp_len = 0;
}
} else if (i < B64_BLOCK_SIZE && ctx->cont > 0) {
// If buffer isn't full and we can retry then restart to read in more
// data.
continue;
}
if (BIO_test_flags(b, BIO_FLAGS_BASE64_NO_NL)) {
int z, jj;
jj = i & ~3; // process per 4
z = EVP_DecodeBlock((uint8_t *)ctx->buf, (uint8_t *)ctx->tmp, jj);
if (jj > 2) {
if (ctx->tmp[jj - 1] == '=') {
z--;
if (ctx->tmp[jj - 2] == '=') {
z--;
}
}
}
// z is now number of output bytes and jj is the number consumed.
if (jj != i) {
OPENSSL_memmove(ctx->tmp, &ctx->tmp[jj], i - jj);
ctx->tmp_len = i - jj;
}
ctx->buf_len = 0;
if (z > 0) {
ctx->buf_len = z;
}
i = z;
} else {
i = EVP_DecodeUpdate(&(ctx->base64), (uint8_t *)ctx->buf, &ctx->buf_len,
(uint8_t *)ctx->tmp, i);
ctx->tmp_len = 0;
}
ctx->buf_off = 0;
if (i < 0) {
ret_code = 0;
ctx->buf_len = 0;
break;
}
if (ctx->buf_len <= outl) {
i = ctx->buf_len;
} else {
i = outl;
}
OPENSSL_memcpy(out, ctx->buf, i);
ret += i;
ctx->buf_off = i;
if (ctx->buf_off == ctx->buf_len) {
ctx->buf_len = 0;
ctx->buf_off = 0;
}
outl -= i;
out += i;
}
BIO_copy_next_retry(b);
return ret == 0 ? ret_code : ret;
}
static int b64_write(BIO *b, const char *in, int inl) {
int ret = 0, n, i;
BIO_B64_CTX *ctx;
ctx = (BIO_B64_CTX *)b->ptr;
BIO_clear_retry_flags(b);
if (ctx->encode != B64_ENCODE) {
ctx->encode = B64_ENCODE;
ctx->buf_len = 0;
ctx->buf_off = 0;
ctx->tmp_len = 0;
EVP_EncodeInit(&(ctx->base64));
}
assert(ctx->buf_off < (int)sizeof(ctx->buf));
assert(ctx->buf_len <= (int)sizeof(ctx->buf));
assert(ctx->buf_len >= ctx->buf_off);
n = ctx->buf_len - ctx->buf_off;
while (n > 0) {
i = BIO_write(b->next_bio, &(ctx->buf[ctx->buf_off]), n);
if (i <= 0) {
BIO_copy_next_retry(b);
return i;
}
assert(i <= n);
ctx->buf_off += i;
assert(ctx->buf_off <= (int)sizeof(ctx->buf));
assert(ctx->buf_len >= ctx->buf_off);
n -= i;
}
// at this point all pending data has been written.
ctx->buf_off = 0;
ctx->buf_len = 0;
if (in == nullptr || inl <= 0) {
return 0;
}
while (inl > 0) {
n = (inl > B64_BLOCK_SIZE) ? B64_BLOCK_SIZE : inl;
if (BIO_test_flags(b, BIO_FLAGS_BASE64_NO_NL)) {
if (ctx->tmp_len > 0) {
assert(ctx->tmp_len <= 3);
n = 3 - ctx->tmp_len;
// There's a theoretical possibility of this.
if (n > inl) {
n = inl;
}
OPENSSL_memcpy(&(ctx->tmp[ctx->tmp_len]), in, n);
ctx->tmp_len += n;
ret += n;
if (ctx->tmp_len < 3) {
break;
}
ctx->buf_len = EVP_EncodeBlock((uint8_t *)ctx->buf, (uint8_t *)ctx->tmp,
ctx->tmp_len);
assert(ctx->buf_len <= (int)sizeof(ctx->buf));
assert(ctx->buf_len >= ctx->buf_off);
// Since we're now done using the temporary buffer, the length should
// be zeroed.
ctx->tmp_len = 0;
} else {
if (n < 3) {
OPENSSL_memcpy(ctx->tmp, in, n);
ctx->tmp_len = n;
ret += n;
break;
}
n -= n % 3;
ctx->buf_len =
EVP_EncodeBlock((uint8_t *)ctx->buf, (const uint8_t *)in, n);
assert(ctx->buf_len <= (int)sizeof(ctx->buf));
assert(ctx->buf_len >= ctx->buf_off);
ret += n;
}
} else {
EVP_EncodeUpdate(&(ctx->base64), (uint8_t *)ctx->buf, &ctx->buf_len,
(uint8_t *)in, n);
assert(ctx->buf_len <= (int)sizeof(ctx->buf));
assert(ctx->buf_len >= ctx->buf_off);
ret += n;
}
inl -= n;
in += n;
ctx->buf_off = 0;
n = ctx->buf_len;
while (n > 0) {
i = BIO_write(b->next_bio, &(ctx->buf[ctx->buf_off]), n);
if (i <= 0) {
BIO_copy_next_retry(b);
return ret == 0 ? i : ret;
}
assert(i <= n);
n -= i;
ctx->buf_off += i;
assert(ctx->buf_off <= (int)sizeof(ctx->buf));
assert(ctx->buf_len >= ctx->buf_off);
}
ctx->buf_len = 0;
ctx->buf_off = 0;
}
return ret;
}
static long b64_ctrl(BIO *b, int cmd, long num, void *ptr) {
BIO_B64_CTX *ctx;
long ret = 1;
int i;
ctx = (BIO_B64_CTX *)b->ptr;
switch (cmd) {
case BIO_CTRL_RESET:
ctx->cont = 1;
ctx->start = 1;
ctx->encode = B64_NONE;
ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
break;
case BIO_CTRL_EOF: // More to read
if (ctx->cont <= 0) {
ret = 1;
} else {
ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
}
break;
case BIO_CTRL_WPENDING: // More to write in buffer
assert(ctx->buf_len >= ctx->buf_off);
ret = ctx->buf_len - ctx->buf_off;
if ((ret == 0) && (ctx->encode != B64_NONE) &&
(ctx->base64.data_used != 0)) {
ret = 1;
} else if (ret <= 0) {
ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
}
break;
case BIO_CTRL_PENDING: // More to read in buffer
assert(ctx->buf_len >= ctx->buf_off);
ret = ctx->buf_len - ctx->buf_off;
if (ret <= 0) {
ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
}
break;
case BIO_CTRL_FLUSH:
// do a final write
again:
while (ctx->buf_len != ctx->buf_off) {
i = b64_write(b, nullptr, 0);
if (i < 0) {
return i;
}
}
if (BIO_test_flags(b, BIO_FLAGS_BASE64_NO_NL)) {
if (ctx->tmp_len != 0) {
ctx->buf_len = EVP_EncodeBlock((uint8_t *)ctx->buf,
(uint8_t *)ctx->tmp, ctx->tmp_len);
ctx->buf_off = 0;
ctx->tmp_len = 0;
goto again;
}
} else if (ctx->encode != B64_NONE && ctx->base64.data_used != 0) {
ctx->buf_off = 0;
EVP_EncodeFinal(&(ctx->base64), (uint8_t *)ctx->buf, &(ctx->buf_len));
// push out the bytes
goto again;
}
// Finally flush the underlying BIO
ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
break;
case BIO_C_DO_STATE_MACHINE:
BIO_clear_retry_flags(b);
ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
BIO_copy_next_retry(b);
break;
case BIO_CTRL_INFO:
case BIO_CTRL_GET:
case BIO_CTRL_SET:
default:
ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
break;
}
return ret;
}
static long b64_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp) {
if (b->next_bio == nullptr) {
return 0;
}
return BIO_callback_ctrl(b->next_bio, cmd, fp);
}
static const BIO_METHOD b64_method = {
BIO_TYPE_BASE64, "base64 encoding", b64_write, b64_read,
/*bgets=*/nullptr, b64_ctrl, b64_new, b64_free,
b64_callback_ctrl,
};
const BIO_METHOD *BIO_f_base64(void) { return &b64_method; }