mirror of
https://github.com/openziti/ziti-sdk-c.git
synced 2026-09-11 01:05:38 +00:00
319 lines
7.6 KiB
C
319 lines
7.6 KiB
C
// Copyright (c) 2023. NetFoundry Inc.
|
|
//
|
|
// 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 <stdint.h>
|
|
#include <stdlib.h>
|
|
#include "utils.h"
|
|
|
|
#if _WIN32
|
|
#include <crtdefs.h>
|
|
#else
|
|
#include <sys/param.h>
|
|
#endif
|
|
#include <tlsuv/queue.h>
|
|
#include <stdbool.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "buffer.h"
|
|
|
|
|
|
/** incoming data chunk */
|
|
typedef struct chunk_s {
|
|
uint8_t *buf;
|
|
size_t len;
|
|
|
|
STAILQ_ENTRY(chunk_s) next;
|
|
} chunk_t;
|
|
|
|
struct buffer_s {
|
|
STAILQ_HEAD(incoming, chunk_s) chunks;
|
|
size_t head_offset;
|
|
size_t available;
|
|
};
|
|
|
|
|
|
buffer *new_buffer() {
|
|
buffer *b = malloc(sizeof(buffer));
|
|
b->head_offset = 0;
|
|
b->available = 0;
|
|
STAILQ_INIT(&b->chunks);
|
|
|
|
return b;
|
|
}
|
|
|
|
void free_buffer(buffer* b) {
|
|
if (b == NULL) return;
|
|
while (!STAILQ_EMPTY(&b->chunks)) {
|
|
chunk_t *chunk = STAILQ_FIRST(&b->chunks);
|
|
STAILQ_REMOVE_HEAD(&b->chunks, next);
|
|
free(chunk->buf);
|
|
free(chunk);
|
|
}
|
|
free(b);
|
|
}
|
|
|
|
void buffer_cleanup(buffer *b) {
|
|
|
|
if (STAILQ_EMPTY(&b->chunks)) {
|
|
return;
|
|
}
|
|
|
|
chunk_t *chunk = STAILQ_FIRST(&b->chunks);
|
|
if (chunk->len == b->head_offset) {
|
|
STAILQ_REMOVE_HEAD(&b->chunks, next);
|
|
b->head_offset = 0;
|
|
free(chunk->buf);
|
|
free(chunk);
|
|
}
|
|
}
|
|
|
|
void buffer_push_back(buffer* b, size_t count) {
|
|
b->available += count;
|
|
b->head_offset -= count;
|
|
}
|
|
|
|
ssize_t buffer_get_next(buffer* b, size_t want, uint8_t** ptr) {
|
|
if (STAILQ_EMPTY(&b->chunks)) {
|
|
return -1;
|
|
}
|
|
|
|
chunk_t *chunk = STAILQ_FIRST(&b->chunks);
|
|
if (chunk->len == b->head_offset) {
|
|
STAILQ_REMOVE_HEAD(&b->chunks, next);
|
|
b->head_offset = 0;
|
|
free(chunk->buf);
|
|
free(chunk);
|
|
|
|
if (STAILQ_EMPTY(&b->chunks)) {
|
|
return -1;
|
|
}
|
|
|
|
chunk = STAILQ_FIRST(&b->chunks);
|
|
}
|
|
int len = MIN(chunk->len - b->head_offset, want);
|
|
*ptr = chunk->buf + b->head_offset;
|
|
b->head_offset += len;
|
|
b->available -= len;
|
|
|
|
return len;
|
|
}
|
|
|
|
void buffer_append_copy(buffer *b, const uint8_t *buf, size_t len) {
|
|
uint8_t *copy = calloc(len, sizeof(uint8_t));
|
|
memcpy(copy, buf, len);
|
|
buffer_append(b, copy, len);
|
|
}
|
|
|
|
void buffer_append(buffer* b, uint8_t *buf, size_t len) {
|
|
chunk_t *e = malloc(sizeof(chunk_t));
|
|
e->buf = buf;
|
|
e->len = len;
|
|
b->available += len;
|
|
|
|
STAILQ_INSERT_TAIL(&b->chunks, e, next);
|
|
}
|
|
|
|
size_t buffer_available(buffer *b) {
|
|
return b ? b->available : 0;
|
|
}
|
|
|
|
#define WRITE_BUF_CHUNK_SIZE 1024
|
|
|
|
void string_buf_init(string_buf_t *wb) {
|
|
wb->fixed = false;
|
|
wb->chunk_size = WRITE_BUF_CHUNK_SIZE;
|
|
wb->chunk = malloc(wb->chunk_size);
|
|
wb->buf = new_buffer();
|
|
wb->wp = wb->chunk;
|
|
}
|
|
|
|
void string_buf_init_fixed(string_buf_t *wb, char *outbuf, size_t max) {
|
|
wb->fixed = true;
|
|
wb->chunk = (uint8_t *) outbuf;
|
|
wb->wp = wb->chunk;
|
|
wb->chunk_size = max;
|
|
wb->buf = NULL;
|
|
}
|
|
|
|
size_t string_buf_size(string_buf_t *wb) {
|
|
return buffer_available(wb->buf) + (wb->wp - wb->chunk);
|
|
}
|
|
|
|
int string_buf_append_byte(string_buf_t *wb, char c) {
|
|
if (wb->wp - wb->chunk >= wb->chunk_size) {
|
|
|
|
if (wb->fixed) { return -1; }
|
|
|
|
buffer_append(wb->buf, wb->chunk, wb->wp - wb->chunk);
|
|
wb->chunk = malloc(wb->chunk_size);
|
|
wb->wp = wb->chunk;
|
|
}
|
|
*wb->wp++ = c;
|
|
return 0;
|
|
}
|
|
|
|
int string_buf_appendn(string_buf_t *wb, const char *str, size_t len) {
|
|
const char *s = str;
|
|
|
|
size_t chunk_len;
|
|
size_t copy_len;
|
|
copy:
|
|
chunk_len = wb->chunk + wb->chunk_size - wb->wp;
|
|
copy_len = MIN(chunk_len, len);
|
|
memcpy(wb->wp, s, copy_len);
|
|
len -= copy_len;
|
|
wb->wp += copy_len;
|
|
s += copy_len;
|
|
|
|
if (len > 0) {
|
|
if (wb->fixed) { return -1; }
|
|
|
|
buffer_append(wb->buf, wb->chunk, wb->wp - wb->chunk);
|
|
wb->chunk = malloc(wb->chunk_size);
|
|
wb->wp = wb->chunk;
|
|
goto copy;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int string_buf_append_urlsafe(string_buf_t *wb, const char *str) {
|
|
static const char unsafe[] = " /:\"<>%{}|\\^`&=+#?";
|
|
|
|
if (str == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
const char *p = str;
|
|
int rc = 0;
|
|
while(*p && rc == 0) {
|
|
char c = *p++;
|
|
if (strchr(unsafe, c) == NULL) {
|
|
rc = string_buf_append_byte(wb, c);
|
|
continue;
|
|
}
|
|
|
|
char buf[4];
|
|
snprintf(buf, sizeof(buf), "%%%02X", (int)c);
|
|
rc = string_buf_appendn(wb, buf, 3);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int string_buf_append(string_buf_t *wb, const char *str) {
|
|
const char *s = str;
|
|
|
|
copy:
|
|
while (*s != '\0' && wb->wp < wb->chunk + wb->chunk_size) { *wb->wp++ = *s++; }
|
|
|
|
if (*s != 0) {
|
|
if (wb->fixed) { return -1; }
|
|
|
|
buffer_append(wb->buf, wb->chunk, wb->wp - wb->chunk);
|
|
wb->chunk = malloc(wb->chunk_size);
|
|
wb->wp = wb->chunk;
|
|
goto copy;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
char *string_buf_to_string(string_buf_t *wb, size_t *outlen) {
|
|
size_t bytes_in_buffer = buffer_available(wb->buf);
|
|
char *result = malloc(bytes_in_buffer + (wb->wp - wb->chunk) + 1);
|
|
|
|
size_t copied = 0;
|
|
while (copied < bytes_in_buffer) {
|
|
uint8_t *copyp;
|
|
size_t copy_len = buffer_get_next(wb->buf, bytes_in_buffer, ©p);
|
|
memcpy(result + copied, copyp, copy_len);
|
|
copied += copy_len;
|
|
}
|
|
|
|
memcpy(result + copied, wb->chunk, wb->wp - wb->chunk);
|
|
result[copied + (wb->wp - wb->chunk)] = 0;
|
|
if (outlen) {
|
|
*outlen = copied + (wb->wp - wb->chunk);
|
|
}
|
|
// after copy buffer contents is empty -- reset current chunk
|
|
wb->wp = wb->chunk;
|
|
|
|
return result;
|
|
}
|
|
|
|
void string_buf_free(string_buf_t *wb) {
|
|
wb->wp = NULL;
|
|
if (!wb->fixed) FREE(wb->chunk);
|
|
wb->chunk = NULL;
|
|
free_buffer(wb->buf);
|
|
wb->buf = NULL;
|
|
}
|
|
|
|
string_buf_t *new_string_buf() {
|
|
NEWP(wb, string_buf_t);
|
|
string_buf_init(wb);
|
|
return wb;
|
|
}
|
|
|
|
string_buf_t *new_fixed_string_buf(char *outbuf, size_t max) {
|
|
NEWP(wb, string_buf_t);
|
|
string_buf_init_fixed(wb, outbuf, max);
|
|
return wb;
|
|
}
|
|
|
|
void delete_string_buf(string_buf_t *wb) {
|
|
string_buf_free(wb);
|
|
free(wb);
|
|
}
|
|
|
|
int string_buf_fmt(string_buf_t *wb, FORMAT_STRING(const char *fmt), ...) {
|
|
va_list argp;
|
|
va_start(argp, fmt);
|
|
|
|
size_t avail_in_chunk = wb->chunk + wb->chunk_size - wb->wp;
|
|
int len = vsnprintf((char *) wb->wp, avail_in_chunk, fmt, argp);
|
|
va_end(argp);
|
|
|
|
// fit into current chunk -- nothing else to do
|
|
if (len < avail_in_chunk) {
|
|
wb->wp += len;
|
|
return len;
|
|
}
|
|
|
|
// can't allocate any more memory
|
|
if (wb->fixed) return -1;
|
|
|
|
// current chunk is not empty push into buffer
|
|
if (wb->chunk != wb->wp) {
|
|
buffer_append(wb->buf, wb->chunk, wb->wp - wb->chunk);
|
|
wb->chunk = malloc(wb->chunk_size);
|
|
wb->wp = wb->chunk;
|
|
}
|
|
|
|
va_start(argp, fmt);
|
|
|
|
if (len < wb->chunk_size) {
|
|
len = vsnprintf((char*)wb->wp, wb->chunk_size, fmt, argp);
|
|
wb->wp += len;
|
|
} else {
|
|
// formatted string won't fit into chunk_size -- add directly to the buffer
|
|
char *s = malloc(len + 1);
|
|
len = vsnprintf(s, len + 1, fmt, argp);
|
|
buffer_append(wb->buf, (uint8_t *)s, len);
|
|
}
|
|
va_end(argp);
|
|
return len;
|
|
}
|
|
|