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