Skip to content

Add support for DTLS fragmentation #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bundlex.exs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule ExDTLS.BundlexProject do
defp natives() do
[
native: [
sources: ["native.c", "dtls.c", "dyn_buff.c"],
sources: ["native.c", "dtls.c", "dyn_buff.c", "bio_frag.c"],
deps: [unifex: :unifex],
os_deps: [openssl: :pkg_config],
libs: ["pthread"],
Expand Down
142 changes: 142 additions & 0 deletions c_src/ex_dtls/bio_frag.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@

#include "bio_frag.h"
#include <openssl/bio.h>

static int bwrite(BIO *bio, const char *buf, int len);
static int bread(BIO *bio, char *buf, int len);
static long ctrl(BIO *bio, int cmd, long arg1, void *arg2);
static int create(BIO *bio);
static int destroy(BIO *bio);
static long callback_ctrl(BIO *bio, int cmd, BIO_info_cb *fp);

static BIO_METHOD *bio_methods = NULL;

#define MAX_FRAGS 100
#define MTU 1200

struct Ctx {
int frag_sizes[MAX_FRAGS];
int witer;
int riter;
};

const BIO_METHOD *BIO_f_frag(void) {
bio_methods = BIO_meth_new(BIO_TYPE_FILTER, "DTLS fragmentation for mem BIO");

BIO_meth_set_read(bio_methods, bread);
BIO_meth_set_write(bio_methods, bwrite);
BIO_meth_set_ctrl(bio_methods, ctrl);
BIO_meth_set_create(bio_methods, create);
BIO_meth_set_destroy(bio_methods, destroy);
BIO_meth_set_callback_ctrl(bio_methods, callback_ctrl);

return bio_methods;
}

static int create(BIO *bio) {
struct Ctx *ctx = calloc(1, sizeof(struct Ctx));
for (int i = 0; i < MAX_FRAGS; i++) {
ctx->frag_sizes[i] = 0;
}
ctx->witer = 0;
ctx->riter = 0;

BIO_set_data(bio, ctx);
BIO_set_init(bio, 1);
return 1;
}

static int destroy(BIO *bio) {
if (bio == NULL) {
return 0;
}

struct Ctx *ctx = BIO_get_data(bio);
free(ctx);
BIO_set_data(bio, NULL);
BIO_set_init(bio, 0);
return 1;
}

static int bread(BIO *bio, char *buf, int len) {
BIO *next = BIO_next(bio);
if (next == NULL) {
return 0;
}

struct Ctx *ctx = BIO_get_data(bio);

if (len != ctx->frag_sizes[ctx->riter]) {
return 0;
}

int ret = BIO_read(next, buf, len);

if (ret > 0) {
if (ret == ctx->frag_sizes[ctx->riter]) {
ctx->frag_sizes[ctx->riter] = 0;
ctx->riter++;

if (ctx->riter == ctx->witer && ctx->frag_sizes[ctx->riter] == 0) {
// reset iterators
ctx->riter = 0;
ctx->witer = 0;
}

} else if (ret < ctx->frag_sizes[ctx->riter]) {
ctx->frag_sizes[ctx->riter] -= ret;
} else {
// This should never happen
return 0;
}
};

return ret;
}

static int bwrite(BIO *bio, const char *buf, int len) {
BIO *next = BIO_next(bio);
if (next == NULL) {
return 0;
}

struct Ctx *ctx = BIO_get_data(bio);

if (ctx->witer >= MAX_FRAGS) {
return 0;
}

int ret = BIO_write(next, buf, len);
if (ret > 0) {
ctx->frag_sizes[ctx->witer] = ret;
ctx->witer++;
}

return ret;
}

static long ctrl(BIO *bio, int cmd, long num, void *ptr) {
BIO *next = BIO_next(bio);
if (next == NULL) {
return 0;
}

struct Ctx *ctx = BIO_get_data(bio);

if (cmd == BIO_CTRL_PENDING) {
return ctx->frag_sizes[ctx->riter];
} else if (cmd == BIO_CTRL_DGRAM_QUERY_MTU) {
return MTU;
}

return BIO_ctrl(next, cmd, num, ptr);
}

static long callback_ctrl(BIO *bio, int cmd, BIO_info_cb *fp) {
BIO *next = BIO_next(bio);
if (next == NULL) {
return 0;
}

return BIO_callback_ctrl(next, cmd, fp);
}
7 changes: 7 additions & 0 deletions c_src/ex_dtls/bio_frag.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include <openssl/opensslv.h>
#include <openssl/err.h>
#include <openssl/ssl.h>

#include "log.h"

const BIO_METHOD *BIO_f_frag(void);
38 changes: 31 additions & 7 deletions c_src/ex_dtls/dtls.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "dtls.h"

#include "bio_frag.h"

SSL_CTX *create_ctx(int dtls_srtp) {
SSL_CTX *ssl_ctx = SSL_CTX_new(DTLS_method());
if (ssl_ctx == NULL) {
Expand Down Expand Up @@ -33,19 +35,41 @@ SSL *create_ssl(SSL_CTX *ssl_ctx, int mode) {
return NULL;
}

BIO *rbio = BIO_new(BIO_s_mem());
if (rbio == NULL) {
DEBUG("Cannot create rbio");
BIO *frag_bio = BIO_new(BIO_f_frag());
if (frag_bio == NULL) {
DEBUG("Cannot create frag bio");
return NULL;
}

BIO *wbio = BIO_new(BIO_s_mem());
if (wbio == NULL) {
DEBUG("Cannot create wbio");
BIO *wmem_bio = BIO_new(BIO_s_mem());
if (wmem_bio == NULL) {
DEBUG("Cannot create write mem bio");
return NULL;
}

SSL_set_bio(ssl, rbio, wbio);
BIO *wchain = BIO_push(frag_bio, wmem_bio);

BIO *rmem_bio = BIO_new(BIO_s_mem());
if (rmem_bio == NULL) {
DEBUG("Cannot create read mem bio");
return NULL;
}

// #TODO Move to the BIO_s_dgram_mem once we require OpenSSL 3
// BIO *rbio = BIO_new(BIO_s_dgram_mem());
// if (rbio == NULL) {
// DEBUG("Cannot create read dgram mem bio");
// return NULL;
// }

// BIO *wbio = BIO_new(BIO_s_dgram_mem());
// if (wbio == NULL) {
// DEBUG("Cannot create write dgram mem bio");
// return NULL;
// }

SSL_set_bio(ssl, rmem_bio, wchain);
// SSL_set_bio(ssl, rbio, wbio);

return ssl;
}
Expand Down
Loading
Loading