From 5e7b2671da238a39001d1c4eb79f87ac56c2add2 Mon Sep 17 00:00:00 2001 From: Skywing Date: Sat, 8 Nov 2008 17:54:03 -0500 Subject: [PATCH] Fix crash on expired keyboard prompts --- apps/irssi/src/silc/core/client_ops.c | 117 +++++++++++-- apps/irssi/src/silc/core/clientutil.c | 217 ++++++++++++++++++++++++ apps/irssi/src/silc/core/clientutil.h | 58 +++++++ apps/irssi/src/silc/core/silc-queries.c | 26 ++- apps/irssi/src/silc/core/silc-servers.c | 22 +++ apps/irssi/src/silc/core/silc-servers.h | 1 + lib/silcutil/silcsocketstream.c | 27 +++ lib/silcutil/silcsocketstream.h | 35 ++++ lib/silcutil/silcsocketstream_i.h | 1 + 9 files changed, 481 insertions(+), 23 deletions(-) diff --git a/apps/irssi/src/silc/core/client_ops.c b/apps/irssi/src/silc/core/client_ops.c index e773009f..2de86781 100644 --- a/apps/irssi/src/silc/core/client_ops.c +++ b/apps/irssi/src/silc/core/client_ops.c @@ -30,6 +30,7 @@ #include "silc-queries.h" #include "silc-nicklist.h" #include "silc-cmdqueue.h" +#include "clientutil.h" #include "signals.h" #include "levels.h" @@ -1439,8 +1440,7 @@ void silc_getkey_cb(bool success, void *context) } /* - * XXX: What if the connection or client went away? They're not even - * refcounted and we don't have a way to cancel the input callback. Bad! + * Drop our references as need be. */ switch (getkey->id_type) { @@ -2394,11 +2394,13 @@ typedef struct { void *context; } *PublicKeyVerify; -static void verify_public_key_completion(const char *line, void *context) +static void verify_public_key_completion(const char *line, void *context, + SilcKeyboardPromptStatus reason) { PublicKeyVerify verify = (PublicKeyVerify)context; + bool success = (reason == KeyboardCompletionSuccess); - if (line[0] == 'Y' || line[0] == 'y') { + if (success && (line[0] == 'Y' || line[0] == 'y')) { /* Call the completion */ if (verify->completion) verify->completion(TRUE, verify->context); @@ -2417,6 +2419,30 @@ static void verify_public_key_completion(const char *line, void *context) verify->entity); } + /* + * If we were not called due to a failure to begin the callback, then we + * shall zero the async context block in the server record. If we were + * called due to a failure to begin the callback, then it is possible that + * we failed due to an overlapping callback, in which case we shouldn't + * overwrite the async context block pointer. + */ + + if (reason != KeyboardCompletionFailed) + { + /* + * Null out the completion context in the server record as this operation + * is done as far as we are concerned. The underlying keyboard library + * routine will take care of freeing the async context memory when the + * actual callback is called by irssi in the abort case. In the success + * case, it will free the async context memory after we return from this + * routine. + */ + + SILC_SERVER_REC *server = (SILC_SERVER_REC*)(verify->conn->context); + + server->prompt_op = NULL; + } + silc_free(verify->filename); silc_free(verify->entity); silc_free(verify->entity_name); @@ -2441,6 +2467,7 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn, SilcPublicKey local_pubkey; SilcSILCPublicKey silc_pubkey; SilcUInt16 port; + SILC_SERVER_REC *server; const char *hostname, *ip; unsigned char *pk; SilcUInt32 pk_len; @@ -2451,6 +2478,51 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn, "server" : "client"); int i; + server = (SILC_SERVER_REC*)conn->context; + + /* + * If we don't have a context yet, then we'll set it up based on the + * stream context associated with the SilcPacketStream that is attached + * to the SilcClientConnection. This is a bit ugly, but we need to have a + * per-connection context value to perform the public key verify operation, + * and the public API was not designed to let us have this in a particularly + * straightforward fashion. + */ + + if (!server) { + SilcPacketStream packet_stream; + SilcStream stream; + + packet_stream = conn->stream; + + if (!packet_stream) + { + if (completion) + completion(FALSE, context); + return; + } + + stream = silc_packet_stream_get_stream(packet_stream); + + if (!stream) + { + if (completion) + completion(FALSE, context); + return; + } + + server = (SILC_SERVER_REC*)(silc_socket_stream_get_context(stream)); + + if (!server) + { + if (completion) + completion(FALSE, context); + return; + } + + conn->context = (void *)server; + } + if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) { printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, @@ -2559,8 +2631,8 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn, SILCTXT_PUBKEY_BABBLEPRINT, babbleprint); format = format_get_text("fe-common/silc", NULL, NULL, NULL, SILCTXT_PUBKEY_ACCEPT); - keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion, - format, 0, verify); + silc_keyboard_entry_redirect(verify_public_key_completion, + format, 0, verify, &server->prompt_op); g_free(format); silc_free(fingerprint); silc_free(babbleprint); @@ -2592,8 +2664,8 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn, SILCTXT_PUBKEY_COULD_NOT_LOAD, entity); format = format_get_text("fe-common/silc", NULL, NULL, NULL, SILCTXT_PUBKEY_ACCEPT_ANYWAY); - keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion, - format, 0, verify); + silc_keyboard_entry_redirect(verify_public_key_completion, + format, 0, verify, &server->prompt_op); g_free(format); silc_free(fingerprint); silc_free(babbleprint); @@ -2622,8 +2694,8 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn, SILCTXT_PUBKEY_MALFORMED, entity); format = format_get_text("fe-common/silc", NULL, NULL, NULL, SILCTXT_PUBKEY_ACCEPT_ANYWAY); - keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion, - format, 0, verify); + silc_keyboard_entry_redirect(verify_public_key_completion, + format, 0, verify, &server->prompt_op); g_free(format); silc_free(fingerprint); silc_free(babbleprint); @@ -2658,8 +2730,8 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn, /* Ask user to verify the key and save it */ format = format_get_text("fe-common/silc", NULL, NULL, NULL, SILCTXT_PUBKEY_ACCEPT_ANYWAY); - keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion, - format, 0, verify); + silc_keyboard_entry_redirect(verify_public_key_completion, + format, 0, verify, &server->prompt_op); g_free(format); silc_free(fingerprint); silc_free(babbleprint); @@ -2701,28 +2773,41 @@ silc_verify_public_key(SilcClient client, SilcClientConnection conn, typedef struct { SilcAskPassphrase completion; + SilcClientConnection conn; void *context; } *AskPassphrase; -void ask_passphrase_completion(const char *passphrase, void *context) +void ask_passphrase_completion(const char *passphrase, void *context, + SilcKeyboardPromptStatus reason) { AskPassphrase p = (AskPassphrase)context; if (passphrase && passphrase[0] == '\0') passphrase = NULL; p->completion((unsigned char *)passphrase, passphrase ? strlen(passphrase) : 0, p->context); + + if (reason != KeyboardCompletionFailed) + { + SILC_SERVER_REC *server = (SILC_SERVER_REC *)(p->conn->context); + + server->prompt_op = NULL; + } + silc_free(p); } void silc_ask_passphrase(SilcClient client, SilcClientConnection conn, SilcAskPassphrase completion, void *context) { + SILC_SERVER_REC *server = (SILC_SERVER_REC*)(conn->context); AskPassphrase p = silc_calloc(1, sizeof(*p)); + p->completion = completion; - p->context = context; + p->conn = conn; + p->context = context; - keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion, - "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p); + silc_keyboard_entry_redirect(ask_passphrase_completion, + "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p, &server->prompt_op); } typedef struct { diff --git a/apps/irssi/src/silc/core/clientutil.c b/apps/irssi/src/silc/core/clientutil.c index 83634733..9a04df91 100644 --- a/apps/irssi/src/silc/core/clientutil.c +++ b/apps/irssi/src/silc/core/clientutil.c @@ -370,6 +370,205 @@ int silc_client_load_keys(SilcClient client) return ret; } +static bool silc_keyboard_prompt_pending; + +typedef struct +{ + SilcAsyncOperation async_context; + SILC_KEYBOARD_PROMPT_PROC user_prompt_proc; + void *user_context; + bool aborted; + bool *immediate_completion; +} * SilcKeyboardEntryRedirectContext; + +static void silc_keyboard_entry_redirect_abort( + SilcAsyncOperation op, + void *context) +{ + SilcKeyboardEntryRedirectContext ctx = (SilcKeyboardEntryRedirectContext)context; + + /* + * Flag ourselves as aborted so the irssi callback doesn't do any real + * work here. + */ + + ctx->aborted = TRUE; + + /* + * Call the user routine to notify it that we are aborting, so that it may + * clean up anything that needs cleaning up, e.g. references. The user + * may not reference the SilcAsyncOperation beyond this abort call. The + * recommended procedure is for the user prompt routine to null out its + * reference to the SilcAsyncOperation context. The underlying context + * structure will be released when the actual wrappered callback fires, + * though the wrappered callback will not call into user code now that + * the operation has been aborted. + */ + + ctx->user_prompt_proc(NULL, ctx->user_context, KeyboardCompletionAborted); +} + +static void silc_keyboard_entry_redirect_completion( + const char *line, + void *context) +{ + SilcKeyboardEntryRedirectContext ctx = (SilcKeyboardEntryRedirectContext)context; + + /* + * If we are aborted, then don't call the user routine. Note that we + * already notified the user that they were aborted when the abort + * call was made in the first place, so the user should not have any + * dangling references at this point. + * + * Otherwise, call the user routine. + */ + + if (!ctx->aborted) + { + ctx->user_prompt_proc(line, ctx->user_context, + KeyboardCompletionSuccess); + } + + /* + * If there's a flag to set on completion, such that we can detect when the + * operation finished immediately instead of being processed as a callback, + * then set that now. + */ + + if (ctx->immediate_completion) + *ctx->immediate_completion = TRUE; + + /* + * Clean up our internal context structures. Note that we are considered + * responsible for handling the SilcAsyncOperation release in this model, + * unless we were aborted, in which case the abort request has released it. + */ + + if (!ctx->aborted) + silc_async_free(ctx->async_context); + + silc_free(ctx); + + /* + * Mark us as not having a keyboard prompt pending. + */ + + silc_keyboard_prompt_pending = FALSE; +} + +/* Prompt for user input. */ +bool silc_keyboard_entry_redirect( + SILC_KEYBOARD_PROMPT_PROC prompt_func, + const char *entry, + int flags, + void *data, + SilcAsyncOperation *async) +{ + SilcKeyboardEntryRedirectContext ctx; + bool completed_now; + + /* + * Check if we already have a keyboard prompt pending. This sucks, but + * irssi stores the keyboard prompt data in a global, and if we request + * a prompt while there is already a prompt in progress, the old prompt + * data is leaked. If irssi gets its act together, this can (and should) + * go away. + */ + + if (silc_keyboard_prompt_pending) + { + prompt_func(NULL, data, KeyboardCompletionFailed); + return FALSE; + } + + /* + * Allocate our context blocks. + */ + + ctx = (SilcKeyboardEntryRedirectContext)silc_calloc(1, sizeof(*ctx)); + + if (!ctx) + { + prompt_func(NULL, data, KeyboardCompletionFailed); + return FALSE; + } + + ctx->async_context = silc_async_alloc(silc_keyboard_entry_redirect_abort, + NULL, ctx); + + if (!ctx->async_context) + { + silc_free(ctx); + prompt_func(NULL, data, KeyboardCompletionFailed); + return FALSE; + } + + /* + * Initially, we don't consider ourselves as having finished. + */ + + completed_now = FALSE; + + /* + * Since irssi can't handle overlapping keyboard prompt requests, block + * future requests until we are finished. N.B. This should really be + * handled inside of irssi, but this requires a breaking change to how + * keyboard callbacks are processed from an API perspective. A problem + * exists where another user could call a keyboard redirect request + * external to silc while we have one pending, and cause ours to get + * lost, in which case we will get stuck denying future prompt requests. + * + * Fortunately, nobody else seems to use keyboard prompt requests, at least + * not that I can tell. + */ + + silc_keyboard_prompt_pending = TRUE; + + /* + * Set up the call to the irssi keyboard entry redirection facility. + */ + + ctx->user_prompt_proc = prompt_func; + ctx->user_context = data; + ctx->aborted = FALSE; + ctx->immediate_completion = &completed_now; + + keyboard_entry_redirect((SIGNAL_FUNC)silc_keyboard_entry_redirect_completion, + entry, 0, ctx); + + ctx->immediate_completion = NULL; + + /* + * If we completed immediately, then there is nothing to return as the async + * context has already been released. In this case we have completed with a + * success status, but there is no SilcAsyncOperation context to return. + */ + + if (completed_now) + { + *async = NULL; + return TRUE; + } + + /* + * Otherwise, we must return an async operation context to the caller, and + * we must unset the immediate_completion flag as we don't want to be + * notified anymore since we're returning out. Note that this is not safe + * if keyboard_entry_redirect can call from a different thread, but we are + * assuming that it doesn't as there's already many other things that seem + * to make this assumption. + */ + + *async = ctx->async_context; + + /* + * All done. Irssi will invoke the callback on this thread at a later point + * in time. + */ + + return TRUE; +} + #ifdef SILC_PLUGIN void create_key_passphrase(const char *answer, CREATE_KEY_REC *rec) { @@ -379,6 +578,12 @@ void create_key_passphrase(const char *answer, CREATE_KEY_REC *rec) if ((rec->passphrase == NULL) && (answer) && (*answer != '\0')) { rec->passphrase = g_strdup(answer); + /* + * This can continue to use keyboard_entry_redirect as it's a one-time at + * initialization function. If create_key_passphrase is used + * somewhere else, then this needs to be ripped out and changed to use + * the new, more correct silc_keyboard_entry_redirect. + */ keyboard_entry_redirect((SIGNAL_FUNC) create_key_passphrase, format_get_text("fe-common/silc", NULL, NULL, NULL, SILCTXT_CONFIG_PASS_ASK2), @@ -424,6 +629,12 @@ void change_private_key_passphrase(const char *answer, CREATE_KEY_REC *rec) if (rec->old == NULL) { rec->old = g_strdup((answer == NULL ? "" : answer)); + /* + * This can continue to use keyboard_entry_redirect as it's a one-time at + * initialization function. If change_private_key_passphrase is used + * somewhere else, then this needs to be ripped out and changed to use + * the new, more correct silc_keyboard_entry_redirect. + */ keyboard_entry_redirect((SIGNAL_FUNC) change_private_key_passphrase, format_get_text("fe-common/silc", NULL, NULL, NULL, SILCTXT_CONFIG_PASS_ASK2), @@ -433,6 +644,12 @@ void change_private_key_passphrase(const char *answer, CREATE_KEY_REC *rec) if ((rec->passphrase == NULL) && (answer) && (*answer != '\0')) { rec->passphrase = g_strdup(answer); + /* + * This can continue to use keyboard_entry_redirect as it's a one-time at + * initialization function. If change_private_key_passphrase is used + * somewhere else, then this needs to be ripped out and changed to use + * the new, more correct silc_keyboard_entry_redirect. + */ keyboard_entry_redirect((SIGNAL_FUNC) change_private_key_passphrase, format_get_text("fe-common/silc", NULL, NULL, NULL, SILCTXT_CONFIG_PASS_ASK3), diff --git a/apps/irssi/src/silc/core/clientutil.h b/apps/irssi/src/silc/core/clientutil.h index 240100a6..dc60a049 100644 --- a/apps/irssi/src/silc/core/clientutil.h +++ b/apps/irssi/src/silc/core/clientutil.h @@ -31,6 +31,64 @@ void silc_client_list_pkcs(); int silc_client_check_silc_dir(); int silc_client_load_keys(SilcClient client); +typedef enum +{ + KeyboardCompletionSuccess, /* Success; keyboard data returned to callback. */ + KeyboardCompletionAborted, /* Operation was aborted after starting successfully. */ + KeyboardCompletionFailed /* Operation was not started successfully. */ +} SilcKeyboardPromptStatus; + +typedef void (*SILC_KEYBOARD_PROMPT_PROC)( + const char *line, + void *context, + SilcKeyboardPromptStatus reason); + +/* + * Prompt for keyboard input. + * + * If the function returns FALSE, then the prompt operation could not be + * initiated and the user supplied callback is called to indicate that the + * operation was not started (reason KeyboardCompletionFailed). This can be + * used to centralize all cleanup work in the callback function. + * + * If the function returns TRUE, then the operation was initiated successfully + * and the prompt callback is guaranteed to be called sometime in the future. + * Note that it is posssible for the completion callback to have been already + * called by the time the function returns TRUE. In this instance, the + * callback will eventually be called with KeyboardCompletionSuccess, unless + * the operation is aborted before then. + * + * If the function returns TRUE, then a SilcAsyncOperation context may be + * returned. If an async operation context is returned, then the operation has + * not been completed immediately, and may be canceled with a call to + * silc_async_abort(*async). + * + * Note that the SilcAsyncOperation object's lifetime is managed internally. A + * user may call silc_async_abort exactly once, after which it can never touch + * the async context again. Additionally, the async context may not be + * referenced after the user callback returns. The recommended way to handle + * the async operation context is to remove the reference to it when the user + * callback is called, either for an abort or regular completion. If the + * callback is called with a KeyboardCompletionFailed reason, then no async + * context object was allocated. + * + * If an abort is requested, then the user callback is called with reason code + * KeyboardCompletionAborted. In this case, the user should clean up all + * associated callback data and perform the handling expected in the abort case, + * such as the associated server connection going away while the operation was + * in progress. + * + * There can only be one keyboard redirect operation in progress. If a + * keyboard redirect operation is aborted while we are still waiting for data, + * then we abort all callbacks until that callback returns. + */ +bool silc_keyboard_entry_redirect( + SILC_KEYBOARD_PROMPT_PROC prompt_func, + const char *entry, + int flags, + void *data, + SilcAsyncOperation *async); + #ifdef SILC_PLUGIN typedef struct { char *old, *passphrase, *file, *pkcs; diff --git a/apps/irssi/src/silc/core/silc-queries.c b/apps/irssi/src/silc/core/silc-queries.c index 1e7f7513..0da01d6b 100644 --- a/apps/irssi/src/silc/core/silc-queries.c +++ b/apps/irssi/src/silc/core/silc-queries.c @@ -26,6 +26,7 @@ #include "modules.h" #include "commands.h" #include "misc.h" +#include "clientutil.h" #include "fe-common/core/printtext.h" #include "fe-common/core/fe-channels.h" @@ -33,7 +34,8 @@ #include "fe-common/silc/module-formats.h" static void silc_query_attributes_print_final(bool success, void *context); -static void silc_query_attributes_accept(const char *line, void *context); +static void silc_query_attributes_accept(const char *line, void *context, + SilcKeyboardPromptStatus reason); QUERY_REC *silc_query_create(const char *server_tag, const char *nick, int automatic) @@ -558,6 +560,7 @@ typedef struct { SilcMime message; SilcMime extension; bool nopk; + bool autoaccept; } *AttrVerify; void silc_query_attributes_print(SILC_SERVER_REC *server, @@ -925,6 +928,7 @@ static void silc_query_attributes_print_final(bool success, void *context) unsigned char filename[256], *fingerprint = NULL, *tmp; struct stat st; int i; + size_t len; if (!verify->nopk) { if (success) { @@ -945,7 +949,10 @@ static void silc_query_attributes_print_final(bool success, void *context) fingerprint = silc_hash_fingerprint(sha1hash, verify->userpk.data, verify->userpk.data_len); - for (i = 0; i < strlen(fingerprint); i++) + + len = strlen(fingerprint); + + for (i = 0; i < len; i++) if (fingerprint[i] == ' ') fingerprint[i] = '_'; @@ -959,17 +966,19 @@ static void silc_query_attributes_print_final(bool success, void *context) /* Ask to accept save requested attributes */ format = format_get_text("fe-common/silc", NULL, NULL, NULL, SILCTXT_ATTR_SAVE); - keyboard_entry_redirect((SIGNAL_FUNC)silc_query_attributes_accept, - format, 0, verify); + silc_keyboard_entry_redirect(silc_query_attributes_accept, + format, 0, verify, &server->prompt_op); } else { /* Save new data to existing directory */ - silc_query_attributes_accept("Y", verify); + verify->autoaccept = TRUE; /* Ensure we don't twiddle the async context */ + silc_query_attributes_accept("Y", verify, KeyboardCompletionSuccess); } g_free(format); } -static void silc_query_attributes_accept(const char *line, void *context) +static void silc_query_attributes_accept(const char *line, void *context, + SilcKeyboardPromptStatus reason) { AttrVerify verify = context; SILC_SERVER_REC *server = verify->server; @@ -978,8 +987,9 @@ static void silc_query_attributes_accept(const char *line, void *context) unsigned char filename[256], filename2[256], *fingerprint = NULL, *tmp; SilcUInt32 len; int i; + bool success = (reason == KeyboardCompletionSuccess); - if (line[0] == 'Y' || line[0] == 'y') { + if (success && (line[0] == 'Y' || line[0] == 'y')) { /* Save the attributes */ memset(filename, 0, sizeof(filename)); memset(filename2, 0, sizeof(filename2)); @@ -1066,6 +1076,8 @@ static void silc_query_attributes_accept(const char *line, void *context) } out: + if((!verify->autoaccept) && (reason != KeyboardCompletionFailed)) + verify->server->prompt_op = NULL; silc_free(fingerprint); silc_free(verify->name); silc_vcard_free(&verify->vcard); diff --git a/apps/irssi/src/silc/core/silc-servers.c b/apps/irssi/src/silc/core/silc-servers.c index e16fb02c..e763b8ee 100644 --- a/apps/irssi/src/silc/core/silc-servers.c +++ b/apps/irssi/src/silc/core/silc-servers.c @@ -426,6 +426,22 @@ static void sig_connected_stream_created(SilcSocketStreamStatus status, SILCTXT_REATTACH, server->tag); silc_free(file); + /* + * Store the SILC_SERVER_REC in the stream context so that we can fetch it + * from the verify key exchange prompt. There should have been an initial + * user parameter value for the SilcClientConnection that could have been + * passed to SilcClientConnectionParams, but because there's no version + * number or size field in SilcClientConnectionParams, it is fixed for all + * time and not extendable. + * + * Instead, we must revert to pulling the SilcStream out of the + * SilcPacketStream associated with the SilcClientConnection object in the + * verify key exchange prompt callback in order to get our per-connection + * context. Hence, the extra levels of indirection. + */ + + silc_socket_stream_set_context(stream, server); + /* Start key exchange */ server->op = silc_client_key_exchange(silc_client, ¶ms, irssi_pubkey, irssi_privkey, @@ -466,6 +482,12 @@ static void sig_disconnected(SILC_SERVER_REC *server) if (!IS_SILC_SERVER(server)) return; + /* If we have a prompt in progress, then abort it. */ + if (server->prompt_op) { + silc_async_abort(server->prompt_op, NULL, NULL); + server->prompt_op = NULL; + } + if (server->conn) { /* Close connection */ silc_client_close_connection(silc_client, server->conn); diff --git a/apps/irssi/src/silc/core/silc-servers.h b/apps/irssi/src/silc/core/silc-servers.h index efde07e6..a638b162 100644 --- a/apps/irssi/src/silc/core/silc-servers.h +++ b/apps/irssi/src/silc/core/silc-servers.h @@ -46,6 +46,7 @@ typedef struct { SilcClientConnection conn; SilcAsyncOperation op; /* Key exchange operation handle */ SilcAsyncOperation tcp_op; /* TCP stream creation operation handle */ + SilcAsyncOperation prompt_op; /* Key verification operation handle */ SilcUInt32 umode; } SILC_SERVER_REC; diff --git a/lib/silcutil/silcsocketstream.c b/lib/silcutil/silcsocketstream.c index af8f2c34..06d55484 100644 --- a/lib/silcutil/silcsocketstream.c +++ b/lib/silcutil/silcsocketstream.c @@ -392,6 +392,33 @@ SilcSchedule silc_socket_stream_get_schedule(SilcStream stream) return socket_stream->schedule; } +/* Return associated context. */ + +void * +silc_socket_stream_get_context(SilcStream stream) +{ + SilcSocketStream socket_stream = stream; + + if (!SILC_IS_SOCKET_STREAM(socket_stream)) + return NULL; + + return socket_stream->user_context; +} + +/* Set associated context. */ + +void +silc_socket_stream_set_context(SilcStream stream, + void *context) +{ + SilcSocketStream socket_stream = stream; + + if (!SILC_IS_SOCKET_STREAM(socket_stream)) + return; + + socket_stream->user_context = context; +} + /* SILC Socket Stream ops. Functions are implemented under the platform specific subdirectories. */ const SilcStreamOps silc_socket_stream_ops = diff --git a/lib/silcutil/silcsocketstream.h b/lib/silcutil/silcsocketstream.h index e9fd4357..264668ca 100644 --- a/lib/silcutil/silcsocketstream.h +++ b/lib/silcutil/silcsocketstream.h @@ -275,6 +275,41 @@ SilcBool silc_socket_stream_set_qos(SilcStream stream, SilcUInt32 limit_sec, SilcUInt32 limit_usec); + +/****f* silcutil/SilcSocketStreamAPI/silc_socket_stream_get_context + * + * SYNOPSIS + * + * void * + * silc_socket_stream_get_context(SilcSocketStream stream); + * + * DESCRIPTION + * + * Returns the context associated with a stream. This context is + * set via silc_socket_stream_set_context. + * + ***/ +void * +silc_socket_stream_get_context(SilcStream stream); + +/****f* silcutil/SilcSocketStreamAPI/silc_socket_stream_set_context + * + * SYNOPSIS + * + * void + * silc_socket_stream_set_context(SilcSocketStream stream, + * void *context); + * + * DESCRIPTION + * + * Returns the context associated with a stream. This context is + * set via silc_socket_stream_set_context. + * + ***/ +void +silc_socket_stream_set_context(SilcStream stream, + void *context); + #include "silcsocketstream_i.h" #endif /* SILCSOCKETSTREAM_H */ diff --git a/lib/silcutil/silcsocketstream_i.h b/lib/silcutil/silcsocketstream_i.h index 13013f25..837cc239 100644 --- a/lib/silcutil/silcsocketstream_i.h +++ b/lib/silcutil/silcsocketstream_i.h @@ -52,6 +52,7 @@ struct SilcSocketStreamStruct { SilcSocketQos qos; SilcStreamNotifier notifier; void *notifier_context; + void *user_context; unsigned int ipv6 : 1; /* UDP IPv6 */ unsigned int connected : 1; /* UDP connected state */ }; -- 2.24.0