diff options
Diffstat (limited to 'src/cadet/cadet_api.c')
-rw-r--r-- | src/cadet/cadet_api.c | 1088 |
1 files changed, 0 insertions, 1088 deletions
diff --git a/src/cadet/cadet_api.c b/src/cadet/cadet_api.c deleted file mode 100644 index c9f0722d5..000000000 --- a/src/cadet/cadet_api.c +++ /dev/null | |||
@@ -1,1088 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2011, 2017 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 | * @file cadet/cadet_api.c | ||
22 | * @brief cadet api: client implementation of cadet service | ||
23 | * @author Bartlomiej Polot | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_constants.h" | ||
29 | #include "gnunet_cadet_service.h" | ||
30 | #include "cadet.h" | ||
31 | #include "cadet_protocol.h" | ||
32 | |||
33 | #define LOG(kind, ...) GNUNET_log_from (kind, "cadet-api", __VA_ARGS__) | ||
34 | |||
35 | /** | ||
36 | * Opaque handle to the service. | ||
37 | */ | ||
38 | struct GNUNET_CADET_Handle | ||
39 | { | ||
40 | /** | ||
41 | * Message queue. | ||
42 | */ | ||
43 | struct GNUNET_MQ_Handle *mq; | ||
44 | |||
45 | /** | ||
46 | * Ports open. | ||
47 | */ | ||
48 | struct GNUNET_CONTAINER_MultiHashMap *ports; | ||
49 | |||
50 | /** | ||
51 | * Channels open. | ||
52 | */ | ||
53 | struct GNUNET_CONTAINER_MultiHashMap32 *channels; | ||
54 | |||
55 | /** | ||
56 | * child of the next channel to create (to avoid reusing IDs often) | ||
57 | */ | ||
58 | struct GNUNET_CADET_ClientChannelNumber next_ccn; | ||
59 | |||
60 | /** | ||
61 | * Configuration given by the client, in case of reconnection | ||
62 | */ | ||
63 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
64 | |||
65 | /** | ||
66 | * Task for trying to reconnect. | ||
67 | */ | ||
68 | struct GNUNET_SCHEDULER_Task *reconnect_task; | ||
69 | |||
70 | /** | ||
71 | * Time to the next reconnect in case one reconnect fails | ||
72 | */ | ||
73 | struct GNUNET_TIME_Relative reconnect_time; | ||
74 | }; | ||
75 | |||
76 | /** | ||
77 | * Opaque handle to a port. | ||
78 | */ | ||
79 | struct GNUNET_CADET_Port | ||
80 | { | ||
81 | /** | ||
82 | * Port "number" | ||
83 | */ | ||
84 | struct GNUNET_HashCode id; | ||
85 | |||
86 | /** | ||
87 | * Handle to the CADET session this port belongs to. | ||
88 | */ | ||
89 | struct GNUNET_CADET_Handle *cadet; | ||
90 | |||
91 | /** | ||
92 | * Closure for @a handler. | ||
93 | */ | ||
94 | void *cls; | ||
95 | |||
96 | /** | ||
97 | * Handler for incoming channels on this port | ||
98 | */ | ||
99 | GNUNET_CADET_ConnectEventHandler connects; | ||
100 | |||
101 | /** | ||
102 | * Closure for @ref connects | ||
103 | */ | ||
104 | void *connects_cls; | ||
105 | |||
106 | /** | ||
107 | * Window size change handler. | ||
108 | */ | ||
109 | GNUNET_CADET_WindowSizeEventHandler window_changes; | ||
110 | |||
111 | /** | ||
112 | * Handler called when an incoming channel is destroyed. | ||
113 | */ | ||
114 | GNUNET_CADET_DisconnectEventHandler disconnects; | ||
115 | |||
116 | /** | ||
117 | * Payload handlers for incoming channels. | ||
118 | */ | ||
119 | struct GNUNET_MQ_MessageHandler *handlers; | ||
120 | }; | ||
121 | |||
122 | |||
123 | /** | ||
124 | * Find the Port struct for a hash. | ||
125 | * | ||
126 | * @param h CADET handle. | ||
127 | * @param hash HashCode for the port number. | ||
128 | * @return The port handle if known, NULL otherwise. | ||
129 | */ | ||
130 | static struct GNUNET_CADET_Port * | ||
131 | find_port (const struct GNUNET_CADET_Handle *h, | ||
132 | const struct GNUNET_HashCode *hash) | ||
133 | { | ||
134 | return GNUNET_CONTAINER_multihashmap_get (h->ports, hash); | ||
135 | } | ||
136 | |||
137 | |||
138 | /** | ||
139 | * Get the channel handler for the channel specified by id from the given handle | ||
140 | * | ||
141 | * @param h Cadet handle | ||
142 | * @param ccn ID of the wanted channel | ||
143 | * @return handle to the required channel or NULL if not found | ||
144 | */ | ||
145 | static struct GNUNET_CADET_Channel * | ||
146 | find_channel (struct GNUNET_CADET_Handle *h, | ||
147 | struct GNUNET_CADET_ClientChannelNumber ccn) | ||
148 | { | ||
149 | return GNUNET_CONTAINER_multihashmap32_get (h->channels, | ||
150 | ntohl (ccn.channel_of_client)); | ||
151 | } | ||
152 | |||
153 | |||
154 | /** | ||
155 | * Create a new channel and insert it in the channel list of the cadet handle | ||
156 | * | ||
157 | * @param h Cadet handle | ||
158 | * @param ccnp pointer to desired ccn of the channel, NULL to assign one automatically. | ||
159 | * @return Handle to the created channel. | ||
160 | */ | ||
161 | static struct GNUNET_CADET_Channel * | ||
162 | create_channel (struct GNUNET_CADET_Handle *h, | ||
163 | const struct GNUNET_CADET_ClientChannelNumber *ccnp) | ||
164 | { | ||
165 | struct GNUNET_CADET_Channel *ch; | ||
166 | struct GNUNET_CADET_ClientChannelNumber ccn; | ||
167 | |||
168 | ch = GNUNET_new (struct GNUNET_CADET_Channel); | ||
169 | ch->cadet = h; | ||
170 | if (NULL == ccnp) | ||
171 | { | ||
172 | while (NULL != find_channel (h, h->next_ccn)) | ||
173 | h->next_ccn.channel_of_client = | ||
174 | htonl (GNUNET_CADET_LOCAL_CHANNEL_ID_CLI | ||
175 | | (1 + ntohl (h->next_ccn.channel_of_client))); | ||
176 | ccn = h->next_ccn; | ||
177 | } | ||
178 | else | ||
179 | { | ||
180 | ccn = *ccnp; | ||
181 | } | ||
182 | ch->ccn = ccn; | ||
183 | GNUNET_assert (GNUNET_OK == | ||
184 | GNUNET_CONTAINER_multihashmap32_put ( | ||
185 | h->channels, | ||
186 | ntohl (ch->ccn.channel_of_client), | ||
187 | ch, | ||
188 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
189 | return ch; | ||
190 | } | ||
191 | |||
192 | |||
193 | /** | ||
194 | * Destroy the specified channel. | ||
195 | * - Destroys all peers, calling the disconnect callback on each if needed | ||
196 | * - Cancels all outgoing traffic for that channel, calling respective notifys | ||
197 | * - Calls cleaner if channel was inbound | ||
198 | * - Frees all memory used | ||
199 | * | ||
200 | * @param ch Pointer to the channel. | ||
201 | * @param call_cleaner Whether to call the cleaner handler. | ||
202 | */ | ||
203 | static void | ||
204 | destroy_channel (struct GNUNET_CADET_Channel *ch) | ||
205 | { | ||
206 | struct GNUNET_CADET_Handle *h = ch->cadet; | ||
207 | |||
208 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
209 | "Destroying channel %X of %p\n", | ||
210 | htonl (ch->ccn.channel_of_client), | ||
211 | h); | ||
212 | GNUNET_assert ( | ||
213 | GNUNET_YES == | ||
214 | GNUNET_CONTAINER_multihashmap32_remove (h->channels, | ||
215 | ntohl (ch->ccn.channel_of_client), | ||
216 | ch)); | ||
217 | if (NULL != ch->mq_cont) | ||
218 | { | ||
219 | GNUNET_SCHEDULER_cancel (ch->mq_cont); | ||
220 | ch->mq_cont = NULL; | ||
221 | } | ||
222 | /* signal channel destruction */ | ||
223 | if (NULL != ch->disconnects) | ||
224 | ch->disconnects (ch->ctx, ch); | ||
225 | if (NULL != ch->pending_env) | ||
226 | GNUNET_MQ_discard (ch->pending_env); | ||
227 | GNUNET_MQ_destroy (ch->mq); | ||
228 | GNUNET_free (ch); | ||
229 | } | ||
230 | |||
231 | |||
232 | /** | ||
233 | * Reconnect to the service, retransmit all information to try to restore the | ||
234 | * original state. | ||
235 | * | ||
236 | * @param h handle to the cadet | ||
237 | */ | ||
238 | static void | ||
239 | reconnect (struct GNUNET_CADET_Handle *h); | ||
240 | |||
241 | |||
242 | /** | ||
243 | * Function called during #reconnect_cbk() to (re)open | ||
244 | * all ports that are still open. | ||
245 | * | ||
246 | * @param cls the `struct GNUNET_CADET_Handle` | ||
247 | * @param id port ID | ||
248 | * @param value a `struct GNUNET_CADET_Channel` to open | ||
249 | * @return #GNUNET_OK (continue to iterate) | ||
250 | */ | ||
251 | static int | ||
252 | open_port_cb (void *cls, const struct GNUNET_HashCode *id, void *value) | ||
253 | { | ||
254 | struct GNUNET_CADET_Handle *h = cls; | ||
255 | struct GNUNET_CADET_Port *port = value; | ||
256 | struct GNUNET_CADET_PortMessage *msg; | ||
257 | struct GNUNET_MQ_Envelope *env; | ||
258 | |||
259 | (void) id; | ||
260 | env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_CADET_LOCAL_PORT_OPEN); | ||
261 | msg->port = port->id; | ||
262 | GNUNET_MQ_send (h->mq, env); | ||
263 | return GNUNET_OK; | ||
264 | } | ||
265 | |||
266 | |||
267 | /** | ||
268 | * Reconnect callback: tries to reconnect again after a failed previous | ||
269 | * connection | ||
270 | * | ||
271 | * @param cls closure (cadet handle) | ||
272 | */ | ||
273 | static void | ||
274 | reconnect_cbk (void *cls) | ||
275 | { | ||
276 | struct GNUNET_CADET_Handle *h = cls; | ||
277 | |||
278 | h->reconnect_task = NULL; | ||
279 | h->reconnect_time = GNUNET_TIME_STD_BACKOFF (h->reconnect_time); | ||
280 | reconnect (h); | ||
281 | GNUNET_CONTAINER_multihashmap_iterate (h->ports, &open_port_cb, h); | ||
282 | } | ||
283 | |||
284 | |||
285 | /** | ||
286 | * Notify the application about a change in the window size (if needed). | ||
287 | * | ||
288 | * @param ch Channel to notify about. | ||
289 | */ | ||
290 | static void | ||
291 | notify_window_size (struct GNUNET_CADET_Channel *ch) | ||
292 | { | ||
293 | if (NULL != ch->window_changes) | ||
294 | ch->window_changes (ch->ctx, | ||
295 | ch, /* FIXME: remove 'ch'? */ | ||
296 | ch->allow_send); | ||
297 | } | ||
298 | |||
299 | |||
300 | /** | ||
301 | * Transmit the next message from our queue. | ||
302 | * | ||
303 | * @param cls Closure (channel whose mq to activate). | ||
304 | */ | ||
305 | static void | ||
306 | cadet_mq_send_now (void *cls) | ||
307 | { | ||
308 | struct GNUNET_CADET_Channel *ch = cls; | ||
309 | struct GNUNET_MQ_Envelope *env = ch->pending_env; | ||
310 | |||
311 | ch->mq_cont = NULL; | ||
312 | if (0 == ch->allow_send) | ||
313 | { | ||
314 | /* how did we get here? */ | ||
315 | GNUNET_break (0); | ||
316 | return; | ||
317 | } | ||
318 | if (NULL == env) | ||
319 | { | ||
320 | /* how did we get here? */ | ||
321 | GNUNET_break (0); | ||
322 | return; | ||
323 | } | ||
324 | ch->allow_send--; | ||
325 | ch->pending_env = NULL; | ||
326 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
327 | "Sending message on channel %s to CADET, new window size is %u\n", | ||
328 | GNUNET_i2s (&ch->peer), | ||
329 | ch->allow_send); | ||
330 | GNUNET_MQ_send (ch->cadet->mq, env); | ||
331 | GNUNET_MQ_impl_send_continue (ch->mq); | ||
332 | } | ||
333 | |||
334 | |||
335 | /** | ||
336 | * Implement sending functionality of a message queue for | ||
337 | * us sending messages to a peer. | ||
338 | * | ||
339 | * Encapsulates the payload message in a #GNUNET_CADET_LocalData message | ||
340 | * in order to label the message with the channel ID and send the | ||
341 | * encapsulated message to the service. | ||
342 | * | ||
343 | * @param mq the message queue | ||
344 | * @param msg the message to send | ||
345 | * @param impl_state state of the implementation | ||
346 | */ | ||
347 | static void | ||
348 | cadet_mq_send_impl (struct GNUNET_MQ_Handle *mq, | ||
349 | const struct GNUNET_MessageHeader *msg, | ||
350 | void *impl_state) | ||
351 | { | ||
352 | struct GNUNET_CADET_Channel *ch = impl_state; | ||
353 | struct GNUNET_CADET_Handle *h = ch->cadet; | ||
354 | uint16_t msize; | ||
355 | struct GNUNET_MQ_Envelope *orig_env; | ||
356 | struct GNUNET_MQ_Envelope *env; | ||
357 | struct GNUNET_CADET_LocalData *cadet_msg; | ||
358 | enum GNUNET_MQ_PriorityPreferences pp; | ||
359 | |||
360 | if (NULL == h->mq) | ||
361 | { | ||
362 | /* We're currently reconnecting, pretend this worked */ | ||
363 | GNUNET_MQ_impl_send_continue (mq); | ||
364 | return; | ||
365 | } | ||
366 | orig_env = GNUNET_MQ_get_current_envelope (mq); | ||
367 | pp = GNUNET_MQ_env_get_options (orig_env); | ||
368 | |||
369 | /* check message size for sanity */ | ||
370 | msize = ntohs (msg->size); | ||
371 | if (msize > GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE) | ||
372 | { | ||
373 | GNUNET_break (0); | ||
374 | GNUNET_MQ_impl_send_continue (mq); | ||
375 | return; | ||
376 | } | ||
377 | env = GNUNET_MQ_msg_nested_mh (cadet_msg, | ||
378 | GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA, | ||
379 | msg); | ||
380 | cadet_msg->ccn = ch->ccn; | ||
381 | cadet_msg->pp = htonl ((uint32_t) pp); | ||
382 | GNUNET_assert (NULL == ch->pending_env); | ||
383 | ch->pending_env = env; | ||
384 | if (0 < ch->allow_send) | ||
385 | ch->mq_cont = GNUNET_SCHEDULER_add_now (&cadet_mq_send_now, ch); | ||
386 | } | ||
387 | |||
388 | |||
389 | /** | ||
390 | * Handle destruction of a message queue. Implementations must not | ||
391 | * free @a mq, but should take care of @a impl_state. | ||
392 | * | ||
393 | * @param mq the message queue to destroy | ||
394 | * @param impl_state state of the implementation | ||
395 | */ | ||
396 | static void | ||
397 | cadet_mq_destroy_impl (struct GNUNET_MQ_Handle *mq, void *impl_state) | ||
398 | { | ||
399 | struct GNUNET_CADET_Channel *ch = impl_state; | ||
400 | |||
401 | GNUNET_assert (mq == ch->mq); | ||
402 | ch->mq = NULL; | ||
403 | } | ||
404 | |||
405 | |||
406 | /** | ||
407 | * We had an error processing a message we forwarded from a peer to | ||
408 | * the CADET service. We should just complain about it but otherwise | ||
409 | * continue processing. | ||
410 | * | ||
411 | * @param cls closure with our `struct GNUNET_CADET_Channel` | ||
412 | * @param error error code | ||
413 | */ | ||
414 | static void | ||
415 | cadet_mq_error_handler (void *cls, enum GNUNET_MQ_Error error) | ||
416 | { | ||
417 | struct GNUNET_CADET_Channel *ch = cls; | ||
418 | |||
419 | if (GNUNET_MQ_ERROR_NO_MATCH == error) | ||
420 | { | ||
421 | /* Got a message we did not understand, still try to continue! */ | ||
422 | GNUNET_break_op (0); | ||
423 | GNUNET_CADET_receive_done (ch); | ||
424 | } | ||
425 | else | ||
426 | { | ||
427 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
428 | "MQ error in communication with CADET: %d\n", | ||
429 | error); | ||
430 | if (NULL != ch->disconnects) | ||
431 | ch->disconnects (ch->ctx, ch); | ||
432 | GNUNET_CADET_channel_destroy (ch); | ||
433 | } | ||
434 | } | ||
435 | |||
436 | |||
437 | /** | ||
438 | * Implementation function that cancels the currently sent message. | ||
439 | * Should basically undo whatever #mq_send_impl() did. | ||
440 | * | ||
441 | * @param mq message queue | ||
442 | * @param impl_state state specific to the implementation | ||
443 | */ | ||
444 | static void | ||
445 | cadet_mq_cancel_impl (struct GNUNET_MQ_Handle *mq, void *impl_state) | ||
446 | { | ||
447 | struct GNUNET_CADET_Channel *ch = impl_state; | ||
448 | |||
449 | (void) mq; | ||
450 | GNUNET_assert (NULL != ch->pending_env); | ||
451 | GNUNET_MQ_discard (ch->pending_env); | ||
452 | ch->pending_env = NULL; | ||
453 | if (NULL != ch->mq_cont) | ||
454 | { | ||
455 | GNUNET_SCHEDULER_cancel (ch->mq_cont); | ||
456 | ch->mq_cont = NULL; | ||
457 | } | ||
458 | } | ||
459 | |||
460 | |||
461 | /** | ||
462 | * Process the new channel notification and add it to the channels in the handle | ||
463 | * | ||
464 | * @param h The cadet handle | ||
465 | * @param msg A message with the details of the new incoming channel | ||
466 | */ | ||
467 | static void | ||
468 | handle_channel_created ( | ||
469 | void *cls, | ||
470 | const struct GNUNET_CADET_LocalChannelCreateMessage *msg) | ||
471 | { | ||
472 | struct GNUNET_CADET_Handle *h = cls; | ||
473 | struct GNUNET_CADET_Channel *ch; | ||
474 | struct GNUNET_CADET_Port *port; | ||
475 | const struct GNUNET_HashCode *port_number; | ||
476 | struct GNUNET_CADET_ClientChannelNumber ccn; | ||
477 | |||
478 | ccn = msg->ccn; | ||
479 | port_number = &msg->port; | ||
480 | if (ntohl (ccn.channel_of_client) >= GNUNET_CADET_LOCAL_CHANNEL_ID_CLI) | ||
481 | { | ||
482 | GNUNET_break (0); | ||
483 | return; | ||
484 | } | ||
485 | port = find_port (h, port_number); | ||
486 | if (NULL == port) | ||
487 | { | ||
488 | /* We could have closed the port but the service didn't know about it yet | ||
489 | * This is not an error. | ||
490 | */ | ||
491 | struct GNUNET_CADET_LocalChannelDestroyMessage *d_msg; | ||
492 | struct GNUNET_MQ_Envelope *env; | ||
493 | |||
494 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
495 | "No handler for incoming channel %X (on port %s, recently closed?)\n", | ||
496 | ntohl (ccn.channel_of_client), | ||
497 | GNUNET_h2s (port_number)); | ||
498 | env = | ||
499 | GNUNET_MQ_msg (d_msg, GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_DESTROY); | ||
500 | d_msg->ccn = msg->ccn; | ||
501 | GNUNET_MQ_send (h->mq, env); | ||
502 | return; | ||
503 | } | ||
504 | |||
505 | ch = create_channel (h, &ccn); | ||
506 | ch->peer = msg->peer; | ||
507 | ch->incoming_port = port; | ||
508 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
509 | "Creating incoming channel %X [%s] %p\n", | ||
510 | ntohl (ccn.channel_of_client), | ||
511 | GNUNET_h2s (port_number), | ||
512 | ch); | ||
513 | |||
514 | GNUNET_assert (NULL != port->connects); | ||
515 | ch->window_changes = port->window_changes; | ||
516 | ch->disconnects = port->disconnects; | ||
517 | ch->mq = GNUNET_MQ_queue_for_callbacks (&cadet_mq_send_impl, | ||
518 | &cadet_mq_destroy_impl, | ||
519 | &cadet_mq_cancel_impl, | ||
520 | ch, | ||
521 | port->handlers, | ||
522 | &cadet_mq_error_handler, | ||
523 | ch); | ||
524 | ch->ctx = port->connects (port->cls, ch, &msg->peer); | ||
525 | GNUNET_MQ_set_handlers_closure (ch->mq, ch->ctx); | ||
526 | } | ||
527 | |||
528 | |||
529 | /** | ||
530 | * Process the channel destroy notification and free associated resources | ||
531 | * | ||
532 | * @param h The cadet handle | ||
533 | * @param msg A message with the details of the channel being destroyed | ||
534 | */ | ||
535 | static void | ||
536 | handle_channel_destroy ( | ||
537 | void *cls, | ||
538 | const struct GNUNET_CADET_LocalChannelDestroyMessage *msg) | ||
539 | { | ||
540 | struct GNUNET_CADET_Handle *h = cls; | ||
541 | struct GNUNET_CADET_Channel *ch; | ||
542 | |||
543 | ch = find_channel (h, msg->ccn); | ||
544 | if (NULL == ch) | ||
545 | { | ||
546 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
547 | "Received channel destroy for unknown channel %X from CADET service (recently close?)\n", | ||
548 | ntohl (msg->ccn.channel_of_client)); | ||
549 | return; | ||
550 | } | ||
551 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
552 | "Received channel destroy for channel %X from CADET service\n", | ||
553 | ntohl (msg->ccn.channel_of_client)); | ||
554 | destroy_channel (ch); | ||
555 | } | ||
556 | |||
557 | |||
558 | /** | ||
559 | * Check that message received from CADET service is well-formed. | ||
560 | * | ||
561 | * @param cls the `struct GNUNET_CADET_Handle` | ||
562 | * @param message the message we got | ||
563 | * @return #GNUNET_OK if the message is well-formed, | ||
564 | * #GNUNET_SYSERR otherwise | ||
565 | */ | ||
566 | static int | ||
567 | check_local_data (void *cls, const struct GNUNET_CADET_LocalData *message) | ||
568 | { | ||
569 | uint16_t size; | ||
570 | |||
571 | (void) cls; | ||
572 | size = ntohs (message->header.size); | ||
573 | if (sizeof(*message) + sizeof(struct GNUNET_MessageHeader) > size) | ||
574 | { | ||
575 | GNUNET_break (0); | ||
576 | return GNUNET_SYSERR; | ||
577 | } | ||
578 | return GNUNET_OK; | ||
579 | } | ||
580 | |||
581 | |||
582 | /** | ||
583 | * Process the incoming data packets, call appropriate handlers. | ||
584 | * | ||
585 | * @param h The cadet handle | ||
586 | * @param message A message encapsulating the data | ||
587 | */ | ||
588 | static void | ||
589 | handle_local_data (void *cls, const struct GNUNET_CADET_LocalData *message) | ||
590 | { | ||
591 | struct GNUNET_CADET_Handle *h = cls; | ||
592 | const struct GNUNET_MessageHeader *payload; | ||
593 | struct GNUNET_CADET_Channel *ch; | ||
594 | uint16_t type; | ||
595 | int fwd; | ||
596 | |||
597 | ch = find_channel (h, message->ccn); | ||
598 | if (NULL == ch) | ||
599 | { | ||
600 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
601 | "Unknown channel %X for incoming data (recently closed?)\n", | ||
602 | ntohl (message->ccn.channel_of_client)); | ||
603 | return; | ||
604 | } | ||
605 | |||
606 | payload = (const struct GNUNET_MessageHeader *) &message[1]; | ||
607 | type = ntohs (payload->type); | ||
608 | fwd = ntohl (ch->ccn.channel_of_client) <= GNUNET_CADET_LOCAL_CHANNEL_ID_CLI; | ||
609 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
610 | "Got a %s data on channel %s [%X] of type %u\n", | ||
611 | fwd ? "FWD" : "BWD", | ||
612 | GNUNET_i2s (&ch->peer), | ||
613 | ntohl (message->ccn.channel_of_client), | ||
614 | type); | ||
615 | GNUNET_MQ_inject_message (ch->mq, payload); | ||
616 | } | ||
617 | |||
618 | |||
619 | /** | ||
620 | * Process a local ACK message, enabling the client to send | ||
621 | * more data to the service. | ||
622 | * | ||
623 | * @param h Cadet handle. | ||
624 | * @param message Message itself. | ||
625 | */ | ||
626 | static void | ||
627 | handle_local_ack (void *cls, const struct GNUNET_CADET_LocalAck *message) | ||
628 | { | ||
629 | struct GNUNET_CADET_Handle *h = cls; | ||
630 | struct GNUNET_CADET_Channel *ch; | ||
631 | |||
632 | ch = find_channel (h, message->ccn); | ||
633 | if (NULL == ch) | ||
634 | { | ||
635 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
636 | "ACK on unknown channel %X\n", | ||
637 | ntohl (message->ccn.channel_of_client)); | ||
638 | return; | ||
639 | } | ||
640 | ch->allow_send++; | ||
641 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
642 | "Got an ACK on mq channel %X (peer %s); new window size is %u!\n", | ||
643 | ntohl (ch->ccn.channel_of_client), | ||
644 | GNUNET_i2s (&ch->peer), | ||
645 | ch->allow_send); | ||
646 | if (NULL == ch->pending_env) | ||
647 | { | ||
648 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
649 | "Got an ACK on mq channel %X, allow send now %u!\n", | ||
650 | ntohl (ch->ccn.channel_of_client), | ||
651 | ch->allow_send); | ||
652 | notify_window_size (ch); | ||
653 | return; | ||
654 | } | ||
655 | if (NULL != ch->mq_cont) | ||
656 | return; /* already working on it! */ | ||
657 | ch->mq_cont = GNUNET_SCHEDULER_add_now (&cadet_mq_send_now, ch); | ||
658 | } | ||
659 | |||
660 | |||
661 | /** | ||
662 | * Function called during #GNUNET_CADET_disconnect() to destroy | ||
663 | * all channels that are still open. | ||
664 | * | ||
665 | * @param cls the `struct GNUNET_CADET_Handle` | ||
666 | * @param cid chanenl ID | ||
667 | * @param value a `struct GNUNET_CADET_Channel` to destroy | ||
668 | * @return #GNUNET_OK (continue to iterate) | ||
669 | */ | ||
670 | static int | ||
671 | destroy_channel_cb (void *cls, uint32_t cid, void *value) | ||
672 | { | ||
673 | /* struct GNUNET_CADET_Handle *handle = cls; */ | ||
674 | struct GNUNET_CADET_Channel *ch = value; | ||
675 | |||
676 | (void) cls; | ||
677 | (void) cid; | ||
678 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
679 | "Destroying channel due to GNUNET_CADET_disconnect()\n"); | ||
680 | destroy_channel (ch); | ||
681 | return GNUNET_OK; | ||
682 | } | ||
683 | |||
684 | |||
685 | /** | ||
686 | * Generic error handler, called with the appropriate error code and | ||
687 | * the same closure specified at the creation of the message queue. | ||
688 | * Not every message queue implementation supports an error handler. | ||
689 | * | ||
690 | * @param cls closure, a `struct GNUNET_CORE_Handle *` | ||
691 | * @param error error code | ||
692 | */ | ||
693 | static void | ||
694 | handle_mq_error (void *cls, enum GNUNET_MQ_Error error) | ||
695 | { | ||
696 | struct GNUNET_CADET_Handle *h = cls; | ||
697 | |||
698 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MQ ERROR: %u\n", error); | ||
699 | GNUNET_CONTAINER_multihashmap32_iterate (h->channels, &destroy_channel_cb, h); | ||
700 | GNUNET_MQ_destroy (h->mq); | ||
701 | h->mq = NULL; | ||
702 | GNUNET_assert (NULL == h->reconnect_task); | ||
703 | h->reconnect_task = | ||
704 | GNUNET_SCHEDULER_add_delayed (h->reconnect_time, &reconnect_cbk, h); | ||
705 | } | ||
706 | |||
707 | |||
708 | /** | ||
709 | * Reconnect to the service, retransmit all information to try to restore the | ||
710 | * original state. | ||
711 | * | ||
712 | * @param h handle to the cadet | ||
713 | */ | ||
714 | static void | ||
715 | reconnect (struct GNUNET_CADET_Handle *h) | ||
716 | { | ||
717 | struct GNUNET_MQ_MessageHandler handlers[] = | ||
718 | { GNUNET_MQ_hd_fixed_size (channel_created, | ||
719 | GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_CREATE, | ||
720 | struct GNUNET_CADET_LocalChannelCreateMessage, | ||
721 | h), | ||
722 | GNUNET_MQ_hd_fixed_size (channel_destroy, | ||
723 | GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_DESTROY, | ||
724 | struct GNUNET_CADET_LocalChannelDestroyMessage, | ||
725 | h), | ||
726 | GNUNET_MQ_hd_var_size (local_data, | ||
727 | GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA, | ||
728 | struct GNUNET_CADET_LocalData, | ||
729 | h), | ||
730 | GNUNET_MQ_hd_fixed_size (local_ack, | ||
731 | GNUNET_MESSAGE_TYPE_CADET_LOCAL_ACK, | ||
732 | struct GNUNET_CADET_LocalAck, | ||
733 | h), | ||
734 | GNUNET_MQ_handler_end () }; | ||
735 | |||
736 | GNUNET_assert (NULL == h->mq); | ||
737 | h->mq = | ||
738 | GNUNET_CLIENT_connect (h->cfg, "cadet", handlers, &handle_mq_error, h); | ||
739 | } | ||
740 | |||
741 | |||
742 | /** | ||
743 | * Function called during #GNUNET_CADET_disconnect() to destroy | ||
744 | * all ports that are still open. | ||
745 | * | ||
746 | * @param cls the `struct GNUNET_CADET_Handle` | ||
747 | * @param id port ID | ||
748 | * @param value a `struct GNUNET_CADET_Channel` to destroy | ||
749 | * @return #GNUNET_OK (continue to iterate) | ||
750 | */ | ||
751 | static int | ||
752 | destroy_port_cb (void *cls, const struct GNUNET_HashCode *id, void *value) | ||
753 | { | ||
754 | /* struct GNUNET_CADET_Handle *handle = cls; */ | ||
755 | struct GNUNET_CADET_Port *port = value; | ||
756 | |||
757 | (void) cls; | ||
758 | (void) id; | ||
759 | /* This is a warning, the app should have cleanly closed all open ports */ | ||
760 | GNUNET_break (0); | ||
761 | GNUNET_CADET_close_port (port); | ||
762 | return GNUNET_OK; | ||
763 | } | ||
764 | |||
765 | |||
766 | /** | ||
767 | * Disconnect from the cadet service. All channels will be destroyed. All channel | ||
768 | * disconnect callbacks will be called on any still connected peers, notifying | ||
769 | * about their disconnection. The registered inbound channel cleaner will be | ||
770 | * called should any inbound channels still exist. | ||
771 | * | ||
772 | * @param handle connection to cadet to disconnect | ||
773 | */ | ||
774 | void | ||
775 | GNUNET_CADET_disconnect (struct GNUNET_CADET_Handle *handle) | ||
776 | { | ||
777 | GNUNET_CONTAINER_multihashmap_iterate (handle->ports, | ||
778 | &destroy_port_cb, | ||
779 | handle); | ||
780 | GNUNET_CONTAINER_multihashmap_destroy (handle->ports); | ||
781 | handle->ports = NULL; | ||
782 | GNUNET_CONTAINER_multihashmap32_iterate (handle->channels, | ||
783 | &destroy_channel_cb, | ||
784 | handle); | ||
785 | GNUNET_CONTAINER_multihashmap32_destroy (handle->channels); | ||
786 | handle->channels = NULL; | ||
787 | if (NULL != handle->mq) | ||
788 | { | ||
789 | GNUNET_MQ_destroy (handle->mq); | ||
790 | handle->mq = NULL; | ||
791 | } | ||
792 | if (NULL != handle->reconnect_task) | ||
793 | { | ||
794 | GNUNET_SCHEDULER_cancel (handle->reconnect_task); | ||
795 | handle->reconnect_task = NULL; | ||
796 | } | ||
797 | GNUNET_free (handle); | ||
798 | } | ||
799 | |||
800 | |||
801 | /** | ||
802 | * Close a port opened with @a GNUNET_CADET_open_port(). | ||
803 | * The @a new_channel callback will no longer be called. | ||
804 | * | ||
805 | * @param p Port handle. | ||
806 | */ | ||
807 | void | ||
808 | GNUNET_CADET_close_port (struct GNUNET_CADET_Port *p) | ||
809 | { | ||
810 | GNUNET_assert ( | ||
811 | GNUNET_YES == | ||
812 | GNUNET_CONTAINER_multihashmap_remove (p->cadet->ports, &p->id, p)); | ||
813 | if (NULL != p->cadet->mq) | ||
814 | { | ||
815 | struct GNUNET_CADET_PortMessage *msg; | ||
816 | struct GNUNET_MQ_Envelope *env; | ||
817 | |||
818 | env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_CADET_LOCAL_PORT_CLOSE); | ||
819 | msg->port = p->id; | ||
820 | GNUNET_MQ_send (p->cadet->mq, env); | ||
821 | } | ||
822 | GNUNET_free (p->handlers); | ||
823 | GNUNET_free (p); | ||
824 | } | ||
825 | |||
826 | |||
827 | /** | ||
828 | * Destroy an existing channel. | ||
829 | * | ||
830 | * The existing end callback for the channel will NOT be called. | ||
831 | * Any pending outgoing messages will be sent but no incoming messages will be | ||
832 | * accepted and no data callbacks will be called. | ||
833 | * | ||
834 | * @param channel Channel handle, becomes invalid after this call. | ||
835 | */ | ||
836 | void | ||
837 | GNUNET_CADET_channel_destroy (struct GNUNET_CADET_Channel *channel) | ||
838 | { | ||
839 | struct GNUNET_CADET_Handle *h = channel->cadet; | ||
840 | struct GNUNET_CADET_LocalChannelDestroyMessage *msg; | ||
841 | struct GNUNET_MQ_Envelope *env; | ||
842 | |||
843 | if (NULL != h->mq) | ||
844 | { | ||
845 | env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_DESTROY); | ||
846 | msg->ccn = channel->ccn; | ||
847 | GNUNET_MQ_send (h->mq, env); | ||
848 | } | ||
849 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
850 | "Destroying channel due to GNUNET_CADET_channel_destroy()\n"); | ||
851 | channel->disconnects = NULL; | ||
852 | destroy_channel (channel); | ||
853 | } | ||
854 | |||
855 | |||
856 | /** | ||
857 | * Get information about a channel. | ||
858 | * | ||
859 | * @param channel Channel handle. | ||
860 | * @param option Query (GNUNET_CADET_OPTION_*). | ||
861 | * @param ... dependent on option, currently not used | ||
862 | * | ||
863 | * @return Union with an answer to the query. | ||
864 | */ | ||
865 | const union GNUNET_CADET_ChannelInfo * | ||
866 | GNUNET_CADET_channel_get_info (struct GNUNET_CADET_Channel *channel, | ||
867 | enum GNUNET_CADET_ChannelInfoOption option, | ||
868 | ...) | ||
869 | { | ||
870 | switch (option) | ||
871 | { | ||
872 | case GNUNET_CADET_OPTION_PEER: | ||
873 | return (const union GNUNET_CADET_ChannelInfo *) &channel->peer; | ||
874 | |||
875 | default: | ||
876 | GNUNET_break (0); | ||
877 | return NULL; | ||
878 | } | ||
879 | } | ||
880 | |||
881 | |||
882 | /** | ||
883 | * Send an ack on the channel to confirm the processing of a message. | ||
884 | * | ||
885 | * @param ch Channel on which to send the ACK. | ||
886 | */ | ||
887 | void | ||
888 | GNUNET_CADET_receive_done (struct GNUNET_CADET_Channel *channel) | ||
889 | { | ||
890 | struct GNUNET_CADET_LocalAck *msg; | ||
891 | struct GNUNET_MQ_Envelope *env; | ||
892 | |||
893 | env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_CADET_LOCAL_ACK); | ||
894 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
895 | "Sending ACK on channel %X\n", | ||
896 | ntohl (channel->ccn.channel_of_client)); | ||
897 | msg->ccn = channel->ccn; | ||
898 | GNUNET_MQ_send (channel->cadet->mq, env); | ||
899 | } | ||
900 | |||
901 | |||
902 | /** | ||
903 | * Connect to the MQ-based cadet service. | ||
904 | * | ||
905 | * @param cfg Configuration to use. | ||
906 | * | ||
907 | * @return Handle to the cadet service NULL on error. | ||
908 | */ | ||
909 | struct GNUNET_CADET_Handle * | ||
910 | GNUNET_CADET_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
911 | { | ||
912 | struct GNUNET_CADET_Handle *h; | ||
913 | |||
914 | LOG (GNUNET_ERROR_TYPE_DEBUG, "GNUNET_CADET_connect()\n"); | ||
915 | h = GNUNET_new (struct GNUNET_CADET_Handle); | ||
916 | h->cfg = cfg; | ||
917 | h->ports = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_YES); | ||
918 | h->channels = GNUNET_CONTAINER_multihashmap32_create (4); | ||
919 | reconnect (h); | ||
920 | if (NULL == h->mq) | ||
921 | { | ||
922 | GNUNET_break (0); | ||
923 | GNUNET_CADET_disconnect (h); | ||
924 | return NULL; | ||
925 | } | ||
926 | h->next_ccn.channel_of_client = htonl (GNUNET_CADET_LOCAL_CHANNEL_ID_CLI); | ||
927 | return h; | ||
928 | } | ||
929 | |||
930 | |||
931 | /** | ||
932 | * Function to return link to AGPL source upon request. | ||
933 | * | ||
934 | * @param cls closure with the identification of the client | ||
935 | * @param msg AGPL request | ||
936 | */ | ||
937 | static void | ||
938 | return_agpl (void *cls, const struct GNUNET_MessageHeader *msg) | ||
939 | { | ||
940 | struct GNUNET_SERVICE_Client *client = cls; | ||
941 | struct GNUNET_MQ_Handle *mq; | ||
942 | struct GNUNET_MQ_Envelope *env; | ||
943 | struct GNUNET_MessageHeader *res; | ||
944 | size_t slen; | ||
945 | const struct GNUNET_OS_ProjectData *pd = GNUNET_OS_project_data_get (); | ||
946 | |||
947 | (void) msg; | ||
948 | slen = strlen (pd->agpl_url) + 1; | ||
949 | env = GNUNET_MQ_msg_extra (res, GNUNET_MESSAGE_TYPE_RESPONSE_AGPL, slen); | ||
950 | memcpy (&res[1], GNUNET_AGPL_URL, slen); | ||
951 | mq = GNUNET_SERVICE_client_get_mq (client); | ||
952 | GNUNET_MQ_send (mq, env); | ||
953 | GNUNET_SERVICE_client_continue (client); | ||
954 | } | ||
955 | |||
956 | |||
957 | /** | ||
958 | * Open a port to receive incoming MQ-based channels. | ||
959 | * | ||
960 | * @param h CADET handle. | ||
961 | * @param port Hash identifying the port. | ||
962 | * @param connects Function called when an incoming channel is connected. | ||
963 | * @param connects_cls Closure for the @a connects handler. | ||
964 | * @param window_changes Function called when the transmit window size changes. | ||
965 | * @param disconnects Function called when a channel is disconnected. | ||
966 | * @param handlers Callbacks for messages we care about, NULL-terminated. | ||
967 | * @return Port handle, NULL if port is in use | ||
968 | */ | ||
969 | struct GNUNET_CADET_Port * | ||
970 | GNUNET_CADET_open_port (struct GNUNET_CADET_Handle *h, | ||
971 | const struct GNUNET_HashCode *port, | ||
972 | GNUNET_CADET_ConnectEventHandler connects, | ||
973 | void *connects_cls, | ||
974 | GNUNET_CADET_WindowSizeEventHandler window_changes, | ||
975 | GNUNET_CADET_DisconnectEventHandler disconnects, | ||
976 | const struct GNUNET_MQ_MessageHandler *handlers) | ||
977 | { | ||
978 | struct GNUNET_CADET_Port *p; | ||
979 | const struct GNUNET_OS_ProjectData *pd = GNUNET_OS_project_data_get (); | ||
980 | |||
981 | GNUNET_assert (NULL != connects); | ||
982 | GNUNET_assert (NULL != disconnects); | ||
983 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
984 | "Listening to CADET port %s\n", | ||
985 | GNUNET_h2s (port)); | ||
986 | |||
987 | p = GNUNET_new (struct GNUNET_CADET_Port); | ||
988 | p->cadet = h; | ||
989 | p->id = *port; | ||
990 | if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put ( | ||
991 | h->ports, | ||
992 | &p->id, | ||
993 | p, | ||
994 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) | ||
995 | { | ||
996 | GNUNET_free (p); | ||
997 | return NULL; | ||
998 | } | ||
999 | p->connects = connects; | ||
1000 | p->cls = connects_cls; | ||
1001 | p->window_changes = window_changes; | ||
1002 | p->disconnects = disconnects; | ||
1003 | p->handlers = (NULL == pd->agpl_url) | ||
1004 | ? GNUNET_MQ_copy_handlers (handlers) | ||
1005 | : GNUNET_MQ_copy_handlers2 (handlers, &return_agpl, NULL); | ||
1006 | |||
1007 | GNUNET_assert (GNUNET_OK == open_port_cb (h, &p->id, p)); | ||
1008 | return p; | ||
1009 | } | ||
1010 | |||
1011 | |||
1012 | /** | ||
1013 | * Create a new channel towards a remote peer. | ||
1014 | * | ||
1015 | * If the destination peer closes the channel after accepting it, | ||
1016 | * @a disconnects will be called for this channel (unless | ||
1017 | * #GNUNET_CADET_channel_destroy() was called on this end first). | ||
1018 | * | ||
1019 | * @param h CADET handle. | ||
1020 | * @param channel_cls Closure for the channel. It's given to: | ||
1021 | * - The disconnect handler @a disconnects | ||
1022 | * - Each message type callback in @a handlers | ||
1023 | * @param destination Peer identity the channel should go to. | ||
1024 | * @param port Identification of the destination port. | ||
1025 | * @param window_changes Function called when the transmit window size changes. | ||
1026 | * @param disconnects Function called when the channel is disconnected. | ||
1027 | * @param handlers Callbacks for messages we care about, NULL-terminated. | ||
1028 | * @return Handle to the channel. | ||
1029 | */ | ||
1030 | struct GNUNET_CADET_Channel * | ||
1031 | GNUNET_CADET_channel_create (struct GNUNET_CADET_Handle *h, | ||
1032 | void *channel_cls, | ||
1033 | const struct GNUNET_PeerIdentity *destination, | ||
1034 | const struct GNUNET_HashCode *port, | ||
1035 | GNUNET_CADET_WindowSizeEventHandler window_changes, | ||
1036 | GNUNET_CADET_DisconnectEventHandler disconnects, | ||
1037 | const struct GNUNET_MQ_MessageHandler *handlers) | ||
1038 | { | ||
1039 | struct GNUNET_CADET_Channel *ch; | ||
1040 | struct GNUNET_CADET_LocalChannelCreateMessage *msg; | ||
1041 | struct GNUNET_MQ_Envelope *env; | ||
1042 | |||
1043 | GNUNET_assert (NULL != disconnects); | ||
1044 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1045 | "Creating channel to peer %s at port %s\n", | ||
1046 | GNUNET_i2s (destination), | ||
1047 | GNUNET_h2s (port)); | ||
1048 | ch = create_channel (h, NULL); | ||
1049 | ch->ctx = channel_cls; | ||
1050 | ch->peer = *destination; | ||
1051 | ch->window_changes = window_changes; | ||
1052 | ch->disconnects = disconnects; | ||
1053 | |||
1054 | /* Create MQ for channel */ | ||
1055 | ch->mq = GNUNET_MQ_queue_for_callbacks (&cadet_mq_send_impl, | ||
1056 | &cadet_mq_destroy_impl, | ||
1057 | &cadet_mq_cancel_impl, | ||
1058 | ch, | ||
1059 | handlers, | ||
1060 | &cadet_mq_error_handler, | ||
1061 | ch); | ||
1062 | GNUNET_MQ_set_handlers_closure (ch->mq, channel_cls); | ||
1063 | |||
1064 | /* Request channel creation to service */ | ||
1065 | env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_CREATE); | ||
1066 | msg->ccn = ch->ccn; | ||
1067 | msg->port = *port; | ||
1068 | msg->peer = *destination; | ||
1069 | GNUNET_MQ_send (h->mq, env); | ||
1070 | return ch; | ||
1071 | } | ||
1072 | |||
1073 | |||
1074 | /** | ||
1075 | * Obtain the message queue for a connected peer. | ||
1076 | * | ||
1077 | * @param channel The channel handle from which to get the MQ. | ||
1078 | * | ||
1079 | * @return NULL if @a channel is not yet connected. | ||
1080 | */ | ||
1081 | struct GNUNET_MQ_Handle * | ||
1082 | GNUNET_CADET_get_mq (const struct GNUNET_CADET_Channel *channel) | ||
1083 | { | ||
1084 | return channel->mq; | ||
1085 | } | ||
1086 | |||
1087 | |||
1088 | /* end of cadet_api.c */ | ||