/* * This file is part of GNUnet * Copyright (C) 2013 GNUnet e.V. * * GNUnet is free software: you can redistribute it and/or modify it * under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * GNUnet is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Affero General Public License for more details. */ /** * @author Gabor X Toth * * @file * PSYC Slicer API */ #include #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_psyc_util_lib.h" #define LOG(kind,...) GNUNET_log_from (kind, "psyc-util-slicer",__VA_ARGS__) /** * Handle for a try-and-slice instance. */ struct GNUNET_PSYC_Slicer { /** * Method handlers: H(method_name) -> SlicerMethodCallbacks */ struct GNUNET_CONTAINER_MultiHashMap *method_handlers; /** * Modifier handlers: H(modifier_name) -> SlicerModifierCallbacks */ struct GNUNET_CONTAINER_MultiHashMap *modifier_handlers; /** * Receive handle for incoming messages. */ struct GNUNET_PSYC_ReceiveHandle *recv; /** * Currently being processed message. */ const struct GNUNET_PSYC_MessageHeader *msg; /** * Currently being processed message part. */ const struct GNUNET_MessageHeader *pmsg; /** * ID of currently being received message. */ uint64_t message_id; /** * Fragment offset of currently being received message. */ uint64_t fragment_offset; /** * Flags of currently being received message. */ uint32_t flags; /** * Method name of currently being received message. */ char *method_name; /** * Name of currently processed modifier. */ char *mod_name; /** * Value of currently processed modifier. */ char *mod_value; /** * Public key of the nym the current message originates from. */ struct GNUNET_CRYPTO_EcdsaPublicKey nym_pub_key; /** * Size of @a method_name (including terminating \0). */ uint16_t method_name_size; /** * Size of @a modifier_name (including terminating \0). */ uint16_t mod_name_size; /** * Size of modifier value fragment. */ uint16_t mod_value_size; /** * Full size of modifier value. */ uint16_t mod_full_value_size; /** * Remaining bytes from the value of the current modifier. */ uint16_t mod_value_remaining; /** * Operator of currently processed modifier. */ uint8_t mod_oper; }; /** * Callbacks for a slicer method handler. */ struct SlicerMethodCallbacks { GNUNET_PSYC_MessageCallback msg_cb; GNUNET_PSYC_MethodCallback method_cb; GNUNET_PSYC_ModifierCallback modifier_cb; GNUNET_PSYC_DataCallback data_cb; GNUNET_PSYC_EndOfMessageCallback eom_cb; void *cls; }; struct SlicerMethodRemoveClosure { struct GNUNET_PSYC_Slicer *slicer; struct SlicerMethodCallbacks rm_cbs; }; /** * Callbacks for a slicer method handler. */ struct SlicerModifierCallbacks { GNUNET_PSYC_ModifierCallback modifier_cb; void *cls; }; struct SlicerModifierRemoveClosure { struct GNUNET_PSYC_Slicer *slicer; struct SlicerModifierCallbacks rm_cbs; }; /** * Call a method handler for an incoming message part. */ static int slicer_method_handler_notify (void *cls, const struct GNUNET_HashCode *key, void *value) { struct GNUNET_PSYC_Slicer *slicer = cls; const struct GNUNET_MessageHeader *pmsg = slicer->pmsg; struct SlicerMethodCallbacks *cbs = value; uint16_t ptype = ntohs (pmsg->type); switch (ptype) { case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD: { if (NULL != cbs->msg_cb) cbs->msg_cb (cbs->cls, slicer->msg); if (NULL == cbs->method_cb) break; struct GNUNET_PSYC_MessageMethod * meth = (struct GNUNET_PSYC_MessageMethod *) pmsg; cbs->method_cb (cbs->cls, slicer->msg, meth, slicer->message_id, slicer->method_name); break; } case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER: { if (NULL == cbs->modifier_cb) break; struct GNUNET_PSYC_MessageModifier * mod = (struct GNUNET_PSYC_MessageModifier *) pmsg; cbs->modifier_cb (cbs->cls, slicer->msg, &mod->header, slicer->message_id, mod->oper, (const char *) &mod[1], (const void *) &mod[1] + ntohs (mod->name_size), ntohs (mod->header.size) - sizeof (*mod) - ntohs (mod->name_size), ntohs (mod->value_size)); break; } case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT: { if (NULL == cbs->modifier_cb) break; cbs->modifier_cb (cbs->cls, slicer->msg, pmsg, slicer->message_id, slicer->mod_oper, slicer->mod_name, &pmsg[1], ntohs (pmsg->size) - sizeof (*pmsg), slicer->mod_full_value_size); break; } case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA: { if (NULL == cbs->data_cb) break; cbs->data_cb (cbs->cls, slicer->msg, pmsg, slicer->message_id, &pmsg[1], ntohs (pmsg->size) - sizeof (*pmsg)); break; } case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END: if (NULL == cbs->eom_cb) break; cbs->eom_cb (cbs->cls, slicer->msg, pmsg, slicer->message_id, GNUNET_NO); break; case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL: if (NULL == cbs->eom_cb) break; cbs->eom_cb (cbs->cls, slicer->msg, pmsg, slicer->message_id, GNUNET_YES); break; } return GNUNET_YES; } /** * Call a method handler for an incoming message part. */ static int slicer_modifier_handler_notify (void *cls, const struct GNUNET_HashCode *key, void *value) { struct GNUNET_PSYC_Slicer *slicer = cls; struct SlicerModifierCallbacks *cbs = value; cbs->modifier_cb (cbs->cls, slicer->msg, slicer->pmsg, slicer->message_id, slicer->mod_oper, slicer->mod_name, slicer->mod_value, slicer->mod_value_size, slicer->mod_full_value_size); return GNUNET_YES; } /** * Process an incoming message and call matching handlers. * * @param slicer * The slicer to use. * @param msg * The message as it arrived from the network. */ void GNUNET_PSYC_slicer_message (struct GNUNET_PSYC_Slicer *slicer, const struct GNUNET_PSYC_MessageHeader *msg) { GNUNET_PSYC_receive_message (slicer->recv, msg); } /** * Process an incoming message part and call matching handlers. * * @param cls * Closure. * @param message_id * ID of the message. * @param flags * Flags for the message. * @see enum GNUNET_PSYC_MessageFlags * @param msg * The message part. as it arrived from the network. */ void GNUNET_PSYC_slicer_message_part (struct GNUNET_PSYC_Slicer *slicer, const struct GNUNET_PSYC_MessageHeader *msg, const struct GNUNET_MessageHeader *pmsg) { slicer->msg = msg; slicer->pmsg = pmsg; uint64_t message_id = GNUNET_ntohll (msg->message_id); uint16_t ptype = ntohs (pmsg->type); if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == ptype) { struct GNUNET_PSYC_MessageMethod * meth = (struct GNUNET_PSYC_MessageMethod *) pmsg; slicer->method_name_size = ntohs (meth->header.size) - sizeof (*meth); slicer->method_name = GNUNET_malloc (slicer->method_name_size); GNUNET_memcpy (slicer->method_name, &meth[1], slicer->method_name_size); slicer->message_id = message_id; } else { GNUNET_assert (message_id == slicer->message_id); } char *nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&msg->slave_pub_key); LOG (GNUNET_ERROR_TYPE_DEBUG, "Slicer received message of type %u and size %u, " "with ID %" PRIu64 " and method %s from %s\n", ptype, ntohs (pmsg->size), message_id, slicer->method_name, nym_str); GNUNET_free (nym_str); /* try-and-slice modifier */ switch (ptype) { case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER: { struct GNUNET_PSYC_MessageModifier * mod = (struct GNUNET_PSYC_MessageModifier *) pmsg; slicer->mod_oper = mod->oper; slicer->mod_name_size = ntohs (mod->name_size); slicer->mod_name = GNUNET_malloc (slicer->mod_name_size); GNUNET_memcpy (slicer->mod_name, &mod[1], slicer->mod_name_size); slicer->mod_value = (char *) &mod[1] + slicer->mod_name_size; slicer->mod_full_value_size = ntohs (mod->value_size); slicer->mod_value_remaining = slicer->mod_full_value_size; slicer->mod_value_size = ntohs (mod->header.size) - sizeof (*mod) - slicer->mod_name_size; // fall through } case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT: if (ptype == GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT) { slicer->mod_value = (char *) &pmsg[1]; slicer->mod_value_size = ntohs (pmsg->size) - sizeof (*pmsg); } slicer->mod_value_remaining -= slicer->mod_value_size; char *name = GNUNET_malloc (slicer->mod_name_size); GNUNET_memcpy (name, slicer->mod_name, slicer->mod_name_size); do { struct GNUNET_HashCode key; uint16_t name_len = strlen (name); GNUNET_CRYPTO_hash (name, name_len, &key); GNUNET_CONTAINER_multihashmap_get_multiple (slicer->modifier_handlers, &key, slicer_modifier_handler_notify, slicer); char *p = strrchr (name, '_'); if (NULL == p) break; *p = '\0'; } while (1); GNUNET_free (name); } /* try-and-slice method */ char *name = GNUNET_malloc (slicer->method_name_size); GNUNET_memcpy (name, slicer->method_name, slicer->method_name_size); do { struct GNUNET_HashCode key; uint16_t name_len = strlen (name); GNUNET_CRYPTO_hash (name, name_len, &key); GNUNET_CONTAINER_multihashmap_get_multiple (slicer->method_handlers, &key, slicer_method_handler_notify, slicer); char *p = strrchr (name, '_'); if (NULL == p) break; *p = '\0'; } while (1); GNUNET_free (name); if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype) GNUNET_free (slicer->method_name); if (0 == slicer->mod_value_remaining && NULL != slicer->mod_name) { GNUNET_free (slicer->mod_name); slicer->mod_name = NULL; slicer->mod_name_size = 0; slicer->mod_value_size = 0; slicer->mod_full_value_size = 0; slicer->mod_oper = 0; } slicer->msg = NULL; slicer->pmsg = NULL; } /** * Create a try-and-slice instance. * * A slicer processes incoming messages and notifies callbacks about matching * methods or modifiers encountered. * * @return A new try-and-slice construct. */ struct GNUNET_PSYC_Slicer * GNUNET_PSYC_slicer_create (void) { struct GNUNET_PSYC_Slicer *slicer = GNUNET_malloc (sizeof (*slicer)); slicer->method_handlers = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO); slicer->modifier_handlers = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO); slicer->recv = GNUNET_PSYC_receive_create (NULL, (GNUNET_PSYC_MessagePartCallback) GNUNET_PSYC_slicer_message_part, slicer); return slicer; } /** * Add a method to the try-and-slice instance. * * The callbacks are called for messages with a matching @a method_name prefix. * * @param slicer * The try-and-slice instance to extend. * @param method_name * Name of the given method, use empty string to match all. * @param method_cb * Method handler invoked upon a matching message. * @param modifier_cb * Modifier handler, invoked after @a method_cb * for each modifier in the message. * @param data_cb * Data handler, invoked after @a modifier_cb for each data fragment. * @param eom_cb * Invoked upon reaching the end of a matching message. * @param cls * Closure for the callbacks. */ void GNUNET_PSYC_slicer_method_add (struct GNUNET_PSYC_Slicer *slicer, const char *method_name, GNUNET_PSYC_MessageCallback msg_cb, GNUNET_PSYC_MethodCallback method_cb, GNUNET_PSYC_ModifierCallback modifier_cb, GNUNET_PSYC_DataCallback data_cb, GNUNET_PSYC_EndOfMessageCallback eom_cb, void *cls) { struct GNUNET_HashCode key; GNUNET_CRYPTO_hash (method_name, strlen (method_name), &key); struct SlicerMethodCallbacks *cbs = GNUNET_malloc (sizeof (*cbs)); cbs->msg_cb = msg_cb, cbs->method_cb = method_cb; cbs->modifier_cb = modifier_cb; cbs->data_cb = data_cb; cbs->eom_cb = eom_cb; cbs->cls = cls; GNUNET_CONTAINER_multihashmap_put (slicer->method_handlers, &key, cbs, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); } static int slicer_method_remove (void *cls, const struct GNUNET_HashCode *key, void *value) { struct SlicerMethodRemoveClosure *rm_cls = cls; struct GNUNET_PSYC_Slicer *slicer = rm_cls->slicer; struct SlicerMethodCallbacks *rm_cbs = &rm_cls->rm_cbs; struct SlicerMethodCallbacks *cbs = value; if ((NULL == rm_cbs->msg_cb || cbs->msg_cb == rm_cbs->msg_cb) && (NULL == rm_cbs->method_cb || cbs->method_cb == rm_cbs->method_cb) && (NULL == rm_cbs->modifier_cb || cbs->modifier_cb == rm_cbs->modifier_cb) && (NULL == rm_cbs->data_cb || cbs->data_cb == rm_cbs->data_cb) && (NULL == rm_cbs->eom_cb || cbs->eom_cb == rm_cbs->eom_cb)) { GNUNET_CONTAINER_multihashmap_remove (slicer->method_handlers, key, cbs); GNUNET_free (cbs); return GNUNET_NO; } return GNUNET_YES; } /** * Remove a registered method from the try-and-slice instance. * * Removes one matching handler registered with the given * @a method_name and callbacks. * * @param slicer * The try-and-slice instance. * @param method_name * Name of the method to remove. * @param method_cb * Method handler. * @param modifier_cb * Modifier handler. * @param data_cb * Data handler. * @param eom_cb * End of message handler. * * @return #GNUNET_OK if a method handler was removed, * #GNUNET_NO if no handler matched the given method name and callbacks. */ int GNUNET_PSYC_slicer_method_remove (struct GNUNET_PSYC_Slicer *slicer, const char *method_name, GNUNET_PSYC_MessageCallback msg_cb, GNUNET_PSYC_MethodCallback method_cb, GNUNET_PSYC_ModifierCallback modifier_cb, GNUNET_PSYC_DataCallback data_cb, GNUNET_PSYC_EndOfMessageCallback eom_cb) { struct GNUNET_HashCode key; GNUNET_CRYPTO_hash (method_name, strlen (method_name), &key); struct SlicerMethodRemoveClosure rm_cls; rm_cls.slicer = slicer; struct SlicerMethodCallbacks *rm_cbs = &rm_cls.rm_cbs; rm_cbs->msg_cb = msg_cb; rm_cbs->method_cb = method_cb; rm_cbs->modifier_cb = modifier_cb; rm_cbs->data_cb = data_cb; rm_cbs->eom_cb = eom_cb; return (GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_get_multiple (slicer->method_handlers, &key, slicer_method_remove, &rm_cls)) ? GNUNET_NO : GNUNET_OK; } /** * Watch a place for changed objects. * * @param slicer * The try-and-slice instance. * @param object_filter * Object prefix to match. * @param modifier_cb * Function to call when encountering a state modifier. * @param cls * Closure for callback. */ void GNUNET_PSYC_slicer_modifier_add (struct GNUNET_PSYC_Slicer *slicer, const char *object_filter, GNUNET_PSYC_ModifierCallback modifier_cb, void *cls) { struct SlicerModifierCallbacks *cbs = GNUNET_malloc (sizeof *cbs); cbs->modifier_cb = modifier_cb; cbs->cls = cls; struct GNUNET_HashCode key; GNUNET_CRYPTO_hash (object_filter, strlen (object_filter), &key); GNUNET_CONTAINER_multihashmap_put (slicer->modifier_handlers, &key, cbs, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); } static int slicer_modifier_remove (void *cls, const struct GNUNET_HashCode *key, void *value) { struct SlicerModifierRemoveClosure *rm_cls = cls; struct GNUNET_PSYC_Slicer *slicer = rm_cls->slicer; struct SlicerModifierCallbacks *rm_cbs = &rm_cls->rm_cbs; struct SlicerModifierCallbacks *cbs = value; if (cbs->modifier_cb == rm_cbs->modifier_cb) { GNUNET_CONTAINER_multihashmap_remove (slicer->modifier_handlers, key, cbs); GNUNET_free (cbs); return GNUNET_NO; } return GNUNET_YES; } /** * Remove a registered modifier from the try-and-slice instance. * * Removes one matching handler registered with the given * @a object_filter and @a modifier_cb. * * @param slicer * The try-and-slice instance. * @param object_filter * Object prefix to match. * @param modifier_cb * Function to call when encountering a state modifier changes. */ int GNUNET_PSYC_slicer_modifier_remove (struct GNUNET_PSYC_Slicer *slicer, const char *object_filter, GNUNET_PSYC_ModifierCallback modifier_cb) { struct GNUNET_HashCode key; GNUNET_CRYPTO_hash (object_filter, strlen (object_filter), &key); struct SlicerModifierRemoveClosure rm_cls; rm_cls.slicer = slicer; struct SlicerModifierCallbacks *rm_cbs = &rm_cls.rm_cbs; rm_cbs->modifier_cb = modifier_cb; return (GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_get_multiple (slicer->modifier_handlers, &key, slicer_modifier_remove, &rm_cls)) ? GNUNET_NO : GNUNET_OK; } static int slicer_method_free (void *cls, const struct GNUNET_HashCode *key, void *value) { struct SlicerMethodCallbacks *cbs = value; GNUNET_free (cbs); return GNUNET_YES; } static int slicer_modifier_free (void *cls, const struct GNUNET_HashCode *key, void *value) { struct SlicerModifierCallbacks *cbs = value; GNUNET_free (cbs); return GNUNET_YES; } /** * Remove all registered method handlers. * * @param slicer * Slicer to clear. */ void GNUNET_PSYC_slicer_method_clear (struct GNUNET_PSYC_Slicer *slicer) { GNUNET_CONTAINER_multihashmap_iterate (slicer->method_handlers, slicer_method_free, NULL); GNUNET_CONTAINER_multihashmap_clear (slicer->method_handlers); } /** * Remove all registered modifier handlers. * * @param slicer * Slicer to clear. */ void GNUNET_PSYC_slicer_modifier_clear (struct GNUNET_PSYC_Slicer *slicer) { GNUNET_CONTAINER_multihashmap_iterate (slicer->modifier_handlers, slicer_modifier_free, NULL); GNUNET_CONTAINER_multihashmap_clear (slicer->modifier_handlers); } /** * Remove all registered method & modifier handlers. * * @param slicer * Slicer to clear. */ void GNUNET_PSYC_slicer_clear (struct GNUNET_PSYC_Slicer *slicer) { GNUNET_PSYC_slicer_method_clear (slicer); GNUNET_PSYC_slicer_modifier_clear (slicer); } /** * Destroy a given try-and-slice instance. * * @param slicer * Slicer to destroy */ void GNUNET_PSYC_slicer_destroy (struct GNUNET_PSYC_Slicer *slicer) { GNUNET_PSYC_slicer_clear (slicer); GNUNET_CONTAINER_multihashmap_destroy (slicer->method_handlers); GNUNET_CONTAINER_multihashmap_destroy (slicer->modifier_handlers); GNUNET_PSYC_receive_destroy (slicer->recv); GNUNET_free (slicer); }