Skip to content
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

Listener reload #253

Merged
merged 4 commits into from
Jan 3, 2024
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
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ clean: distclean


fmt:
rebar3 fmt
@clang-format-12 -i c_src/*
@rebar3 fmt


.PHONY: distclean
Expand Down
107 changes: 71 additions & 36 deletions c_src/quicer_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,9 @@ ServerLoadConfiguration(ErlNifEnv *env,
}

unsigned alpn_buffer_length = 0;
QUIC_BUFFER alpn_buffers[MAX_ALPN] = { 0 };
QUIC_BUFFER *alpn_buffers = NULL;

if (!load_alpn(env, option, &alpn_buffer_length, alpn_buffers))
if (!load_alpn(env, option, &alpn_buffer_length, &alpn_buffers))
{
return ATOM_ALPN;
}
Expand All @@ -240,14 +240,15 @@ ServerLoadConfiguration(ErlNifEnv *env,
// Allocate/initialize the configuration object, with the configured ALPN
// and settings.
//
QUIC_STATUS Status = QUIC_STATUS_SUCCESS;
if (QUIC_FAILED(Status = MsQuic->ConfigurationOpen(Registration,
alpn_buffers,
alpn_buffer_length,
&Settings,
sizeof(Settings),
CredConfig, // Context
Configuration)))
QUIC_STATUS Status = MsQuic->ConfigurationOpen(Registration,
alpn_buffers,
alpn_buffer_length,
&Settings,
sizeof(Settings),
CredConfig, // Context
Configuration);
free_alpn_buffers(alpn_buffers, alpn_buffer_length);
if (QUIC_FAILED(Status))
{
return ATOM_STATUS(Status);
}
Expand Down Expand Up @@ -313,9 +314,9 @@ ClientLoadConfiguration(ErlNifEnv *env,
}

unsigned alpn_buffer_length = 0;
QUIC_BUFFER alpn_buffers[MAX_ALPN];
QUIC_BUFFER *alpn_buffers = NULL;

if (!load_alpn(env, options, &alpn_buffer_length, alpn_buffers))
if (!load_alpn(env, options, &alpn_buffer_length, &alpn_buffers))
{
ret = ATOM_ALPN;
goto done;
Expand All @@ -325,14 +326,15 @@ ClientLoadConfiguration(ErlNifEnv *env,
// Allocate/initialize the configuration object, with the configured ALPN
// and settings.
//
QUIC_STATUS Status = QUIC_STATUS_SUCCESS;
if (QUIC_FAILED(Status = MsQuic->ConfigurationOpen(Registration,
alpn_buffers,
alpn_buffer_length,
&Settings,
sizeof(Settings),
NULL,
Configuration)))
QUIC_STATUS Status = MsQuic->ConfigurationOpen(Registration,
alpn_buffers,
alpn_buffer_length,
&Settings,
sizeof(Settings),
NULL,
Configuration);
free_alpn_buffers(alpn_buffers, alpn_buffer_length);
if (QUIC_FAILED(Status))
{
ret = ATOM_STATUS(Status);
goto done;
Expand All @@ -357,50 +359,83 @@ ClientLoadConfiguration(ErlNifEnv *env,
return ret;
}

/*
** load alpn from eterm options to the alpn_buffers
** @NOTE 1:caller must call free_alpn_buffers after use
*/
bool
load_alpn(ErlNifEnv *env,
const ERL_NIF_TERM *options,
unsigned *alpn_buffer_length,
QUIC_BUFFER alpn_buffers[])
QUIC_BUFFER **alpn_buffers)
{
ERL_NIF_TERM alpn_list;
assert(*alpn_buffers == NULL);
if (!enif_get_map_value(env, *options, ATOM_ALPN, &alpn_list))
{
return false;
}

if (!enif_get_list_length(env, alpn_list, alpn_buffer_length))
if (!enif_get_list_length(env, alpn_list, alpn_buffer_length)
|| alpn_buffer_length == 0)
{
return false;
}

ERL_NIF_TERM head, tail;
*alpn_buffers = malloc((*alpn_buffer_length) * sizeof(QUIC_BUFFER));

if (!enif_get_list_cell(env, alpn_list, &head, &tail))
if (!*alpn_buffers)
{
return false;
}

for (int i = 0; i < (int)(*alpn_buffer_length); i++)
CxPlatZeroMemory(*alpn_buffers, (*alpn_buffer_length) * sizeof(QUIC_BUFFER));

ERL_NIF_TERM list, head, tail;
unsigned i = 0;
list = alpn_list;
while (enif_get_list_cell(env, list, &head, &tail))
{
// @todo check if PATH_MAX is the correct length
char str[PATH_MAX];
if (enif_get_string(env, head, str, PATH_MAX, ERL_NIF_LATIN1) <= 0)
unsigned len = 0;
#if ERL_NIF_MINOR_VERSION > 16
if (!enif_get_string_length(env, head, &len, ERL_NIF_LATIN1))
#else
if (!enif_get_list_length(env, head, &len))
#endif
{
return false;
goto exit;
}
len++; // for '\0'
char *str = malloc(len * sizeof(char));

alpn_buffers[i].Buffer = (uint8_t *)str;
alpn_buffers[i].Length = strlen(str);

if (!enif_get_list_cell(env, tail, &head, &tail)
&& i + 1 < (int)(*alpn_buffer_length))
if (enif_get_string(env, head, str, len, ERL_NIF_LATIN1) <= 0)
{
return false;
free(str);
str = NULL;
goto exit;
}
}

(*alpn_buffers)[i].Buffer = (uint8_t *)str;
(*alpn_buffers)[i].Length = len - 1; // msquic doesn't need '\0'
i++;
list = tail;
}
return true;

