aboutsummaryrefslogtreecommitdiff
path: root/src/psycutil
diff options
context:
space:
mode:
Diffstat (limited to 'src/psycutil')
-rw-r--r--src/psycutil/.gitignore1
-rw-r--r--src/psycutil/Makefile.am45
-rw-r--r--src/psycutil/psyc_env.c196
-rw-r--r--src/psycutil/psyc_message.c1355
-rw-r--r--src/psycutil/psyc_slicer.c711
-rw-r--r--src/psycutil/test_psyc_env.c96
6 files changed, 2404 insertions, 0 deletions
diff --git a/src/psycutil/.gitignore b/src/psycutil/.gitignore
new file mode 100644
index 0000000..03d8197
--- /dev/null
+++ b/src/psycutil/.gitignore
@@ -0,0 +1 @@
test_psyc_env
diff --git a/src/psycutil/Makefile.am b/src/psycutil/Makefile.am
new file mode 100644
index 0000000..2732c3a
--- /dev/null
+++ b/src/psycutil/Makefile.am
@@ -0,0 +1,45 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4pkgcfgdir= $(pkgdatadir)/config.d/
5
6libexecdir= $(pkglibdir)/libexec/
7
8if MINGW
9 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
10endif
11
12if USE_COVERAGE
13 AM_CFLAGS = --coverage -O0
14 XLIB = -lgcov
15endif
16
17lib_LTLIBRARIES = libgnunetpsycutil.la
18
19libgnunetpsycutil_la_SOURCES = \
20 psyc_env.c \
21 psyc_message.c \
22 psyc_slicer.c
23libgnunetpsycutil_la_LIBADD = \
24 $(top_builddir)/src/util/libgnunetutil.la \
25 $(GN_LIBINTL) $(XLIB)
26libgnunetpsycutil_la_LDFLAGS = \
27 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
28 -version-info 0:0:0
29
30if HAVE_TESTING
31check_PROGRAMS = \
32 test_psyc_env
33endif
34
35if ENABLE_TEST_RUN
36AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
37TESTS = $(check_PROGRAMS)
38endif
39
40test_psyc_env_SOURCES = \
41 test_psyc_env.c
42test_psyc_env_LDADD = \
43 libgnunetpsycutil.la \
44 $(top_builddir)/src/testing/libgnunettesting.la \
45 $(top_builddir)/src/util/libgnunetutil.la
diff --git a/src/psycutil/psyc_env.c b/src/psycutil/psyc_env.c
new file mode 100644
index 0000000..fc4b8eb
--- /dev/null
+++ b/src/psycutil/psyc_env.c
@@ -0,0 +1,196 @@
1/*
2 * This file is part of GNUnet.
3 * Copyright (C) 2013 GNUnet e.V.
4 *
5 * GNUnet is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Affero General Public License as published
7 * by the Free Software Foundation, either version 3 of the License,
8 * or (at your option) any later version.
9 *
10 * GNUnet is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @author Gabor X Toth
23 *
24 * @file
25 * Library providing operations for the @e environment of
26 * PSYC and Social messages.
27 */
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31#include "gnunet_psyc_env.h"
32
33/**
34 * Environment for a message.
35 *
36 * Contains modifiers.
37 */
38struct GNUNET_PSYC_Environment
39{
40 struct GNUNET_PSYC_Modifier *mod_head;
41 struct GNUNET_PSYC_Modifier *mod_tail;
42 size_t mod_count;
43};
44
45
46/**
47 * Create an environment.
48 *
49 * @return A newly allocated environment.
50 */
51struct GNUNET_PSYC_Environment *
52GNUNET_PSYC_env_create ()
53{
54 return GNUNET_new (struct GNUNET_PSYC_Environment);
55}
56
57
58/**
59 * Add a modifier to the environment.
60 *
61 * @param env The environment.
62 * @param oper Operation to perform.
63 * @param name Name of the variable.
64 * @param value Value of the variable.
65 * @param value_size Size of @a value.
66 */
67void
68GNUNET_PSYC_env_add (struct GNUNET_PSYC_Environment *env,
69 enum GNUNET_PSYC_Operator oper, const char *name,
70 const void *value, size_t value_size)
71{
72 struct GNUNET_PSYC_Modifier *mod = GNUNET_new (struct GNUNET_PSYC_Modifier);
73 mod->oper = oper;
74 mod->name = name;
75 mod->value = value;
76 mod->value_size = value_size;
77 GNUNET_CONTAINER_DLL_insert_tail (env->mod_head, env->mod_tail, mod);
78 env->mod_count++;
79}
80
81
82/**
83 * Get the first modifier of the environment.
84 */
85struct GNUNET_PSYC_Modifier *
86GNUNET_PSYC_env_head (const struct GNUNET_PSYC_Environment *env)
87{
88 return env->mod_head;
89}
90
91
92/**
93 * Get the last modifier of the environment.
94 */
95struct GNUNET_PSYC_Modifier *
96GNUNET_PSYC_env_tail (const struct GNUNET_PSYC_Environment *env)
97{
98 return env->mod_tail;
99}
100
101
102/**
103 * Remove a modifier from the environment.
104 */
105void
106GNUNET_PSYC_env_remove (struct GNUNET_PSYC_Environment *env,
107 struct GNUNET_PSYC_Modifier *mod)
108{
109 GNUNET_CONTAINER_DLL_remove (env->mod_head, env->mod_tail, mod);
110}
111
112
113/**
114 * Get the modifier at the beginning of an environment and remove it.
115 *
116 * @param env
117 * @param oper
118 * @param name
119 * @param value
120 * @param value_size
121 *
122 * @return
123 */
124int
125GNUNET_PSYC_env_shift (struct GNUNET_PSYC_Environment *env,
126 enum GNUNET_PSYC_Operator *oper, const char **name,
127 const void **value, size_t *value_size)
128{
129 if (NULL == env->mod_head)
130 return GNUNET_NO;
131
132 struct GNUNET_PSYC_Modifier *mod = env->mod_head;
133 *oper = mod->oper;
134 *name = mod->name;
135 *value = mod->value;
136 *value_size = mod->value_size;
137
138 GNUNET_CONTAINER_DLL_remove (env->mod_head, env->mod_tail, mod);
139 GNUNET_free (mod);
140 env->mod_count--;
141
142 return GNUNET_YES;
143}
144
145
146/**
147 * Iterate through all modifiers in the environment.
148 *
149 * @param env The environment.
150 * @param it Iterator.
151 * @param it_cls Closure for iterator.
152 */
153void
154GNUNET_PSYC_env_iterate (const struct GNUNET_PSYC_Environment *env,
155 GNUNET_PSYC_Iterator it, void *it_cls)
156{
157 struct GNUNET_PSYC_Modifier *mod;
158 for (mod = env->mod_head; NULL != mod; mod = mod->next)
159 it (it_cls, mod->oper, mod->name, mod->value, mod->value_size);
160}
161
162
163/**
164 * Get the number of modifiers in the environment.
165 *
166 * @param env The environment.
167 *
168 * @return Number of modifiers.
169 */
170size_t
171GNUNET_PSYC_env_get_count (const struct GNUNET_PSYC_Environment *env)
172{
173 return env->mod_count;
174}
175
176
177/**
178 * Destroy an environment.
179 *
180 * @param env The environment to destroy.
181 */
182void
183GNUNET_PSYC_env_destroy (struct GNUNET_PSYC_Environment *env)
184{
185 struct GNUNET_PSYC_Modifier *mod, *prev = NULL;
186 for (mod = env->mod_head; NULL != mod; mod = mod->next)
187 {
188 if (NULL != prev)
189 GNUNET_free (prev);
190 prev = mod;
191 }
192 if (NULL != prev)
193 GNUNET_free (prev);
194
195 GNUNET_free (env);
196}
diff --git a/src/psycutil/psyc_message.c b/src/psycutil/psyc_message.c
new file mode 100644
index 0000000..a03eff4
--- /dev/null
+++ b/src/psycutil/psyc_message.c
@@ -0,0 +1,1355 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2013 GNUnet e.V.
4 *
5 * GNUnet is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Affero General Public License as published
7 * by the Free Software Foundation, either version 3 of the License,
8 * or (at your option) any later version.
9 *
10 * GNUnet is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file psycutil/psyc_message.c
23 * @brief PSYC utilities; receiving/transmitting/logging PSYC messages.
24 * @author Gabor X Toth
25 */
26
27#include <inttypes.h>
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31#include "gnunet_psyc_util_lib.h"
32#include "gnunet_psyc_service.h"
33
34#define LOG(kind,...) GNUNET_log_from (kind, "psyc-util",__VA_ARGS__)
35
36
37struct GNUNET_PSYC_TransmitHandle
38{
39 /**
40 * Client connection to service.
41 */
42 struct GNUNET_MQ_Handle *mq;
43
44 /**
45 * Message currently being received from the client.
46 */
47 struct GNUNET_MessageHeader *msg;
48
49 /**
50 * Envelope for @a msg
51 */
52 struct GNUNET_MQ_Envelope *env;
53
54 /**
55 * Callback to request next modifier from client.
56 */
57 GNUNET_PSYC_TransmitNotifyModifier notify_mod;
58
59 /**
60 * Closure for the notify callbacks.
61 */
62 void *notify_mod_cls;
63
64 /**
65 * Callback to request next data fragment from client.
66 */
67 GNUNET_PSYC_TransmitNotifyData notify_data;
68
69 /**
70 * Closure for the notify callbacks.
71 */
72 void *notify_data_cls;
73
74 /**
75 * Modifier of the environment that is currently being transmitted.
76 */
77 struct GNUNET_PSYC_Modifier *mod;
78
79 /**
80 *
81 */
82 const char *mod_value;
83
84 /**
85 * Number of bytes remaining to be transmitted from the current modifier value.
86 */
87 uint32_t mod_value_remaining;
88
89 /**
90 * State of the current message being received from client.
91 */
92 enum GNUNET_PSYC_MessageState state;
93
94 /**
95 * Number of PSYC_TRANSMIT_ACK messages we are still waiting for.
96 */
97 uint8_t acks_pending;
98
99 /**
100 * Is transmission paused?
101 */
102 uint8_t paused;
103
104 /**
105 * Are we currently transmitting a message?
106 */
107 uint8_t in_transmit;
108
109 /**
110 * Notify callback is currently being called.
111 */
112 uint8_t in_notify;
113
114};
115
116
117
118struct GNUNET_PSYC_ReceiveHandle
119{
120 /**
121 * Message callback.
122 */
123 GNUNET_PSYC_MessageCallback message_cb;
124
125 /**
126 * Message part callback.
127 */
128 GNUNET_PSYC_MessagePartCallback message_part_cb;
129
130 /**
131 * Closure for the callbacks.
132 */
133 void *cb_cls;
134
135 /**
136 * ID of the message being received from the PSYC service.
137 */
138 uint64_t message_id;
139
140 /**
141 * Public key of the slave from which a message is being received.
142 */
143 struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key;
144
145 /**
146 * State of the currently being received message from the PSYC service.
147 */
148 enum GNUNET_PSYC_MessageState state;
149
150 /**
151 * Flags for the currently being received message from the PSYC service.
152 */
153 enum GNUNET_PSYC_MessageFlags flags;
154
155 /**
156 * Expected value size for the modifier being received from the PSYC service.
157 */
158 uint32_t mod_value_size_expected;
159
160 /**
161 * Actual value size for the modifier being received from the PSYC service.
162 */
163 uint32_t mod_value_size;
164};
165
166
167/**** Messages ****/
168
169
170/**
171 * Create a PSYC message.
172 *
173 * @param method_name
174 * PSYC method for the message.
175 * @param env
176 * Environment for the message.
177 * @param data
178 * Data payload for the message.
179 * @param data_size
180 * Size of @a data.
181 *
182 * @return Message header with size information,
183 * followed by the message parts.
184 */
185struct GNUNET_PSYC_Message *
186GNUNET_PSYC_message_create (const char *method_name,
187 const struct GNUNET_PSYC_Environment *env,
188 const void *data,
189 size_t data_size)
190{
191 struct GNUNET_PSYC_Modifier *mod = NULL;
192 struct GNUNET_PSYC_MessageMethod *pmeth = NULL;
193 struct GNUNET_PSYC_MessageModifier *pmod = NULL;
194 struct GNUNET_MessageHeader *pmsg = NULL;
195 uint16_t env_size = 0;
196 if (NULL != env)
197 {
198 mod = GNUNET_PSYC_env_head (env);
199 while (NULL != mod)
200 {
201 env_size += sizeof (*pmod) + strlen (mod->name) + 1 + mod->value_size;
202 mod = mod->next;
203 }
204 }
205
206 struct GNUNET_PSYC_Message *msg;
207 uint16_t method_name_size = strlen (method_name) + 1;
208 if (method_name_size == 1)
209 return NULL;
210
211 uint16_t msg_size = sizeof (*msg) /* header */
212 + sizeof (*pmeth) + method_name_size /* method */
213 + env_size /* modifiers */
214 + ((0 < data_size) ? sizeof (*pmsg) + data_size : 0) /* data */
215 + sizeof (*pmsg); /* end of message */
216 msg = GNUNET_malloc (msg_size);
217 msg->header.size = htons (msg_size);
218 msg->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE); /* FIXME */
219
220 pmeth = (struct GNUNET_PSYC_MessageMethod *) &msg[1];
221 pmeth->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD);
222 pmeth->header.size = htons (sizeof (*pmeth) + method_name_size);
223 GNUNET_memcpy (&pmeth[1], method_name, method_name_size);
224
225 uint16_t p = sizeof (*msg) + sizeof (*pmeth) + method_name_size;
226 if (NULL != env)
227 {
228 mod = GNUNET_PSYC_env_head (env);
229 while (NULL != mod)
230 {
231 uint16_t mod_name_size = strlen (mod->name) + 1;
232 pmod = (struct GNUNET_PSYC_MessageModifier *) ((char *) msg + p);
233 pmod->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER);
234 pmod->header.size = sizeof (*pmod) + mod_name_size + mod->value_size;
235 p += pmod->header.size;
236 pmod->header.size = htons (pmod->header.size);
237
238 pmod->oper = mod->oper;
239 pmod->name_size = htons (mod_name_size);
240 pmod->value_size = htonl (mod->value_size);
241
242 GNUNET_memcpy (&pmod[1], mod->name, mod_name_size);
243 if (0 < mod->value_size)
244 GNUNET_memcpy ((char *) &pmod[1] + mod_name_size, mod->value, mod->value_size);
245
246 mod = mod->next;
247 }
248 }
249
250 if (0 < data_size)
251 {
252 pmsg = (struct GNUNET_MessageHeader *) ((char *) msg + p);
253 pmsg->size = sizeof (*pmsg) + data_size;
254 p += pmsg->size;
255 pmsg->size = htons (pmsg->size);
256 pmsg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA);
257 GNUNET_memcpy (&pmsg[1], data, data_size);
258 }
259
260 pmsg = (struct GNUNET_MessageHeader *) ((char *) msg + p);
261 pmsg->size = htons (sizeof (*pmsg));
262 pmsg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END);
263
264 GNUNET_assert (p + sizeof (*pmsg) == msg_size);
265 return msg;
266}
267
268
269void
270GNUNET_PSYC_log_message (enum GNUNET_ErrorType kind,
271 const struct GNUNET_MessageHeader *msg)
272{
273 uint16_t size = ntohs (msg->size);
274 uint16_t type = ntohs (msg->type);
275
276 GNUNET_log (kind,
277 "Message of type %d and size %u:\n",
278 type,
279 size);
280 switch (type)
281 {
282 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE:
283 {
284 const struct GNUNET_PSYC_MessageHeader *pmsg
285 = (const struct GNUNET_PSYC_MessageHeader *) msg;
286 GNUNET_log (kind,
287 "\tID: %" PRIu64 "\tflags: %x" PRIu32 "\n",
288 GNUNET_ntohll (pmsg->message_id),
289 ntohl (pmsg->flags));
290 break;
291 }
292 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
293 {
294 const struct GNUNET_PSYC_MessageMethod *meth
295 = (const struct GNUNET_PSYC_MessageMethod *) msg;
296 GNUNET_log (kind,
297 "\t%.*s\n",
298 (int) (size - sizeof (*meth)),
299 (const char *) &meth[1]);
300 break;
301 }
302 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
303 {
304 const struct GNUNET_PSYC_MessageModifier *mod
305 = (const struct GNUNET_PSYC_MessageModifier *) msg;
306 uint16_t name_size = ntohs (mod->name_size);
307 char oper = ' ' < mod->oper ? mod->oper : ' ';
308 GNUNET_log (kind,
309 "\t%c%.*s\t%.*s\n",
310 oper,
311 (int) name_size,
312 (const char *) &mod[1],
313 (int) (size - sizeof (*mod) - name_size),
314 ((const char *) &mod[1]) + name_size);
315 break;
316 }
317 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
318 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
319 GNUNET_log (kind,
320 "\t%.*s\n",
321 (int) (size - sizeof (*msg)),
322 (const char *) &msg[1]);
323 break;
324 }
325}
326
327
328/**** Transmitting messages ****/
329
330
331/**
332 * Create a transmission handle.
333 */
334struct GNUNET_PSYC_TransmitHandle *
335GNUNET_PSYC_transmit_create (struct GNUNET_MQ_Handle *mq)
336{
337 struct GNUNET_PSYC_TransmitHandle *tmit = GNUNET_new (struct GNUNET_PSYC_TransmitHandle);
338
339 tmit->mq = mq;
340 return tmit;
341}
342
343
344/**
345 * Destroy a transmission handle.
346 */
347void
348GNUNET_PSYC_transmit_destroy (struct GNUNET_PSYC_TransmitHandle *tmit)
349{
350 GNUNET_free (tmit);
351}
352
353
354/**
355 * Queue a message part for transmission.
356 *
357 * The message part is added to the current message buffer.
358 * When this buffer is full, it is added to the transmission queue.
359 *
360 * @param tmit
361 * Transmission handle.
362 * @param msg
363 * Message part, or NULL.
364 * @param tmit_now
365 * Transmit message now, or wait for buffer to fill up?
366 * #GNUNET_YES or #GNUNET_NO.
367 */
368static void
369transmit_queue_insert (struct GNUNET_PSYC_TransmitHandle *tmit,
370 const struct GNUNET_MessageHeader *msg,
371 uint8_t tmit_now)
372{
373 uint16_t size = (NULL != msg) ? ntohs (msg->size) : 0;
374
375 LOG (GNUNET_ERROR_TYPE_DEBUG,
376 "Queueing message part of type %u and size %u (tmit_now: %u)).\n",
377 NULL != msg ? ntohs (msg->type) : 0, size, tmit_now);
378
379 if (NULL != tmit->msg)
380 {
381 if (NULL == msg
382 || GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < tmit->msg->size + size)
383 {
384 /* End of message or buffer is full, add it to transmission queue
385 * and start with empty buffer */
386 tmit->msg->size = htons (tmit->msg->size);
387 GNUNET_MQ_send (tmit->mq, tmit->env);
388 tmit->env = NULL;
389 tmit->msg = NULL;
390 tmit->acks_pending++;
391 }
392 else
393 {
394 /* Message fits in current buffer, append */
395 GNUNET_memcpy ((char *) tmit->msg + tmit->msg->size, msg, size);
396 tmit->msg->size += size;
397 }
398 }
399
400 if (NULL == tmit->msg && NULL != msg)
401 {
402 /* Empty buffer, copy over message. */
403 tmit->env = GNUNET_MQ_msg_extra (tmit->msg,
404 GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD,
405 GNUNET_MESSAGE_TYPE_PSYC_MESSAGE);
406 /* store current message size in host byte order
407 * then later switch it to network byte order before sending */
408 tmit->msg->size = sizeof (*tmit->msg) + size;
409
410 GNUNET_memcpy (&tmit->msg[1], msg, size);
411 }
412
413 if (NULL != tmit->msg
414 && (GNUNET_YES == tmit_now
415 || (GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD
416 < tmit->msg->size + sizeof (struct GNUNET_MessageHeader))))
417 {
418 /* End of message or buffer is full, add it to transmission queue. */
419 tmit->msg->size = htons (tmit->msg->size);
420 GNUNET_MQ_send (tmit->mq, tmit->env);
421 tmit->env = NULL;
422 tmit->msg = NULL;
423 tmit->acks_pending++;
424 }
425}
426
427
428/**
429 * Request data from client to transmit.
430 *
431 * @param tmit Transmission handle.
432 */
433static void
434transmit_data (struct GNUNET_PSYC_TransmitHandle *tmit)
435{
436 int notify_ret = GNUNET_YES;
437 uint16_t data_size = 0;
438 char data[GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD] = "";
439 struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) data;
440 msg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA);
441
442 if (NULL != tmit->notify_data)
443 {
444 data_size = GNUNET_PSYC_DATA_MAX_PAYLOAD;
445 tmit->in_notify = GNUNET_YES;
446 notify_ret = tmit->notify_data (tmit->notify_data_cls, &data_size, &msg[1]);
447 tmit->in_notify = GNUNET_NO;
448 }
449 LOG (GNUNET_ERROR_TYPE_DEBUG,
450 "transmit_data (ret: %d, size: %u): %.*s\n",
451 notify_ret, data_size, data_size, &msg[1]);
452 switch (notify_ret)
453 {
454 case GNUNET_NO:
455 if (0 == data_size)
456 {
457 /* Transmission paused, nothing to send. */
458 tmit->paused = GNUNET_YES;
459 return;
460 }
461 break;
462
463 case GNUNET_YES:
464 tmit->state = GNUNET_PSYC_MESSAGE_STATE_END;
465 break;
466
467 default:
468 LOG (GNUNET_ERROR_TYPE_ERROR,
469 "TransmitNotifyData callback returned error when requesting data.\n");
470
471 tmit->state = GNUNET_PSYC_MESSAGE_STATE_CANCEL;
472 msg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL);
473 msg->size = htons (sizeof (*msg));
474 transmit_queue_insert (tmit, msg, GNUNET_YES);
475 tmit->in_transmit = GNUNET_NO;
476 return;
477 }
478
479 if (0 < data_size)
480 {
481 GNUNET_assert (data_size <= GNUNET_PSYC_DATA_MAX_PAYLOAD);
482 msg->size = htons (sizeof (*msg) + data_size);
483 transmit_queue_insert (tmit, msg, !notify_ret);
484 }
485
486 /* End of message. */
487 if (GNUNET_YES == notify_ret)
488 {
489 msg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END);
490 msg->size = htons (sizeof (*msg));
491 transmit_queue_insert (tmit, msg, GNUNET_YES);
492 /* FIXME: wait for ACK before setting in_transmit to no */
493 tmit->in_transmit = GNUNET_NO;
494 }
495}
496
497
498/**
499 * Request a modifier from a client to transmit.
500 *
501 * @param tmit Transmission handle.
502 */
503static void
504transmit_mod (struct GNUNET_PSYC_TransmitHandle *tmit)
505{
506 uint16_t max_data_size = 0;
507 uint16_t data_size = 0;
508 char data[GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD] = "";
509 struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) data;
510 int notify_ret = GNUNET_YES;
511
512 switch (tmit->state)
513 {
514 case GNUNET_PSYC_MESSAGE_STATE_MODIFIER:
515 {
516 struct GNUNET_PSYC_MessageModifier *mod
517 = (struct GNUNET_PSYC_MessageModifier *) msg;
518 msg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER);
519 msg->size = sizeof (struct GNUNET_PSYC_MessageModifier);
520
521 if (NULL != tmit->notify_mod)
522 {
523 max_data_size = GNUNET_PSYC_MODIFIER_MAX_PAYLOAD;
524 data_size = max_data_size;
525 tmit->in_notify = GNUNET_YES;
526 notify_ret = tmit->notify_mod (tmit->notify_mod_cls, &data_size, &mod[1],
527 &mod->oper, &mod->value_size);
528 tmit->in_notify = GNUNET_NO;
529 }
530
531 mod->name_size = strnlen ((char *) &mod[1], data_size) + 1;
532 LOG (GNUNET_ERROR_TYPE_DEBUG,
533 "transmit_mod (ret: %d, size: %u + %u): %.*s\n",
534 notify_ret, mod->name_size, mod->value_size, data_size, &mod[1]);
535 if (mod->name_size < data_size)
536 {
537 tmit->mod_value_remaining
538 = mod->value_size - (data_size - mod->name_size);
539 mod->value_size = htonl (mod->value_size);
540 mod->name_size = htons (mod->name_size);
541 }
542 else if (0 < data_size)
543 {
544 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got invalid modifier name.\n");
545 notify_ret = GNUNET_SYSERR;
546 }
547 break;
548 }
549 case GNUNET_PSYC_MESSAGE_STATE_MOD_CONT:
550 {
551 msg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT);
552 msg->size = sizeof (struct GNUNET_MessageHeader);
553
554 if (NULL != tmit->notify_mod)
555 {
556 max_data_size = GNUNET_PSYC_MOD_CONT_MAX_PAYLOAD;
557 data_size = max_data_size;
558 tmit->in_notify = GNUNET_YES;
559 notify_ret = tmit->notify_mod (tmit->notify_mod_cls,
560 &data_size, &msg[1], NULL, NULL);
561 tmit->in_notify = GNUNET_NO;
562 }
563 tmit->mod_value_remaining -= data_size;
564 LOG (GNUNET_ERROR_TYPE_DEBUG,
565 "transmit_mod (ret: %d, size: %u): %.*s\n",
566 notify_ret, data_size, data_size, &msg[1]);
567 break;
568 }
569 default:
570 GNUNET_assert (0);
571 }
572
573 switch (notify_ret)
574 {
575 case GNUNET_NO:
576 if (0 == data_size)
577 { /* Transmission paused, nothing to send. */
578 tmit->paused = GNUNET_YES;
579 return;
580 }
581 tmit->state
582 = (0 == tmit->mod_value_remaining)
583 ? GNUNET_PSYC_MESSAGE_STATE_MODIFIER
584 : GNUNET_PSYC_MESSAGE_STATE_MOD_CONT;
585 break;
586
587 case GNUNET_YES: /* End of modifiers. */
588 GNUNET_assert (0 == tmit->mod_value_remaining);
589 break;
590
591 default:
592 LOG (GNUNET_ERROR_TYPE_ERROR,
593 "TransmitNotifyModifier callback returned with error.\n");
594
595 tmit->state = GNUNET_PSYC_MESSAGE_STATE_CANCEL;
596 msg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL);
597 msg->size = htons (sizeof (*msg));
598 transmit_queue_insert (tmit, msg, GNUNET_YES);
599 tmit->in_transmit = GNUNET_NO;
600 return;
601 }
602
603 if (0 < data_size)
604 {
605 GNUNET_assert (data_size <= max_data_size);
606 msg->size = htons (msg->size + data_size);
607 transmit_queue_insert (tmit, msg, GNUNET_NO);
608 }
609
610 if (GNUNET_YES == notify_ret)
611 {
612 tmit->state = GNUNET_PSYC_MESSAGE_STATE_DATA;
613 if (0 == tmit->acks_pending)
614 transmit_data (tmit);
615 }
616 else
617 {
618 transmit_mod (tmit);
619 }
620}
621
622
623int
624transmit_notify_env (void *cls, uint16_t *data_size, void *data, uint8_t *oper,
625 uint32_t *full_value_size)
626
627{
628 struct GNUNET_PSYC_TransmitHandle *tmit = cls;
629 uint16_t name_size = 0;
630 uint32_t value_size = 0;
631 const char *value = NULL;
632
633 if (NULL != oper)
634 { /* New modifier */
635 if (NULL != tmit->mod)
636 tmit->mod = tmit->mod->next;
637 if (NULL == tmit->mod)
638 { /* No more modifiers, continue with data */
639 *data_size = 0;
640 return GNUNET_YES;
641 }
642
643 GNUNET_assert (tmit->mod->value_size < UINT32_MAX);
644 *full_value_size = tmit->mod->value_size;
645 *oper = tmit->mod->oper;
646 name_size = strlen (tmit->mod->name) + 1;
647
648 if (name_size + tmit->mod->value_size <= *data_size)
649 {
650 value_size = tmit->mod->value_size;
651 *data_size = name_size + value_size;
652 }
653 else /* full modifier does not fit in data, continuation needed */
654 {
655 value_size = *data_size - name_size;
656 tmit->mod_value = tmit->mod->value + value_size;
657 }
658
659 GNUNET_memcpy (data, tmit->mod->name, name_size);
660 GNUNET_memcpy ((char *)data + name_size, tmit->mod->value, value_size);
661 return GNUNET_NO;
662 }
663 else
664 { /* Modifier continuation */
665 GNUNET_assert (NULL != tmit->mod_value && 0 < tmit->mod_value_remaining);
666 value = tmit->mod_value;
667 if (tmit->mod_value_remaining <= *data_size)
668 {
669 value_size = tmit->mod_value_remaining;
670 tmit->mod_value = NULL;
671 }
672 else
673 {
674 value_size = *data_size;
675 tmit->mod_value += value_size;
676 }
677
678 if (*data_size < value_size)
679 {
680 LOG (GNUNET_ERROR_TYPE_DEBUG,
681 "Value in environment larger than buffer: %u < %zu\n",
682 *data_size, value_size);
683 *data_size = 0;
684 return GNUNET_NO;
685 }
686
687 *data_size = value_size;
688 GNUNET_memcpy (data, value, value_size);
689 return (NULL == tmit->mod_value) ? GNUNET_YES : GNUNET_NO;
690 }
691}
692
693
694/**
695 * Transmit a message.
696 *
697 * @param tmit
698 * Transmission handle.
699 * @param method_name
700 * Which method should be invoked.
701 * @param env
702 * Environment for the message.
703 * Should stay available until the first call to notify_data.
704 * Can be NULL if there are no modifiers or @a notify_mod is
705 * provided instead.
706 * @param notify_mod
707 * Function to call to obtain modifiers.
708 * Can be NULL if there are no modifiers or @a env is provided instead.
709 * @param notify_data
710 * Function to call to obtain fragments of the data.
711 * @param notify_cls
712 * Closure for @a notify_mod and @a notify_data.
713 * @param flags
714 * Flags for the message being transmitted.
715 *
716 * @return #GNUNET_OK if the transmission was started.
717 * #GNUNET_SYSERR if another transmission is already going on.
718 */
719int
720GNUNET_PSYC_transmit_message (struct GNUNET_PSYC_TransmitHandle *tmit,
721 const char *method_name,
722 const struct GNUNET_PSYC_Environment *env,
723 GNUNET_PSYC_TransmitNotifyModifier notify_mod,
724 GNUNET_PSYC_TransmitNotifyData notify_data,
725 void *notify_cls,
726 uint32_t flags)
727{
728 if (GNUNET_NO != tmit->in_transmit)
729 return GNUNET_SYSERR;
730 tmit->in_transmit = GNUNET_YES;
731
732 size_t size = strlen (method_name) + 1;
733 struct GNUNET_PSYC_MessageMethod *pmeth;
734
735 tmit->env = GNUNET_MQ_msg_extra (tmit->msg,
736 GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD,
737 GNUNET_MESSAGE_TYPE_PSYC_MESSAGE);
738 /* store current message size in host byte order
739 * then later switch it to network byte order before sending */
740 tmit->msg->size = sizeof (*tmit->msg) + sizeof (*pmeth) + size;
741
742 if (NULL != notify_mod)
743 {
744 tmit->notify_mod = notify_mod;
745 tmit->notify_mod_cls = notify_cls;
746 }
747 else
748 {
749 tmit->notify_mod = &transmit_notify_env;
750 tmit->notify_mod_cls = tmit;
751 if (NULL != env)
752 {
753 struct GNUNET_PSYC_Modifier mod = {};
754 mod.next = GNUNET_PSYC_env_head (env);
755 tmit->mod = &mod;
756
757 struct GNUNET_PSYC_Modifier *m = tmit->mod;
758 while (NULL != (m = m->next))
759 {
760 if (m->oper != GNUNET_PSYC_OP_SET)
761 flags |= GNUNET_PSYC_MASTER_TRANSMIT_STATE_MODIFY;
762 }
763 }
764 else
765 {
766 tmit->mod = NULL;
767 }
768 }
769
770 pmeth = (struct GNUNET_PSYC_MessageMethod *) &tmit->msg[1];
771 pmeth->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD);
772 pmeth->header.size = htons (sizeof (*pmeth) + size);
773 pmeth->flags = htonl (flags);
774 GNUNET_memcpy (&pmeth[1], method_name, size);
775
776 tmit->state = GNUNET_PSYC_MESSAGE_STATE_MODIFIER;
777 tmit->notify_data = notify_data;
778 tmit->notify_data_cls = notify_cls;
779
780 transmit_mod (tmit);
781 return GNUNET_OK;
782}
783
784
785/**
786 * Resume transmission.
787 *
788 * @param tmit Transmission handle.
789 */
790void
791GNUNET_PSYC_transmit_resume (struct GNUNET_PSYC_TransmitHandle *tmit)
792{
793 if (GNUNET_YES != tmit->in_transmit || GNUNET_NO != tmit->in_notify)
794 return;
795
796 if (0 == tmit->acks_pending)
797 {
798 tmit->paused = GNUNET_NO;
799 transmit_data (tmit);
800 }
801}
802
803
804/**
805 * Abort transmission request.
806 *
807 * @param tmit Transmission handle.
808 */
809void
810GNUNET_PSYC_transmit_cancel (struct GNUNET_PSYC_TransmitHandle *tmit)
811{
812 if (GNUNET_NO == tmit->in_transmit)
813 return;
814
815 tmit->state = GNUNET_PSYC_MESSAGE_STATE_CANCEL;
816 tmit->in_transmit = GNUNET_NO;
817 tmit->paused = GNUNET_NO;
818
819 /* FIXME */
820 struct GNUNET_MessageHeader msg;
821 msg.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL);
822 msg.size = htons (sizeof (msg));
823 transmit_queue_insert (tmit, &msg, GNUNET_YES);
824}
825
826
827/**
828 * Got acknowledgement of a transmitted message part, continue transmission.
829 *
830 * @param tmit Transmission handle.
831 */
832void
833GNUNET_PSYC_transmit_got_ack (struct GNUNET_PSYC_TransmitHandle *tmit)
834{
835 if (0 == tmit->acks_pending)
836 {
837 LOG (GNUNET_ERROR_TYPE_WARNING, "Ignoring extraneous message ACK\n");
838 GNUNET_break (0);
839 return;
840 }
841 tmit->acks_pending--;
842
843 if (GNUNET_YES == tmit->paused)
844 return;
845
846 switch (tmit->state)
847 {
848 case GNUNET_PSYC_MESSAGE_STATE_MODIFIER:
849 case GNUNET_PSYC_MESSAGE_STATE_MOD_CONT:
850 transmit_mod (tmit);
851 break;
852
853 case GNUNET_PSYC_MESSAGE_STATE_DATA:
854 transmit_data (tmit);
855 break;
856
857 case GNUNET_PSYC_MESSAGE_STATE_END:
858 case GNUNET_PSYC_MESSAGE_STATE_CANCEL:
859 break;
860
861 default:
862 LOG (GNUNET_ERROR_TYPE_DEBUG,
863 "Ignoring message ACK in state %u.\n", tmit->state);
864 }
865}
866
867
868/**** Receiving messages ****/
869
870
871/**
872 * Create handle for receiving messages.
873 */
874struct GNUNET_PSYC_ReceiveHandle *
875GNUNET_PSYC_receive_create (GNUNET_PSYC_MessageCallback message_cb,
876 GNUNET_PSYC_MessagePartCallback message_part_cb,
877 void *cb_cls)
878{
879 struct GNUNET_PSYC_ReceiveHandle *recv = GNUNET_malloc (sizeof (*recv));
880 recv->message_cb = message_cb;
881 recv->message_part_cb = message_part_cb;
882 recv->cb_cls = cb_cls;
883 return recv;
884}
885
886
887/**
888 * Destroy handle for receiving messages.
889 */
890void
891GNUNET_PSYC_receive_destroy (struct GNUNET_PSYC_ReceiveHandle *recv)
892{
893 GNUNET_free (recv);
894}
895
896
897/**
898 * Reset stored data related to the last received message.
899 */
900void
901GNUNET_PSYC_receive_reset (struct GNUNET_PSYC_ReceiveHandle *recv)
902{
903 recv->state = GNUNET_PSYC_MESSAGE_STATE_START;
904 recv->flags = 0;
905 recv->message_id = 0;
906 recv->mod_value_size = 0;
907 recv->mod_value_size_expected = 0;
908}
909
910
911static void
912recv_error (struct GNUNET_PSYC_ReceiveHandle *recv)
913{
914 if (NULL != recv->message_part_cb)
915 recv->message_part_cb (recv->cb_cls, NULL, NULL);
916
917 if (NULL != recv->message_cb)
918 recv->message_cb (recv->cb_cls, NULL);
919
920 GNUNET_PSYC_receive_reset (recv);
921}
922
923
924/**
925 * Handle incoming PSYC message.
926 *
927 * @param recv Receive handle.
928 * @param msg The message.
929 *
930 * @return #GNUNET_OK on success,
931 * #GNUNET_SYSERR on receive error.
932 */
933int
934GNUNET_PSYC_receive_message (struct GNUNET_PSYC_ReceiveHandle *recv,
935 const struct GNUNET_PSYC_MessageHeader *msg)
936{
937 uint16_t size = ntohs (msg->header.size);
938 uint32_t flags = ntohl (msg->flags);
939
940 GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG,
941 (struct GNUNET_MessageHeader *) msg);
942
943 if (GNUNET_PSYC_MESSAGE_STATE_START == recv->state)
944 {
945 recv->message_id = GNUNET_ntohll (msg->message_id);
946 recv->flags = flags;
947 recv->slave_pub_key = msg->slave_pub_key;
948 recv->mod_value_size = 0;
949 recv->mod_value_size_expected = 0;
950 }
951 else if (GNUNET_ntohll (msg->message_id) != recv->message_id)
952 {
953 // FIXME
954 LOG (GNUNET_ERROR_TYPE_WARNING,
955 "Unexpected message ID. Got: %" PRIu64 ", expected: %" PRIu64 "\n",
956 GNUNET_ntohll (msg->message_id), recv->message_id);
957 GNUNET_break_op (0);
958 recv_error (recv);
959 return GNUNET_SYSERR;
960 }
961 else if (flags != recv->flags)
962 {
963 LOG (GNUNET_ERROR_TYPE_WARNING,
964 "Unexpected message flags. Got: %lu, expected: %lu\n",
965 flags, recv->flags);
966 GNUNET_break_op (0);
967 recv_error (recv);
968 return GNUNET_SYSERR;
969 }
970
971 uint16_t pos = 0, psize = 0, ptype, size_eq, size_min;
972
973 for (pos = 0; sizeof (*msg) + pos < size; pos += psize)
974 {
975 const struct GNUNET_MessageHeader *pmsg
976 = (const struct GNUNET_MessageHeader *) ((char *) &msg[1] + pos);
977 psize = ntohs (pmsg->size);
978 ptype = ntohs (pmsg->type);
979 size_eq = size_min = 0;
980
981 if (psize < sizeof (*pmsg) || sizeof (*msg) + pos + psize > size)
982 {
983 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
984 "Dropping message of type %u with invalid size %u.\n",
985 ptype, psize);
986 recv_error (recv);
987 return GNUNET_SYSERR;
988 }
989
990 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
991 "Received message part of type %u and size %u from PSYC.\n",
992 ptype, psize);
993 GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
994
995 switch (ptype)
996 {
997 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
998 size_min = sizeof (struct GNUNET_PSYC_MessageMethod);
999 break;
1000 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
1001 size_min = sizeof (struct GNUNET_PSYC_MessageModifier);
1002 break;
1003 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
1004 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
1005 size_min = sizeof (struct GNUNET_MessageHeader);
1006 break;
1007 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
1008 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
1009 size_eq = sizeof (struct GNUNET_MessageHeader);
1010 break;
1011 default:
1012 GNUNET_break_op (0);
1013 recv_error (recv);
1014 return GNUNET_SYSERR;
1015 }
1016
1017 if (! ((0 < size_eq && psize == size_eq)
1018 || (0 < size_min && size_min <= psize)))
1019 {
1020 GNUNET_break_op (0);
1021 recv_error (recv);
1022 return GNUNET_SYSERR;
1023 }
1024
1025 switch (ptype)
1026 {
1027 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
1028 {
1029 struct GNUNET_PSYC_MessageMethod *meth
1030 = (struct GNUNET_PSYC_MessageMethod *) pmsg;
1031
1032 if (GNUNET_PSYC_MESSAGE_STATE_START != recv->state)
1033 {
1034 LOG (GNUNET_ERROR_TYPE_WARNING,
1035 "Dropping out of order message method (%u).\n",
1036 recv->state);
1037 /* It is normal to receive an incomplete message right after connecting,
1038 * but should not happen later.
1039 * FIXME: add a check for this condition.
1040 */
1041 GNUNET_break_op (0);
1042 recv_error (recv);
1043 return GNUNET_SYSERR;
1044 }
1045
1046 if ('\0' != *((char *) meth + psize - 1))
1047 {
1048 LOG (GNUNET_ERROR_TYPE_WARNING,
1049 "Dropping message with malformed method. "
1050 "Message ID: %" PRIu64 "\n", recv->message_id);
1051 GNUNET_break_op (0);
1052 recv_error (recv);
1053 return GNUNET_SYSERR;
1054 }
1055 recv->state = GNUNET_PSYC_MESSAGE_STATE_METHOD;
1056 break;
1057 }
1058 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
1059 {
1060 if (!(GNUNET_PSYC_MESSAGE_STATE_METHOD == recv->state
1061 || GNUNET_PSYC_MESSAGE_STATE_MODIFIER == recv->state
1062 || GNUNET_PSYC_MESSAGE_STATE_MOD_CONT == recv->state))
1063 {
1064 LOG (GNUNET_ERROR_TYPE_WARNING,
1065 "Dropping out of order message modifier (%u).\n",
1066 recv->state);
1067 GNUNET_break_op (0);
1068 recv_error (recv);
1069 return GNUNET_SYSERR;
1070 }
1071
1072 struct GNUNET_PSYC_MessageModifier *mod
1073 = (struct GNUNET_PSYC_MessageModifier *) pmsg;
1074
1075 uint16_t name_size = ntohs (mod->name_size);
1076 recv->mod_value_size_expected = ntohl (mod->value_size);
1077 recv->mod_value_size = psize - sizeof (*mod) - name_size;
1078
1079 if (psize < sizeof (*mod) + name_size
1080 || '\0' != *((char *) &mod[1] + name_size - 1)
1081 || recv->mod_value_size_expected < recv->mod_value_size)
1082 {
1083 LOG (GNUNET_ERROR_TYPE_WARNING, "Dropping malformed modifier.\n");
1084 GNUNET_break_op (0);
1085 recv_error (recv);
1086 return GNUNET_SYSERR;
1087 }
1088 recv->state = GNUNET_PSYC_MESSAGE_STATE_MODIFIER;
1089 break;
1090 }
1091 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
1092 {
1093 recv->mod_value_size += psize - sizeof (*pmsg);
1094
1095 if (!(GNUNET_PSYC_MESSAGE_STATE_MODIFIER == recv->state
1096 || GNUNET_PSYC_MESSAGE_STATE_MOD_CONT == recv->state)
1097 || recv->mod_value_size_expected < recv->mod_value_size)
1098 {
1099 LOG (GNUNET_ERROR_TYPE_WARNING,
1100 "Dropping out of order message modifier continuation "
1101 "!(%u == %u || %u == %u) || %lu < %lu.\n",
1102 GNUNET_PSYC_MESSAGE_STATE_MODIFIER, recv->state,
1103 GNUNET_PSYC_MESSAGE_STATE_MOD_CONT, recv->state,
1104 recv->mod_value_size_expected, recv->mod_value_size);
1105 GNUNET_break_op (0);
1106 recv_error (recv);
1107 return GNUNET_SYSERR;
1108 }
1109 break;
1110 }
1111 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
1112 {
1113 if (recv->state < GNUNET_PSYC_MESSAGE_STATE_METHOD
1114 || recv->mod_value_size_expected != recv->mod_value_size)
1115 {
1116 LOG (GNUNET_ERROR_TYPE_WARNING,
1117 "Dropping out of order message data fragment "
1118 "(%u < %u || %lu != %lu).\n",
1119 recv->state, GNUNET_PSYC_MESSAGE_STATE_METHOD,
1120 recv->mod_value_size_expected, recv->mod_value_size);
1121
1122 GNUNET_break_op (0);
1123 recv_error (recv);
1124 return GNUNET_SYSERR;
1125 }
1126 recv->state = GNUNET_PSYC_MESSAGE_STATE_DATA;
1127 break;
1128 }
1129 }
1130
1131 if (NULL != recv->message_part_cb)
1132 recv->message_part_cb (recv->cb_cls, msg, pmsg);
1133
1134 switch (ptype)
1135 {
1136 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
1137 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
1138 GNUNET_PSYC_receive_reset (recv);
1139 break;
1140 }
1141 }
1142
1143 if (NULL != recv->message_cb)
1144 recv->message_cb (recv->cb_cls, msg);
1145 return GNUNET_OK;
1146}
1147
1148
1149/**
1150 * Check if @a data contains a series of valid message parts.
1151 *
1152 * @param data_size Size of @a data.
1153 * @param data Data.
1154 * @param[out] first_ptype Type of first message part.
1155 * @param[out] last_ptype Type of last message part.
1156 *
1157 * @return Number of message parts found in @a data.
1158 * or GNUNET_SYSERR if the message contains invalid parts.
1159 */
1160int
1161GNUNET_PSYC_receive_check_parts (uint16_t data_size, const char *data,
1162 uint16_t *first_ptype, uint16_t *last_ptype)
1163{
1164 const struct GNUNET_MessageHeader *pmsg;
1165 uint16_t parts = 0, ptype = 0, psize = 0, pos = 0;
1166 if (NULL != first_ptype)
1167 *first_ptype = 0;
1168 if (NULL != last_ptype)
1169 *last_ptype = 0;
1170
1171 for (pos = 0; pos < data_size; pos += psize, parts++)
1172 {
1173 pmsg = (const struct GNUNET_MessageHeader *) (data + pos);
1174 GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
1175 psize = ntohs (pmsg->size);
1176 ptype = ntohs (pmsg->type);
1177 if (0 == parts && NULL != first_ptype)
1178 *first_ptype = ptype;
1179 if (NULL != last_ptype
1180 && *last_ptype < GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END)
1181 *last_ptype = ptype;
1182 if (psize < sizeof (*pmsg)
1183 || pos + psize > data_size
1184 || ptype < GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD
1185 || GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL < ptype)
1186 {
1187 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1188 "Invalid message part of type %u and size %u.\n",
1189 ptype, psize);
1190 return GNUNET_SYSERR;
1191 }
1192 /** @todo FIXME: check message part order */
1193 }
1194 return parts;
1195}
1196
1197
1198struct ParseMessageClosure
1199{
1200 struct GNUNET_PSYC_Environment *env;
1201 const char **method_name;
1202 const void **data;
1203 uint16_t *data_size;
1204 enum GNUNET_PSYC_MessageState msg_state;
1205};
1206
1207
1208static void
1209parse_message_part_cb (void *cls,
1210 const struct GNUNET_PSYC_MessageHeader *msg,
1211 const struct GNUNET_MessageHeader *pmsg)
1212{
1213 struct ParseMessageClosure *pmc = cls;
1214 if (NULL == pmsg)
1215 {
1216 pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_ERROR;
1217 return;
1218 }
1219
1220 switch (ntohs (pmsg->type))
1221 {
1222 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
1223 {
1224 struct GNUNET_PSYC_MessageMethod *
1225 pmeth = (struct GNUNET_PSYC_MessageMethod *) pmsg;
1226 *pmc->method_name = (const char *) &pmeth[1];
1227 pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_METHOD;
1228 break;
1229 }
1230
1231 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
1232 {
1233 struct GNUNET_PSYC_MessageModifier *
1234 pmod = (struct GNUNET_PSYC_MessageModifier *) pmsg;
1235
1236 const char *name = (const char *) &pmod[1];
1237 const void *value = name + ntohs (pmod->name_size);
1238 GNUNET_PSYC_env_add (pmc->env, pmod->oper, name, value,
1239 ntohl (pmod->value_size));
1240 pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_MODIFIER;
1241 break;
1242 }
1243
1244 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
1245 *pmc->data = &pmsg[1];
1246 *pmc->data_size = ntohs (pmsg->size) - sizeof (*pmsg);
1247 pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_DATA;
1248 break;
1249
1250 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
1251 pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_END;
1252 break;
1253
1254 default:
1255 pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_ERROR;
1256 }
1257}
1258
1259
1260/**
1261 * Parse PSYC message.
1262 *
1263 * @param msg
1264 * The PSYC message to parse.
1265 * @param[out] method_name
1266 * Pointer to the method name inside @a pmsg.
1267 * @param env
1268 * The environment for the message with a list of modifiers.
1269 * @param[out] data
1270 * Pointer to data inside @a msg.
1271 * @param[out] data_size
1272 * Size of @data is written here.
1273 *
1274 * @return #GNUNET_OK on success,
1275 * #GNUNET_SYSERR on parse error.
1276 */
1277int
1278GNUNET_PSYC_message_parse (const struct GNUNET_PSYC_MessageHeader *msg,
1279 const char **method_name,
1280 struct GNUNET_PSYC_Environment *env,
1281 const void **data,
1282 uint16_t *data_size)
1283{
1284 struct ParseMessageClosure cls;
1285 cls.env = env;
1286 cls.method_name = method_name;
1287 cls.data = data;
1288 cls.data_size = data_size;
1289
1290 struct GNUNET_PSYC_ReceiveHandle *
1291 recv = GNUNET_PSYC_receive_create (NULL, parse_message_part_cb, &cls);
1292 int ret = GNUNET_PSYC_receive_message (recv, msg);
1293 GNUNET_PSYC_receive_destroy (recv);
1294
1295 if (GNUNET_OK != ret)
1296 return GNUNET_SYSERR;
1297
1298 return (GNUNET_PSYC_MESSAGE_STATE_END == cls.msg_state)
1299 ? GNUNET_OK
1300 : GNUNET_NO;
1301}
1302
1303
1304/**
1305 * Initialize PSYC message header.
1306 */
1307void
1308GNUNET_PSYC_message_header_init (struct GNUNET_PSYC_MessageHeader *pmsg,
1309 const struct GNUNET_MULTICAST_MessageHeader *mmsg,
1310 uint32_t flags)
1311{
1312 uint16_t size = ntohs (mmsg->header.size);
1313 uint16_t psize = sizeof (*pmsg) + size - sizeof (*mmsg);
1314
1315 pmsg->header.size = htons (psize);
1316 pmsg->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE);
1317 pmsg->message_id = mmsg->message_id;
1318 pmsg->fragment_offset = mmsg->fragment_offset;
1319 pmsg->flags = htonl (flags);
1320
1321 GNUNET_memcpy (&pmsg[1], &mmsg[1], size - sizeof (*mmsg));
1322}
1323
1324
1325/**
1326 * Create a new PSYC message header from a multicast message.
1327 */
1328struct GNUNET_PSYC_MessageHeader *
1329GNUNET_PSYC_message_header_create (const struct GNUNET_MULTICAST_MessageHeader *mmsg,
1330 uint32_t flags)
1331{
1332 struct GNUNET_PSYC_MessageHeader *pmsg;
1333 uint16_t size = ntohs (mmsg->header.size);
1334 uint16_t psize = sizeof (*pmsg) + size - sizeof (*mmsg);
1335
1336 pmsg = GNUNET_malloc (psize);
1337 GNUNET_PSYC_message_header_init (pmsg, mmsg, flags);
1338 return pmsg;
1339}
1340
1341
1342/**
1343 * Create a new PSYC message header from a PSYC message.
1344 */
1345struct GNUNET_PSYC_MessageHeader *
1346GNUNET_PSYC_message_header_create_from_psyc (const struct GNUNET_PSYC_Message *msg)
1347{
1348 uint16_t msg_size = ntohs (msg->header.size);
1349 struct GNUNET_PSYC_MessageHeader *
1350 pmsg = GNUNET_malloc (sizeof (*pmsg) + msg_size - sizeof (*msg));
1351 pmsg->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE);
1352 pmsg->header.size = htons (sizeof (*pmsg) + msg_size - sizeof (*msg));
1353 GNUNET_memcpy (&pmsg[1], &msg[1], msg_size - sizeof (*msg));
1354 return pmsg;
1355}
diff --git a/src/psycutil/psyc_slicer.c b/src/psycutil/psyc_slicer.c
new file mode 100644
index 0000000..9b25d8a
--- /dev/null
+++ b/src/psycutil/psyc_slicer.c
@@ -0,0 +1,711 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2013 GNUnet e.V.
4 *
5 * GNUnet is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Affero General Public License as published
7 * by the Free Software Foundation, either version 3 of the License,
8 * or (at your option) any later version.
9 *
10 * GNUnet is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @author Gabor X Toth
23 *
24 * @file
25 * PSYC Slicer API
26 */
27
28#include <inttypes.h>
29
30#include "platform.h"
31#include "gnunet_util_lib.h"
32#include "gnunet_psyc_util_lib.h"
33
34#define LOG(kind,...) GNUNET_log_from (kind, "psyc-util-slicer",__VA_ARGS__)
35
36
37/**
38 * Handle for a try-and-slice instance.
39 */
40struct GNUNET_PSYC_Slicer
41{
42 /**
43 * Method handlers: H(method_name) -> SlicerMethodCallbacks
44 */
45 struct GNUNET_CONTAINER_MultiHashMap *method_handlers;
46
47 /**
48 * Modifier handlers: H(modifier_name) -> SlicerModifierCallbacks
49 */
50 struct GNUNET_CONTAINER_MultiHashMap *modifier_handlers;
51
52 /**
53 * Receive handle for incoming messages.
54 */
55 struct GNUNET_PSYC_ReceiveHandle *recv;
56
57 /**
58 * Currently being processed message.
59 */
60 const struct GNUNET_PSYC_MessageHeader *msg;
61
62 /**
63 * Currently being processed message part.
64 */
65 const struct GNUNET_MessageHeader *pmsg;
66
67 /**
68 * ID of currently being received message.
69 */
70 uint64_t message_id;
71
72 /**
73 * Fragment offset of currently being received message.
74 */
75 uint64_t fragment_offset;
76
77 /**
78 * Flags of currently being received message.
79 */
80 uint32_t flags;
81
82 /**
83 * Method name of currently being received message.
84 */
85 char *method_name;
86
87 /**
88 * Name of currently processed modifier.
89 */
90 char *mod_name;
91
92 /**
93 * Value of currently processed modifier.
94 */
95 char *mod_value;
96
97 /**
98 * Public key of the nym the current message originates from.
99 */
100 struct GNUNET_CRYPTO_EcdsaPublicKey nym_pub_key;
101
102 /**
103 * Size of @a method_name (including terminating \0).
104 */
105 uint16_t method_name_size;
106
107 /**
108 * Size of @a modifier_name (including terminating \0).
109 */
110 uint16_t mod_name_size;
111
112 /**
113 * Size of modifier value fragment.
114 */
115 uint16_t mod_value_size;
116
117 /**
118 * Full size of modifier value.
119 */
120 uint16_t mod_full_value_size;
121
122 /**
123 * Remaining bytes from the value of the current modifier.
124 */
125 uint16_t mod_value_remaining;
126
127 /**
128 * Operator of currently processed modifier.
129 */
130 uint8_t mod_oper;
131};
132
133
134/**
135 * Callbacks for a slicer method handler.
136 */
137struct SlicerMethodCallbacks
138{
139 GNUNET_PSYC_MessageCallback msg_cb;
140 GNUNET_PSYC_MethodCallback method_cb;
141 GNUNET_PSYC_ModifierCallback modifier_cb;
142 GNUNET_PSYC_DataCallback data_cb;
143 GNUNET_PSYC_EndOfMessageCallback eom_cb;
144 void *cls;
145};
146
147
148struct SlicerMethodRemoveClosure
149{
150 struct GNUNET_PSYC_Slicer *slicer;
151 struct SlicerMethodCallbacks rm_cbs;
152};
153
154
155/**
156 * Callbacks for a slicer method handler.
157 */
158struct SlicerModifierCallbacks
159{
160 GNUNET_PSYC_ModifierCallback modifier_cb;
161 void *cls;
162};
163
164
165struct SlicerModifierRemoveClosure
166{
167 struct GNUNET_PSYC_Slicer *slicer;
168 struct SlicerModifierCallbacks rm_cbs;
169};
170
171
172/**
173 * Call a method handler for an incoming message part.
174 */
175static int
176slicer_method_handler_notify (void *cls, const struct GNUNET_HashCode *key,
177 void *value)
178{
179 struct GNUNET_PSYC_Slicer *slicer = cls;
180 const struct GNUNET_MessageHeader *pmsg = slicer->pmsg;
181 struct SlicerMethodCallbacks *cbs = value;
182
183 uint16_t ptype = ntohs (pmsg->type);
184 switch (ptype)
185 {
186 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
187 {
188 if (NULL != cbs->msg_cb)
189 cbs->msg_cb (cbs->cls, slicer->msg);
190 if (NULL == cbs->method_cb)
191 break;
192 struct GNUNET_PSYC_MessageMethod *
193 meth = (struct GNUNET_PSYC_MessageMethod *) pmsg;
194 cbs->method_cb (cbs->cls, slicer->msg, meth, slicer->message_id,
195 slicer->method_name);
196 break;
197 }
198
199 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
200 {
201 if (NULL == cbs->modifier_cb)
202 break;
203 struct GNUNET_PSYC_MessageModifier *
204 mod = (struct GNUNET_PSYC_MessageModifier *) pmsg;
205 cbs->modifier_cb (cbs->cls, slicer->msg, &mod->header, slicer->message_id,
206 mod->oper, (const char *) &mod[1],
207 (const void *) &mod[1] + ntohs (mod->name_size),
208 ntohs (mod->header.size) - sizeof (*mod) - ntohs (mod->name_size),
209 ntohs (mod->value_size));
210 break;
211 }
212
213 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
214 {
215 if (NULL == cbs->modifier_cb)
216 break;
217 cbs->modifier_cb (cbs->cls, slicer->msg, pmsg, slicer->message_id,
218 slicer->mod_oper, slicer->mod_name, &pmsg[1],
219 ntohs (pmsg->size) - sizeof (*pmsg),
220 slicer->mod_full_value_size);
221 break;
222 }
223
224 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
225 {
226 if (NULL == cbs->data_cb)
227 break;
228 cbs->data_cb (cbs->cls, slicer->msg, pmsg, slicer->message_id,
229 &pmsg[1], ntohs (pmsg->size) - sizeof (*pmsg));
230 break;
231 }
232
233 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
234 if (NULL == cbs->eom_cb)
235 break;
236 cbs->eom_cb (cbs->cls, slicer->msg, pmsg, slicer->message_id, GNUNET_NO);
237 break;
238
239 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
240 if (NULL == cbs->eom_cb)
241 break;
242 cbs->eom_cb (cbs->cls, slicer->msg, pmsg, slicer->message_id, GNUNET_YES);
243 break;
244 }
245 return GNUNET_YES;
246}
247
248
249/**
250 * Call a method handler for an incoming message part.
251 */
252static int
253slicer_modifier_handler_notify (void *cls, const struct GNUNET_HashCode *key,
254 void *value)
255{
256 struct GNUNET_PSYC_Slicer *slicer = cls;
257 struct SlicerModifierCallbacks *cbs = value;
258
259 cbs->modifier_cb (cbs->cls, slicer->msg, slicer->pmsg, slicer->message_id,
260 slicer->mod_oper, slicer->mod_name, slicer->mod_value,
261 slicer->mod_value_size, slicer->mod_full_value_size);
262 return GNUNET_YES;
263}
264
265
266/**
267 * Process an incoming message and call matching handlers.
268 *
269 * @param slicer
270 * The slicer to use.
271 * @param msg
272 * The message as it arrived from the network.
273 */
274void
275GNUNET_PSYC_slicer_message (struct GNUNET_PSYC_Slicer *slicer,
276 const struct GNUNET_PSYC_MessageHeader *msg)
277{
278 GNUNET_PSYC_receive_message (slicer->recv, msg);
279}
280
281
282/**
283 * Process an incoming message part and call matching handlers.
284 *
285 * @param cls
286 * Closure.
287 * @param message_id
288 * ID of the message.
289 * @param flags
290 * Flags for the message.
291 * @see enum GNUNET_PSYC_MessageFlags
292 * @param msg
293 * The message part. as it arrived from the network.
294 */
295void
296GNUNET_PSYC_slicer_message_part (struct GNUNET_PSYC_Slicer *slicer,
297 const struct GNUNET_PSYC_MessageHeader *msg,
298 const struct GNUNET_MessageHeader *pmsg)
299{
300 slicer->msg = msg;
301 slicer->pmsg = pmsg;
302
303 uint64_t message_id = GNUNET_ntohll (msg->message_id);
304
305 uint16_t ptype = ntohs (pmsg->type);
306 if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == ptype)
307 {
308 struct GNUNET_PSYC_MessageMethod *
309 meth = (struct GNUNET_PSYC_MessageMethod *) pmsg;
310 slicer->method_name_size = ntohs (meth->header.size) - sizeof (*meth);
311 slicer->method_name = GNUNET_malloc (slicer->method_name_size);
312 GNUNET_memcpy (slicer->method_name, &meth[1], slicer->method_name_size);
313 slicer->message_id = message_id;
314 }
315 else
316 {
317 GNUNET_assert (message_id == slicer->message_id);
318 }
319
320 char *nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&msg->slave_pub_key);
321 LOG (GNUNET_ERROR_TYPE_DEBUG,
322 "Slicer received message of type %u and size %u, "
323 "with ID %" PRIu64 " and method %s from %s\n",
324 ptype, ntohs (pmsg->size), message_id, slicer->method_name, nym_str);
325 GNUNET_free (nym_str);
326
327 /* try-and-slice modifier */
328
329 switch (ptype)
330 {
331 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
332 {
333 struct GNUNET_PSYC_MessageModifier *
334 mod = (struct GNUNET_PSYC_MessageModifier *) pmsg;
335 slicer->mod_oper = mod->oper;
336 slicer->mod_name_size = ntohs (mod->name_size);
337 slicer->mod_name = GNUNET_malloc (slicer->mod_name_size);
338 GNUNET_memcpy (slicer->mod_name, &mod[1], slicer->mod_name_size);
339 slicer->mod_value = (char *) &mod[1] + slicer->mod_name_size;
340 slicer->mod_full_value_size = ntohs (mod->value_size);
341 slicer->mod_value_remaining = slicer->mod_full_value_size;
342 slicer->mod_value_size
343 = ntohs (mod->header.size) - sizeof (*mod) - slicer->mod_name_size;
344 // fall through
345 }
346 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
347 if (ptype == GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT)
348 {
349 slicer->mod_value = (char *) &pmsg[1];
350 slicer->mod_value_size = ntohs (pmsg->size) - sizeof (*pmsg);
351 }
352 slicer->mod_value_remaining -= slicer->mod_value_size;
353 char *name = GNUNET_malloc (slicer->mod_name_size);
354 GNUNET_memcpy (name, slicer->mod_name, slicer->mod_name_size);
355 do
356 {
357 struct GNUNET_HashCode key;
358 uint16_t name_len = strlen (name);
359 GNUNET_CRYPTO_hash (name, name_len, &key);
360 GNUNET_CONTAINER_multihashmap_get_multiple (slicer->modifier_handlers, &key,
361 slicer_modifier_handler_notify,
362 slicer);
363 char *p = strrchr (name, '_');
364 if (NULL == p)
365 break;
366 *p = '\0';
367 } while (1);
368 GNUNET_free (name);
369 }
370
371 /* try-and-slice method */
372
373 char *name = GNUNET_malloc (slicer->method_name_size);
374 GNUNET_memcpy (name, slicer->method_name, slicer->method_name_size);
375 do
376 {
377 struct GNUNET_HashCode key;
378 uint16_t name_len = strlen (name);
379 GNUNET_CRYPTO_hash (name, name_len, &key);
380 GNUNET_CONTAINER_multihashmap_get_multiple (slicer->method_handlers, &key,
381 slicer_method_handler_notify,
382 slicer);
383 char *p = strrchr (name, '_');
384 if (NULL == p)
385 break;
386 *p = '\0';
387 } while (1);
388 GNUNET_free (name);
389
390 if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
391 GNUNET_free (slicer->method_name);
392
393 if (0 == slicer->mod_value_remaining && NULL != slicer->mod_name)
394 {
395 GNUNET_free (slicer->mod_name);
396 slicer->mod_name = NULL;
397 slicer->mod_name_size = 0;
398 slicer->mod_value_size = 0;
399 slicer->mod_full_value_size = 0;
400 slicer->mod_oper = 0;
401 }
402
403 slicer->msg = NULL;
404 slicer->pmsg = NULL;
405}
406
407
408/**
409 * Create a try-and-slice instance.
410 *
411 * A slicer processes incoming messages and notifies callbacks about matching
412 * methods or modifiers encountered.
413 *
414 * @return A new try-and-slice construct.
415 */
416struct GNUNET_PSYC_Slicer *
417GNUNET_PSYC_slicer_create (void)
418{
419 struct GNUNET_PSYC_Slicer *slicer = GNUNET_malloc (sizeof (*slicer));
420 slicer->method_handlers = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
421 slicer->modifier_handlers = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
422 slicer->recv = GNUNET_PSYC_receive_create (NULL,
423 (GNUNET_PSYC_MessagePartCallback)
424 GNUNET_PSYC_slicer_message_part,
425 slicer);
426 return slicer;
427}
428
429
430/**
431 * Add a method to the try-and-slice instance.
432 *
433 * The callbacks are called for messages with a matching @a method_name prefix.
434 *
435 * @param slicer
436 * The try-and-slice instance to extend.
437 * @param method_name
438 * Name of the given method, use empty string to match all.
439 * @param method_cb
440 * Method handler invoked upon a matching message.
441 * @param modifier_cb
442 * Modifier handler, invoked after @a method_cb
443 * for each modifier in the message.
444 * @param data_cb
445 * Data handler, invoked after @a modifier_cb for each data fragment.
446 * @param eom_cb
447 * Invoked upon reaching the end of a matching message.
448 * @param cls
449 * Closure for the callbacks.
450 */
451void
452GNUNET_PSYC_slicer_method_add (struct GNUNET_PSYC_Slicer *slicer,
453 const char *method_name,
454 GNUNET_PSYC_MessageCallback msg_cb,
455 GNUNET_PSYC_MethodCallback method_cb,
456 GNUNET_PSYC_ModifierCallback modifier_cb,
457 GNUNET_PSYC_DataCallback data_cb,
458 GNUNET_PSYC_EndOfMessageCallback eom_cb,
459 void *cls)
460{
461 struct GNUNET_HashCode key;
462 GNUNET_CRYPTO_hash (method_name, strlen (method_name), &key);
463
464 struct SlicerMethodCallbacks *cbs = GNUNET_malloc (sizeof (*cbs));
465 cbs->msg_cb = msg_cb,
466 cbs->method_cb = method_cb;
467 cbs->modifier_cb = modifier_cb;
468 cbs->data_cb = data_cb;
469 cbs->eom_cb = eom_cb;
470 cbs->cls = cls;
471
472 GNUNET_CONTAINER_multihashmap_put (slicer->method_handlers, &key, cbs,
473 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
474}
475
476
477static int
478slicer_method_remove (void *cls, const struct GNUNET_HashCode *key, void *value)
479{
480 struct SlicerMethodRemoveClosure *rm_cls = cls;
481 struct GNUNET_PSYC_Slicer *slicer = rm_cls->slicer;
482 struct SlicerMethodCallbacks *rm_cbs = &rm_cls->rm_cbs;
483 struct SlicerMethodCallbacks *cbs = value;
484
485 if ((NULL == rm_cbs->msg_cb || cbs->msg_cb == rm_cbs->msg_cb)
486 && (NULL == rm_cbs->method_cb || cbs->method_cb == rm_cbs->method_cb)
487 && (NULL == rm_cbs->modifier_cb || cbs->modifier_cb == rm_cbs->modifier_cb)
488 && (NULL == rm_cbs->data_cb || cbs->data_cb == rm_cbs->data_cb)
489 && (NULL == rm_cbs->eom_cb || cbs->eom_cb == rm_cbs->eom_cb))
490 {
491 GNUNET_CONTAINER_multihashmap_remove (slicer->method_handlers, key, cbs);
492 GNUNET_free (cbs);
493 return GNUNET_NO;
494 }
495 return GNUNET_YES;
496}
497
498
499/**
500 * Remove a registered method from the try-and-slice instance.
501 *
502 * Removes one matching handler registered with the given
503 * @a method_name and callbacks.
504 *
505 * @param slicer
506 * The try-and-slice instance.
507 * @param method_name
508 * Name of the method to remove.
509 * @param method_cb
510 * Method handler.
511 * @param modifier_cb
512 * Modifier handler.
513 * @param data_cb
514 * Data handler.
515 * @param eom_cb
516 * End of message handler.
517 *
518 * @return #GNUNET_OK if a method handler was removed,
519 * #GNUNET_NO if no handler matched the given method name and callbacks.
520 */
521int
522GNUNET_PSYC_slicer_method_remove (struct GNUNET_PSYC_Slicer *slicer,
523 const char *method_name,
524 GNUNET_PSYC_MessageCallback msg_cb,
525 GNUNET_PSYC_MethodCallback method_cb,
526 GNUNET_PSYC_ModifierCallback modifier_cb,
527 GNUNET_PSYC_DataCallback data_cb,
528 GNUNET_PSYC_EndOfMessageCallback eom_cb)
529{
530 struct GNUNET_HashCode key;
531 GNUNET_CRYPTO_hash (method_name, strlen (method_name), &key);
532
533 struct SlicerMethodRemoveClosure rm_cls;
534 rm_cls.slicer = slicer;
535 struct SlicerMethodCallbacks *rm_cbs = &rm_cls.rm_cbs;
536 rm_cbs->msg_cb = msg_cb;
537 rm_cbs->method_cb = method_cb;
538 rm_cbs->modifier_cb = modifier_cb;
539 rm_cbs->data_cb = data_cb;
540 rm_cbs->eom_cb = eom_cb;
541
542 return
543 (GNUNET_SYSERR
544 == GNUNET_CONTAINER_multihashmap_get_multiple (slicer->method_handlers, &key,
545 slicer_method_remove,
546 &rm_cls))
547 ? GNUNET_NO
548 : GNUNET_OK;
549}
550
551
552/**
553 * Watch a place for changed objects.
554 *
555 * @param slicer
556 * The try-and-slice instance.
557 * @param object_filter
558 * Object prefix to match.
559 * @param modifier_cb
560 * Function to call when encountering a state modifier.
561 * @param cls
562 * Closure for callback.
563 */
564void
565GNUNET_PSYC_slicer_modifier_add (struct GNUNET_PSYC_Slicer *slicer,
566 const char *object_filter,
567 GNUNET_PSYC_ModifierCallback modifier_cb,
568 void *cls)
569{
570 struct SlicerModifierCallbacks *cbs = GNUNET_malloc (sizeof *cbs);
571 cbs->modifier_cb = modifier_cb;
572 cbs->cls = cls;
573
574 struct GNUNET_HashCode key;
575 GNUNET_CRYPTO_hash (object_filter, strlen (object_filter), &key);
576 GNUNET_CONTAINER_multihashmap_put (slicer->modifier_handlers, &key, cbs,
577 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
578}
579
580
581static int
582slicer_modifier_remove (void *cls, const struct GNUNET_HashCode *key, void *value)
583{
584 struct SlicerModifierRemoveClosure *rm_cls = cls;
585 struct GNUNET_PSYC_Slicer *slicer = rm_cls->slicer;
586 struct SlicerModifierCallbacks *rm_cbs = &rm_cls->rm_cbs;
587 struct SlicerModifierCallbacks *cbs = value;
588
589 if (cbs->modifier_cb == rm_cbs->modifier_cb)
590 {
591 GNUNET_CONTAINER_multihashmap_remove (slicer->modifier_handlers, key, cbs);
592 GNUNET_free (cbs);
593 return GNUNET_NO;
594 }
595 return GNUNET_YES;
596}
597
598
599/**
600 * Remove a registered modifier from the try-and-slice instance.
601 *
602 * Removes one matching handler registered with the given
603 * @a object_filter and @a modifier_cb.
604 *
605 * @param slicer
606 * The try-and-slice instance.
607 * @param object_filter
608 * Object prefix to match.
609 * @param modifier_cb
610 * Function to call when encountering a state modifier changes.
611 */
612int
613GNUNET_PSYC_slicer_modifier_remove (struct GNUNET_PSYC_Slicer *slicer,
614 const char *object_filter,
615 GNUNET_PSYC_ModifierCallback modifier_cb)
616{
617 struct GNUNET_HashCode key;
618 GNUNET_CRYPTO_hash (object_filter, strlen (object_filter), &key);
619
620 struct SlicerModifierRemoveClosure rm_cls;
621 rm_cls.slicer = slicer;
622 struct SlicerModifierCallbacks *rm_cbs = &rm_cls.rm_cbs;
623 rm_cbs->modifier_cb = modifier_cb;
624
625 return
626 (GNUNET_SYSERR
627 == GNUNET_CONTAINER_multihashmap_get_multiple (slicer->modifier_handlers, &key,
628 slicer_modifier_remove,
629 &rm_cls))
630 ? GNUNET_NO
631 : GNUNET_OK;
632 }
633
634
635static int
636slicer_method_free (void *cls, const struct GNUNET_HashCode *key, void *value)
637{
638 struct SlicerMethodCallbacks *cbs = value;
639 GNUNET_free (cbs);
640 return GNUNET_YES;
641}
642
643
644static int
645slicer_modifier_free (void *cls, const struct GNUNET_HashCode *key, void *value)
646{
647 struct SlicerModifierCallbacks *cbs = value;
648 GNUNET_free (cbs);
649 return GNUNET_YES;
650}
651
652
653/**
654 * Remove all registered method handlers.
655 *
656 * @param slicer
657 * Slicer to clear.
658 */
659void
660GNUNET_PSYC_slicer_method_clear (struct GNUNET_PSYC_Slicer *slicer)
661{
662 GNUNET_CONTAINER_multihashmap_iterate (slicer->method_handlers,
663 slicer_method_free, NULL);
664 GNUNET_CONTAINER_multihashmap_clear (slicer->method_handlers);
665}
666
667
668/**
669 * Remove all registered modifier handlers.
670 *
671 * @param slicer
672 * Slicer to clear.
673 */
674void
675GNUNET_PSYC_slicer_modifier_clear (struct GNUNET_PSYC_Slicer *slicer)
676{
677 GNUNET_CONTAINER_multihashmap_iterate (slicer->modifier_handlers,
678 slicer_modifier_free, NULL);
679 GNUNET_CONTAINER_multihashmap_clear (slicer->modifier_handlers);
680}
681
682
683/**
684 * Remove all registered method & modifier handlers.
685 *
686 * @param slicer
687 * Slicer to clear.
688 */
689void
690GNUNET_PSYC_slicer_clear (struct GNUNET_PSYC_Slicer *slicer)
691{
692 GNUNET_PSYC_slicer_method_clear (slicer);
693 GNUNET_PSYC_slicer_modifier_clear (slicer);
694}
695
696
697/**
698 * Destroy a given try-and-slice instance.
699 *
700 * @param slicer
701 * Slicer to destroy
702 */
703void
704GNUNET_PSYC_slicer_destroy (struct GNUNET_PSYC_Slicer *slicer)
705{
706 GNUNET_PSYC_slicer_clear (slicer);
707 GNUNET_CONTAINER_multihashmap_destroy (slicer->method_handlers);
708 GNUNET_CONTAINER_multihashmap_destroy (slicer->modifier_handlers);
709 GNUNET_PSYC_receive_destroy (slicer->recv);
710 GNUNET_free (slicer);
711}
diff --git a/src/psycutil/test_psyc_env.c b/src/psycutil/test_psyc_env.c
new file mode 100644
index 0000000..432e155
--- /dev/null
+++ b/src/psycutil/test_psyc_env.c
@@ -0,0 +1,96 @@
1/*
2 * This file is part of GNUnet.
3 * Copyright (C) 2013 GNUnet e.V.
4 *
5 * GNUnet is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Affero General Public License as published
7 * by the Free Software Foundation, either version 3 of the License,
8 * or (at your option) any later version.
9 *
10 * GNUnet is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @author Gabor X Toth
23 *
24 * @file
25 * Tests for the environment library.
26 */
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_testing_lib.h"
31#include "gnunet_psyc_util_lib.h"
32
33struct GNUNET_PSYC_Modifier mods[] = {
34 { .oper = GNUNET_PSYC_OP_SET,
35 .name = "_foo", .value = "foo", .value_size = 3 },
36
37 { .oper = GNUNET_PSYC_OP_ASSIGN,
38 .name = "_foo_bar", .value = "foo bar", .value_size = 7 },
39
40 { .oper = GNUNET_PSYC_OP_AUGMENT,
41 .name = "_foo_bar_baz", .value = "foo bar baz", .value_size = 11 }
42};
43
44struct ItCls
45{
46 size_t n;
47};
48
49int
50iterator (void *cls, enum GNUNET_PSYC_Operator oper,
51 const char *name, const char *value, uint32_t value_size)
52{
53 struct ItCls *it_cls = cls;
54 struct GNUNET_PSYC_Modifier *m = &mods[it_cls->n++];
55
56 GNUNET_assert (oper == m->oper);
57 GNUNET_assert (value_size == m->value_size);
58 GNUNET_assert (0 == memcmp (name, m->name, strlen (m->name)));
59 GNUNET_assert (0 == memcmp (value, m->value, m->value_size));
60
61 return GNUNET_YES;
62}
63
64int
65main (int argc, char *argv[])
66{
67 GNUNET_log_setup ("test-env", "WARNING", NULL);
68
69 struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
70 GNUNET_assert (NULL != env);
71 int i, len = 3;
72
73 for (i = 0; i < len; i++)
74 {
75 GNUNET_PSYC_env_add (env, mods[i].oper, mods[i].name,
76 mods[i].value, mods[i].value_size);
77 }
78
79 struct ItCls it_cls = { .n = 0 };
80 GNUNET_PSYC_env_iterate (env, iterator, &it_cls);
81 GNUNET_assert (len == it_cls.n);
82
83 for (i = 0; i < len; i++)
84 {
85 enum GNUNET_PSYC_Operator oper;
86 const char *name;
87 const void *value;
88 size_t value_size;
89 GNUNET_PSYC_env_shift (env, &oper, &name, &value, &value_size);
90 GNUNET_assert (len - i - 1 == GNUNET_PSYC_env_get_count (env));
91 }
92
93 GNUNET_PSYC_env_destroy (env);
94
95 return 0;
96}