Skip to content

Commit

Permalink
Merge pull request #253 from qzhuyan/dev/william/listener-reload
Browse files Browse the repository at this point in the history
Listener reload
  • Loading branch information
qzhuyan authored Jan 3, 2024
2 parents 6b1825d + 214bd68 commit 746d7d4
Show file tree
Hide file tree
Showing 12 changed files with 404 additions and 72 deletions.
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

0 comments on commit 746d7d4

Please sign in to comment.