exit:
free_alpn_buffers(*alpn_buffers, i);
return false;
}

void
free_alpn_buffers(QUIC_BUFFER *alpn_buffers, unsigned len)
{
for (unsigned i = 0; i < len; i++)
{
free(alpn_buffers[i].Buffer);
}
free(alpn_buffers);
alpn_buffers = NULL;
}

bool
Expand Down
5 changes: 4 additions & 1 deletion c_src/quicer_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ ERL_NIF_TERM ClientLoadConfiguration(ErlNifEnv *env,
bool load_alpn(ErlNifEnv *env,
const ERL_NIF_TERM *option,
unsigned *alpn_buffer_length,
QUIC_BUFFER alpn_buffers[]);
QUIC_BUFFER **alpn_buffers);

void free_alpn_buffers(QUIC_BUFFER *alpn_buffers, unsigned alpn_buffer_length);

bool load_verify(ErlNifEnv *env,
const ERL_NIF_TERM *option,
const bool default_verify);
Expand Down
2 changes: 1 addition & 1 deletion c_src/quicer_connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -1054,7 +1054,7 @@ addr2eterm(ErlNifEnv *env, QUIC_ADDR *addr)
enif_make_int(env, ntohs(ip[5])),
enif_make_int(env, ntohs(ip[6])),
enif_make_int(env, ntohs(ip[7]))),
enif_make_int(env, addr->Ipv6.sin6_port));
enif_make_int(env, ntohs(addr->Ipv6.sin6_port)));
}
else
{
Expand Down
91 changes: 76 additions & 15 deletions c_src/quicer_listener.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ listen2(ErlNifEnv *env, __unused_parm__ int argc, const ERL_NIF_TERM argv[])

// Now try to start listener
unsigned alpn_buffer_length = 0;
QUIC_BUFFER alpn_buffers[MAX_ALPN];
QUIC_BUFFER *alpn_buffers = NULL;

// Allow insecure, default is false
ERL_NIF_TERM eisInsecure;
Expand All @@ -420,16 +420,18 @@ listen2(ErlNifEnv *env, __unused_parm__ int argc, const ERL_NIF_TERM argv[])
l_ctx->allow_insecure = TRUE;
}

if (!load_alpn(env, &options, &alpn_buffer_length, alpn_buffers))
if (!load_alpn(env, &options, &alpn_buffer_length, &alpn_buffers))
{
ret = ERROR_TUPLE_2(ATOM_ALPN);
goto exit;
}

// Start Listener
if (QUIC_FAILED(
Status = MsQuic->ListenerStart(
l_ctx->Listener, alpn_buffers, alpn_buffer_length, &Address)))
Status = MsQuic->ListenerStart(
l_ctx->Listener, alpn_buffers, alpn_buffer_length, &Address);
free_alpn_buffers(alpn_buffers, alpn_buffer_length);

