/*
* 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.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
SPDX-License-Identifier: AGPL3.0-or-later
*/
/**
* @author Gabor X Toth
*
* @file
* PSYC Slicer API
*/
#include
#include
#include
#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);
}