if (QUIC_FAILED(Status))
{
TP_NIF_3(start_fail, (uintptr_t)(l_ctx->Listener), Status);
HQUIC Listener = l_ctx->Listener;
Expand Down Expand Up @@ -515,25 +517,26 @@ stop_listener1(ErlNifEnv *env,
return ret;
}

// For simplicity, we do not support to switch the Registration
ERL_NIF_TERM
start_listener3(ErlNifEnv *env,
__unused_parm__ int argc,
const ERL_NIF_TERM argv[])
start_listener3(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM listener_handle = argv[0];
ERL_NIF_TERM elisten_on = argv[1];
ERL_NIF_TERM options = argv[2];

QuicerListenerCTX *l_ctx;
unsigned alpn_buffer_length = 0;
QUIC_BUFFER alpn_buffers[MAX_ALPN];
QUIC_BUFFER *alpn_buffers = NULL;
QUIC_ADDR Address = {};
int UdpPort = 0;

// Return value
ERL_NIF_TERM ret = ATOM_OK;
QUIC_STATUS Status = QUIC_STATUS_SUCCESS;

CXPLAT_FRE_ASSERT(argc == 3);

if (!enif_get_resource(
env, listener_handle, ctx_listener_t, (void **)&l_ctx))
{
Expand Down Expand Up @@ -561,31 +564,89 @@ start_listener3(ErlNifEnv *env,
return ERROR_TUPLE_2(ATOM_BADARG);
}

if (!load_alpn(env, &options, &alpn_buffer_length, alpn_buffers))
QuicerConfigCTX *new_config_ctx = init_config_ctx();
if (!new_config_ctx)
{
return ERROR_TUPLE_2(ATOM_ERROR_NOT_ENOUGH_MEMORY);
}

QUIC_CREDENTIAL_CONFIG CredConfig = { 0 };
#if defined(QUICER_USE_TRUSTED_STORE)
X509_STORE *trusted_store = NULL;
ret = eoptions_to_cred_config(env, options, &CredConfig, &trusted_store);
#else
ret = eoptions_to_cred_config(env, options, &CredConfig, NULL);
#endif // QUICER_USE_TRUSTED_STORE

if (!IS_SAME_TERM(ret, ATOM_OK))
{
return ERROR_TUPLE_2(ATOM_ALPN);
return ERROR_TUPLE_2(ret);
}

// ===================================================
// Safe to access l_ctx now
// ===================================================
enif_mutex_lock(l_ctx->lock);

if (!l_ctx->Listener)
{
ret = ERROR_TUPLE_2(ATOM_CLOSED);
goto exit;
}

if (QUIC_FAILED(
Status = MsQuic->ListenerStart(
l_ctx->Listener, alpn_buffers, alpn_buffer_length, &Address)))
QuicerRegistrationCTX *target_r_ctx = NULL;

// This is a read, do not need to bump the ref count
target_r_ctx = l_ctx->r_ctx ? l_ctx->r_ctx : G_r_ctx;

ret = ServerLoadConfiguration(env,
&options,
target_r_ctx->Registration,
&new_config_ctx->Configuration,
&CredConfig);
free_certificate(&CredConfig);

if (!IS_SAME_TERM(ret, ATOM_OK))
{
enif_release_resource(new_config_ctx);
ret = ERROR_TUPLE_2(ret);
goto exit;
}

QuicerConfigCTX *old_config_ctx = l_ctx->config_resource;
l_ctx->config_resource = new_config_ctx;

#if defined(QUICER_USE_TRUSTED_STORE)
X509_STORE_free(l_ctx->trusted_store);
l_ctx->trusted_store = trusted_store;
#endif // QUICER_USE_TRUSTED_STORE
// Now we swap the config

if (!load_alpn(env, &options, &alpn_buffer_length, &alpn_buffers))
{
enif_release_resource(new_config_ctx);
ret = ERROR_TUPLE_2(ATOM_ALPN);
goto exit;
}
Status = MsQuic->ListenerStart(
l_ctx->Listener, alpn_buffers, alpn_buffer_length, &Address);

free_alpn_buffers(alpn_buffers, alpn_buffer_length);

if (QUIC_FAILED(Status))
{
TP_NIF_3(start_fail, (uintptr_t)(l_ctx->Listener), Status);
ret = ERROR_TUPLE_3(ATOM_LISTENER_START_ERROR, ATOM_STATUS(Status));
enif_release_resource(new_config_ctx);
goto exit;
}
l_ctx->is_stopped = FALSE;

// the ongoing handshake will be completed with the old config
enif_release_resource(old_config_ctx);

exit:
enif_mutex_unlock(l_ctx->lock);

return ret;
}

Expand Down
4 changes: 2 additions & 2 deletions src/quicer.erl
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ reg_open(Profile) ->
reg_close() ->
quicer_nif:reg_close().

%% @doc Start a stopped listener with listener handle.
%% @doc Start a stopped listener with listener handle with new Options.
-spec start_listener(listener_handle(), listen_on(), listen_opts()) ->
{ok, pid()} | {error, any()}.
start_listener(Listener, Port, Options) when is_list(Options) ->
Expand All @@ -274,7 +274,7 @@ start_listener(Listener, Port, Options) ->
quicer_nif:start_listener(Listener, Port, Options).

%% @doc Stop a started listener which could be closed or restarted later.
-spec stop_listener(listener_handle()) -> ok.
-spec stop_listener(listener_handle()) -> ok | {error, any()}.
stop_listener(Handle) ->
case quicer_nif:stop_listener(Handle) of
ok ->
Expand Down
2 changes: 1 addition & 1 deletion src/quicer_connection.erl
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ start_link(undefined, Listener, {_LOpts, COpts, _SOpts} = Opts, Sup) when is_map
start_link(CallbackModule, Listener, Opts, Sup) ->
gen_server:start_link(?MODULE, [CallbackModule, Listener, Opts, Sup], []).

-spec get_cb_state(ConnPid :: pid()) -> {ok, cb_state()} | {error, any()}.
-spec get_cb_state(ConnPid :: pid()) -> cb_state() | {error, any()}.
get_cb_state(ConnPid) ->
gen_server:call(ConnPid, get_cb_state, infinity).

Expand Down
Loading