diff options
author | Christian Grothoff <christian@grothoff.org> | 2019-01-26 19:51:30 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2019-01-26 19:51:30 +0100 |
commit | a5a54c51011a9498e72b2a9341b35333c6beef1a (patch) | |
tree | f8152bbc67e1e834e90f5f4f99ec0f6412a25d7f | |
parent | 93bd3b50faed299a276365a9635e84002d89a5ae (diff) | |
download | gnunet-a5a54c51011a9498e72b2a9341b35333c6beef1a.tar.gz gnunet-a5a54c51011a9498e72b2a9341b35333c6beef1a.zip |
skeleton for TCP communicator
-rw-r--r-- | src/include/gnunet_signatures.h | 5 | ||||
-rw-r--r-- | src/transport/Makefile.am | 9 | ||||
-rw-r--r-- | src/transport/gnunet-communicator-tcp.c | 1093 |
3 files changed, 1107 insertions, 0 deletions
diff --git a/src/include/gnunet_signatures.h b/src/include/gnunet_signatures.h index 3c2413d99..738a414ab 100644 --- a/src/include/gnunet_signatures.h +++ b/src/include/gnunet_signatures.h | |||
@@ -202,6 +202,11 @@ extern "C" | |||
202 | */ | 202 | */ |
203 | #define GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL 30 | 203 | #define GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL 30 |
204 | 204 | ||
205 | /** | ||
206 | * Signature used by TCP communicator handshake, | ||
207 | */ | ||
208 | #define GNUNET_SIGNATURE_COMMUNICATOR_TCP_HANDSHAKE 31 | ||
209 | |||
205 | #if 0 /* keep Emacsens' auto-indent happy */ | 210 | #if 0 /* keep Emacsens' auto-indent happy */ |
206 | { | 211 | { |
207 | #endif | 212 | #endif |
diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am index ab4ef4bfa..0df3e4e27 100644 --- a/src/transport/Makefile.am +++ b/src/transport/Makefile.am | |||
@@ -142,6 +142,7 @@ endif | |||
142 | noinst_PROGRAMS = \ | 142 | noinst_PROGRAMS = \ |
143 | gnunet-transport-profiler \ | 143 | gnunet-transport-profiler \ |
144 | gnunet-communicator-unix \ | 144 | gnunet-communicator-unix \ |
145 | gnunet-communicator-tcp \ | ||
145 | gnunet-service-tng \ | 146 | gnunet-service-tng \ |
146 | $(WLAN_BIN_SENDER) \ | 147 | $(WLAN_BIN_SENDER) \ |
147 | $(WLAN_BIN_RECEIVER) | 148 | $(WLAN_BIN_RECEIVER) |
@@ -251,6 +252,14 @@ gnunet_communicator_unix_LDADD = \ | |||
251 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ | 252 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ |
252 | $(top_builddir)/src/util/libgnunetutil.la | 253 | $(top_builddir)/src/util/libgnunetutil.la |
253 | 254 | ||
255 | gnunet_communicator_tcp_SOURCES = \ | ||
256 | gnunet-communicator-tcp.c | ||
257 | gnunet_communicator_tcp_LDADD = \ | ||
258 | libgnunettransportcommunicator.la \ | ||
259 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ | ||
260 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
261 | $(LIBGCRYPT_LIBS) | ||
262 | |||
254 | 263 | ||
255 | gnunet_helper_transport_wlan_SOURCES = \ | 264 | gnunet_helper_transport_wlan_SOURCES = \ |
256 | gnunet-helper-transport-wlan.c | 265 | gnunet-helper-transport-wlan.c |
diff --git a/src/transport/gnunet-communicator-tcp.c b/src/transport/gnunet-communicator-tcp.c new file mode 100644 index 000000000..6e3faa44a --- /dev/null +++ b/src/transport/gnunet-communicator-tcp.c | |||
@@ -0,0 +1,1093 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2010-2014, 2018 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 transport/gnunet-communicator-tcp.c | ||
23 | * @brief Transport plugin using TCP. | ||
24 | * @author Christian Grothoff | ||
25 | * | ||
26 | * TODO: | ||
27 | * - lots of basic adaptations (see FIXMEs) | ||
28 | * - better message queue management | ||
29 | * - actually encrypt, hmac, decrypt | ||
30 | * - actually transmit | ||
31 | * - | ||
32 | */ | ||
33 | #include "platform.h" | ||
34 | #include "gnunet_util_lib.h" | ||
35 | #include "gnunet_protocols.h" | ||
36 | #include "gnunet_constants.h" | ||
37 | #include "gnunet_nt_lib.h" | ||
38 | #include "gnunet_statistics_service.h" | ||
39 | #include "gnunet_transport_communication_service.h" | ||
40 | |||
41 | /** | ||
42 | * How many messages do we keep at most in the queue to the | ||
43 | * transport service before we start to drop (default, | ||
44 | * can be changed via the configuration file). | ||
45 | * Should be _below_ the level of the communicator API, as | ||
46 | * otherwise we may read messages just to have them dropped | ||
47 | * by the communicator API. | ||
48 | */ | ||
49 | #define DEFAULT_MAX_QUEUE_LENGTH 8 | ||
50 | |||
51 | /** | ||
52 | * Address prefix used by the communicator. | ||
53 | */ | ||
54 | #define COMMUNICATOR_ADDRESS_PREFIX "tcp" | ||
55 | |||
56 | /** | ||
57 | * Configuration section used by the communicator. | ||
58 | */ | ||
59 | #define COMMUNICATOR_CONFIG_SECTION "communicator-tcp" | ||
60 | |||
61 | GNUNET_NETWORK_STRUCT_BEGIN | ||
62 | |||
63 | /** | ||
64 | * TCP initial bytes on the wire (in either direction), used to | ||
65 | * establish a shared secret. | ||
66 | */ | ||
67 | struct TCPHandshake | ||
68 | { | ||
69 | /** | ||
70 | * First bytes: ephemeral key for KX. | ||
71 | */ | ||
72 | struct GNUNET_CRYPTO_EcdhePublicKey ephemeral; | ||
73 | |||
74 | }; | ||
75 | |||
76 | |||
77 | /** | ||
78 | * Signature we use to verify that the ephemeral key was really chosen by | ||
79 | * the specified sender. | ||
80 | */ | ||
81 | struct TcpHandshakeSignature | ||
82 | { | ||
83 | /** | ||
84 | * Purpose must be #GNUNET_SIGNATURE_COMMUNICATOR_TCP_HANDSHAKE | ||
85 | */ | ||
86 | struct GNUNET_CRYPTO_EccSignaturePurpose purpose; | ||
87 | |||
88 | /** | ||
89 | * Identity of the inititor of the TCP connection (TCP client). | ||
90 | */ | ||
91 | struct GNUNET_PeerIdentity sender; | ||
92 | |||
93 | /** | ||
94 | * Presumed identity of the target of the TCP connection (TCP server) | ||
95 | */ | ||
96 | struct GNUNET_PeerIdentity receiver; | ||
97 | |||
98 | /** | ||
99 | * Ephemeral key used by the @e sender. | ||
100 | */ | ||
101 | struct GNUNET_CRYPTO_EcdhePublicKey ephemeral; | ||
102 | |||
103 | /** | ||
104 | * Monotonic time of @e sender, to possibly help detect replay attacks | ||
105 | * (if receiver persists times by sender). | ||
106 | */ | ||
107 | struct GNUNET_TIME_AbsoluteNBO monotonic_time; | ||
108 | }; | ||
109 | |||
110 | |||
111 | /** | ||
112 | * Encrypted continuation of TCP initial handshake. | ||
113 | */ | ||
114 | struct TCPConfirmation | ||
115 | { | ||
116 | /** | ||
117 | * Sender's identity | ||
118 | */ | ||
119 | struct GNUNET_PeerIdentity sender; | ||
120 | |||
121 | /** | ||
122 | * Sender's signature of type #GNUNET_SIGNATURE_COMMUNICATOR_TCP_HANDSHAKE | ||
123 | */ | ||
124 | struct GNUNET_CRYPTO_EddsaSignature sender_sig; | ||
125 | |||
126 | /** | ||
127 | * Monotonic time of @e sender, to possibly help detect replay attacks | ||
128 | * (if receiver persists times by sender). | ||
129 | */ | ||
130 | struct GNUNET_TIME_AbsoluteNBO monotonic_time; | ||
131 | |||
132 | }; | ||
133 | |||
134 | |||
135 | /** | ||
136 | * TCP message box. Always sent encrypted! | ||
137 | */ | ||
138 | struct TCPBox | ||
139 | { | ||
140 | |||
141 | /** | ||
142 | * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_BOX. | ||
143 | */ | ||
144 | struct GNUNET_MessageHeader header; | ||
145 | |||
146 | /** | ||
147 | * HMAC for the following encrypted message. Yes, we MUST use | ||
148 | * mac-then-encrypt here, as we want to hide the message sizes on | ||
149 | * the wire (zero plaintext design!). Using CTR mode padding oracle | ||
150 | * attacks do not apply. Besides, due to the use of ephemeral keys | ||
151 | * (hopefully with effective replay protection from monotonic time!) | ||
152 | * the attacker is limited in using the oracle. | ||
153 | */ | ||
154 | struct GNUNET_ShortHashCode hmac; | ||
155 | |||
156 | /* followed by as may bytes of payload as indicated in @e header */ | ||
157 | |||
158 | }; | ||
159 | |||
160 | |||
161 | /** | ||
162 | * TCP rekey message box. Always sent encrypted! Data after | ||
163 | * this message will use the new key. | ||
164 | */ | ||
165 | struct TCPRekey | ||
166 | { | ||
167 | |||
168 | /** | ||
169 | * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_REKEY. | ||
170 | */ | ||
171 | struct GNUNET_MessageHeader header; | ||
172 | |||
173 | /** | ||
174 | * HMAC for the following encrypted message. Yes, we MUST use | ||
175 | * mac-then-encrypt here, as we want to hide the message sizes on | ||
176 | * the wire (zero plaintext design!). Using CTR mode padding oracle | ||
177 | * attacks do not apply. Besides, due to the use of ephemeral keys | ||
178 | * (hopefully with effective replay protection from monotonic time!) | ||
179 | * the attacker is limited in using the oracle. | ||
180 | */ | ||
181 | struct GNUNET_ShortHashCode hmac; | ||
182 | |||
183 | /** | ||
184 | * New ephemeral key. | ||
185 | */ | ||
186 | struct GNUNET_CRYPTO_EcdhePublicKey ephemeral; | ||
187 | |||
188 | /** | ||
189 | * Sender's signature of type #GNUNET_SIGNATURE_COMMUNICATOR_TCP_HANDSHAKE | ||
190 | */ | ||
191 | struct GNUNET_CRYPTO_EddsaSignature sender_sig; | ||
192 | |||
193 | /** | ||
194 | * Monotonic time of @e sender, to possibly help detect replay attacks | ||
195 | * (if receiver persists times by sender). | ||
196 | */ | ||
197 | struct GNUNET_TIME_AbsoluteNBO monotonic_time; | ||
198 | |||
199 | }; | ||
200 | |||
201 | |||
202 | /** | ||
203 | * TCP finish. Sender asks for the connection to be closed. | ||
204 | * Needed/useful in case we drop RST/FIN packets on the GNUnet | ||
205 | * port due to the possibility of malicious RST/FIN injection. | ||
206 | */ | ||
207 | struct TCPFinish | ||
208 | { | ||
209 | |||
210 | /** | ||
211 | * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_FINISH. | ||
212 | */ | ||
213 | struct GNUNET_MessageHeader header; | ||
214 | |||
215 | /** | ||
216 | * HMAC for the following encrypted message. Yes, we MUST use | ||
217 | * mac-then-encrypt here, as we want to hide the message sizes on | ||
218 | * the wire (zero plaintext design!). Using CTR mode padding oracle | ||
219 | * attacks do not apply. Besides, due to the use of ephemeral keys | ||
220 | * (hopefully with effective replay protection from monotonic time!) | ||
221 | * the attacker is limited in using the oracle. | ||
222 | */ | ||
223 | struct GNUNET_ShortHashCode hmac; | ||
224 | |||
225 | }; | ||
226 | |||
227 | |||
228 | GNUNET_NETWORK_STRUCT_END | ||
229 | |||
230 | |||
231 | /** | ||
232 | * Handle for a queue. | ||
233 | */ | ||
234 | struct Queue | ||
235 | { | ||
236 | |||
237 | /** | ||
238 | * To whom are we talking to. | ||
239 | */ | ||
240 | struct GNUNET_PeerIdentity target; | ||
241 | |||
242 | /** | ||
243 | * socket that we transmit all data with on this queue | ||
244 | */ | ||
245 | struct GNUNET_NETWORK_Handle *sock; | ||
246 | |||
247 | /** | ||
248 | * cipher for decryption of incoming data. | ||
249 | */ | ||
250 | gcry_cipher_hd_t in_cipher; | ||
251 | |||
252 | /** | ||
253 | * cipher for encryption of outgoing data. | ||
254 | */ | ||
255 | gcry_cipher_hd_t out_cipher; | ||
256 | |||
257 | /** | ||
258 | * Shared secret for HMAC verification on incoming data. | ||
259 | */ | ||
260 | struct GNUNET_HashCode in_hmac; | ||
261 | |||
262 | /** | ||
263 | * Shared secret for HMAC generation on outgoing data. | ||
264 | */ | ||
265 | struct GNUNET_HashCode out_hmac; | ||
266 | |||
267 | /** | ||
268 | * ID of read task for this connection. | ||
269 | */ | ||
270 | struct GNUNET_SCHEDULER_Task *read_task; | ||
271 | |||
272 | /** | ||
273 | * ID of write task for this connection. | ||
274 | */ | ||
275 | struct GNUNET_SCHEDULER_Task *write_task; | ||
276 | |||
277 | /** | ||
278 | * Address of the other peer. | ||
279 | */ | ||
280 | struct sockaddr *address; | ||
281 | |||
282 | /** | ||
283 | * How many more bytes may we sent with the current @e out_cipher | ||
284 | * before we should rekey? | ||
285 | */ | ||
286 | uint64_t rekey_left_bytes; | ||
287 | |||
288 | /** | ||
289 | * Until what time may we sent with the current @e out_cipher | ||
290 | * before we should rekey? | ||
291 | */ | ||
292 | struct GNUNET_TIME_Absolute rekey_time; | ||
293 | |||
294 | /** | ||
295 | * Length of the address. | ||
296 | */ | ||
297 | socklen_t address_len; | ||
298 | |||
299 | /** | ||
300 | * Message currently scheduled for transmission, non-NULL if and only | ||
301 | * if this queue is in the #queue_head DLL. | ||
302 | */ | ||
303 | const struct GNUNET_MessageHeader *msg; | ||
304 | |||
305 | /** | ||
306 | * Message queue we are providing for the #ch. | ||
307 | */ | ||
308 | struct GNUNET_MQ_Handle *mq; | ||
309 | |||
310 | /** | ||
311 | * handle for this queue with the #ch. | ||
312 | */ | ||
313 | struct GNUNET_TRANSPORT_QueueHandle *qh; | ||
314 | |||
315 | /** | ||
316 | * Number of bytes we currently have in our write queue. | ||
317 | */ | ||
318 | unsigned long long bytes_in_queue; | ||
319 | |||
320 | /** | ||
321 | * Timeout for this queue. | ||
322 | */ | ||
323 | struct GNUNET_TIME_Absolute timeout; | ||
324 | |||
325 | /** | ||
326 | * Which network type does this queue use? | ||
327 | */ | ||
328 | enum GNUNET_NetworkType nt; | ||
329 | |||
330 | }; | ||
331 | |||
332 | |||
333 | /** | ||
334 | * ID of listen task | ||
335 | */ | ||
336 | static struct GNUNET_SCHEDULER_Task *listen_task; | ||
337 | |||
338 | /** | ||
339 | * Number of messages we currently have in our queues towards the transport service. | ||
340 | */ | ||
341 | static unsigned long long delivering_messages; | ||
342 | |||
343 | /** | ||
344 | * Maximum queue length before we stop reading towards the transport service. | ||
345 | */ | ||
346 | static unsigned long long max_queue_length; | ||
347 | |||
348 | /** | ||
349 | * For logging statistics. | ||
350 | */ | ||
351 | static struct GNUNET_STATISTICS_Handle *stats; | ||
352 | |||
353 | /** | ||
354 | * Our environment. | ||
355 | */ | ||
356 | static struct GNUNET_TRANSPORT_CommunicatorHandle *ch; | ||
357 | |||
358 | /** | ||
359 | * Queues (map from peer identity to `struct Queue`) | ||
360 | */ | ||
361 | static struct GNUNET_CONTAINER_MultiPeerMap *queue_map; | ||
362 | |||
363 | /** | ||
364 | * Listen socket. | ||
365 | */ | ||
366 | static struct GNUNET_NETWORK_Handle *listen_sock; | ||
367 | |||
368 | /** | ||
369 | * Handle to the operation that publishes our address. | ||
370 | */ | ||
371 | static struct GNUNET_TRANSPORT_AddressIdentifier *ai; | ||
372 | |||
373 | |||
374 | /** | ||
375 | * We have been notified that our listen socket has something to | ||
376 | * read. Do the read and reschedule this function to be called again | ||
377 | * once more is available. | ||
378 | * | ||
379 | * @param cls NULL | ||
380 | */ | ||
381 | static void | ||
382 | listen_cb (void *cls); | ||
383 | |||
384 | |||
385 | /** | ||
386 | * Functions with this signature are called whenever we need | ||
387 | * to close a queue due to a disconnect or failure to | ||
388 | * establish a connection. | ||
389 | * | ||
390 | * @param queue queue to close down | ||
391 | */ | ||
392 | static void | ||
393 | queue_destroy (struct Queue *queue) | ||
394 | { | ||
395 | struct GNUNET_MQ_Handle *mq; | ||
396 | |||
397 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
398 | "Disconnecting queue for peer `%s'\n", | ||
399 | GNUNET_i2s (&queue->target)); | ||
400 | if (NULL != (mq = queue->mq)) | ||
401 | { | ||
402 | queue->mq = NULL; | ||
403 | GNUNET_MQ_destroy (mq); | ||
404 | } | ||
405 | GNUNET_assert (GNUNET_YES == | ||
406 | GNUNET_CONTAINER_multipeermap_remove (queue_map, | ||
407 | &queue->target, | ||
408 | queue)); | ||
409 | GNUNET_STATISTICS_set (stats, | ||
410 | "# UNIX queues active", | ||
411 | GNUNET_CONTAINER_multipeermap_size (queue_map), | ||
412 | GNUNET_NO); | ||
413 | if (NULL != queue->read_task) | ||
414 | { | ||
415 | GNUNET_SCHEDULER_cancel (queue->read_task); | ||
416 | queue->read_task = NULL; | ||
417 | } | ||
418 | if (NULL != queue->write_task) | ||
419 | { | ||
420 | GNUNET_SCHEDULER_cancel (queue->write_task); | ||
421 | queue->write_task = NULL; | ||
422 | } | ||
423 | GNUNET_NETWORK_socket_close (queue->sock); | ||
424 | gcry_cipher_close (queue->in_cipher); | ||
425 | gcry_cipher_close (queue->out_cipher); | ||
426 | GNUNET_free (queue->address); | ||
427 | GNUNET_free (queue); | ||
428 | if (NULL == listen_task) | ||
429 | listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
430 | listen_sock, | ||
431 | &listen_cb, | ||
432 | NULL); | ||
433 | |||
434 | } | ||
435 | |||
436 | |||
437 | /** | ||
438 | * Queue read task. If we hit the timeout, disconnect it | ||
439 | * | ||
440 | * @param cls the `struct Queue *` to disconnect | ||
441 | */ | ||
442 | static void | ||
443 | queue_read (void *cls) | ||
444 | { | ||
445 | struct Queue *queue = cls; | ||
446 | struct GNUNET_TIME_Relative left; | ||
447 | |||
448 | queue->read_task = NULL; | ||
449 | /* CHECK IF READ-ready, then perform read! */ | ||
450 | |||
451 | left = GNUNET_TIME_absolute_get_remaining (queue->timeout); | ||
452 | if (0 != left.rel_value_us) | ||
453 | { | ||
454 | /* not actually our turn yet, but let's at least update | ||
455 | the monitor, it may think we're about to die ... */ | ||
456 | queue->read_task | ||
457 | = GNUNET_SCHEDULER_add_read_net (left, | ||
458 | queue->sock, | ||
459 | &queue_read, | ||
460 | queue); | ||
461 | return; | ||
462 | } | ||
463 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
464 | "Queue %p was idle for %s, disconnecting\n", | ||
465 | queue, | ||
466 | GNUNET_STRINGS_relative_time_to_string (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, | ||
467 | GNUNET_YES)); | ||
468 | // FIXME: try to send 'finish' message first!? | ||
469 | queue_destroy (queue); | ||
470 | } | ||
471 | |||
472 | |||
473 | /** | ||
474 | * Increment queue timeout due to activity. We do not immediately | ||
475 | * notify the monitor here as that might generate excessive | ||
476 | * signalling. | ||
477 | * | ||
478 | * @param queue queue for which the timeout should be rescheduled | ||
479 | */ | ||
480 | static void | ||
481 | reschedule_queue_timeout (struct Queue *queue) | ||
482 | { | ||
483 | GNUNET_assert (NULL != queue->read_task); | ||
484 | queue->timeout | ||
485 | = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); | ||
486 | } | ||
487 | |||
488 | |||
489 | /** | ||
490 | * Convert TCP bind specification to a `struct sockaddr *` | ||
491 | * | ||
492 | * @param bindto bind specification to convert | ||
493 | * @param[out] sock_len set to the length of the address | ||
494 | * @return converted bindto specification | ||
495 | */ | ||
496 | static struct sockaddr * | ||
497 | tcp_address_to_sockaddr (const char *bindto, | ||
498 | socklen_t *sock_len) | ||
499 | { | ||
500 | struct sockaddr *in; | ||
501 | size_t slen; | ||
502 | |||
503 | /* FIXME: parse, allocate, return! */ | ||
504 | return NULL; | ||
505 | } | ||
506 | |||
507 | |||
508 | /** | ||
509 | * We have been notified that our socket is ready to write. | ||
510 | * Then reschedule this function to be called again once more is available. | ||
511 | * | ||
512 | * @param cls a `struct Queue` | ||
513 | */ | ||
514 | static void | ||
515 | queue_write (void *cls) | ||
516 | { | ||
517 | struct Queue *queue = cls; | ||
518 | const struct GNUNET_MessageHeader *msg = queue->msg; | ||
519 | size_t msg_size = ntohs (msg->size); | ||
520 | |||
521 | queue->write_task = NULL; | ||
522 | /* FIXME: send 'msg' */ | ||
523 | /* FIXME: check if we have more messages pending */ | ||
524 | queue->write_task | ||
525 | = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
526 | queue->sock, | ||
527 | &queue_write, | ||
528 | queue); | ||
529 | } | ||
530 | |||
531 | |||
532 | /** | ||
533 | * Signature of functions implementing the sending functionality of a | ||
534 | * message queue. | ||
535 | * | ||
536 | * @param mq the message queue | ||
537 | * @param msg the message to send | ||
538 | * @param impl_state our `struct Queue` | ||
539 | */ | ||
540 | static void | ||
541 | mq_send (struct GNUNET_MQ_Handle *mq, | ||
542 | const struct GNUNET_MessageHeader *msg, | ||
543 | void *impl_state) | ||
544 | { | ||
545 | struct Queue *queue = impl_state; | ||
546 | |||
547 | GNUNET_assert (mq == queue->mq); | ||
548 | GNUNET_assert (NULL == queue->msg); | ||
549 | queue->msg = msg; | ||
550 | GNUNET_assert (NULL != queue->sock); | ||
551 | if (NULL == queue->write_task) | ||
552 | queue->write_task = | ||
553 | GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
554 | queue->sock, | ||
555 | &queue_write, | ||
556 | queue); | ||
557 | } | ||
558 | |||
559 | |||
560 | /** | ||
561 | * Signature of functions implementing the destruction of a message | ||
562 | * queue. Implementations must not free @a mq, but should take care | ||
563 | * of @a impl_state. | ||
564 | * | ||
565 | * @param mq the message queue to destroy | ||
566 | * @param impl_state our `struct Queue` | ||
567 | */ | ||
568 | static void | ||
569 | mq_destroy (struct GNUNET_MQ_Handle *mq, | ||
570 | void *impl_state) | ||
571 | { | ||
572 | struct Queue *queue = impl_state; | ||
573 | |||
574 | if (mq == queue->mq) | ||
575 | { | ||
576 | queue->mq = NULL; | ||
577 | queue_destroy (queue); | ||
578 | } | ||
579 | } | ||
580 | |||
581 | |||
582 | /** | ||
583 | * Implementation function that cancels the currently sent message. | ||
584 | * | ||
585 | * @param mq message queue | ||
586 | * @param impl_state our `struct Queue` | ||
587 | */ | ||
588 | static void | ||
589 | mq_cancel (struct GNUNET_MQ_Handle *mq, | ||
590 | void *impl_state) | ||
591 | { | ||
592 | struct Queue *queue = impl_state; | ||
593 | |||
594 | GNUNET_assert (NULL != queue->msg); | ||
595 | queue->msg = NULL; | ||
596 | GNUNET_assert (NULL != queue->write_task); | ||
597 | if (1) // FIXME? | ||
598 | { | ||
599 | GNUNET_SCHEDULER_cancel (queue->write_task); | ||
600 | queue->write_task = NULL; | ||
601 | } | ||
602 | } | ||
603 | |||
604 | |||
605 | /** | ||
606 | * Generic error handler, called with the appropriate | ||
607 | * error code and the same closure specified at the creation of | ||
608 | * the message queue. | ||
609 | * Not every message queue implementation supports an error handler. | ||
610 | * | ||
611 | * @param cls our `struct Queue` | ||
612 | * @param error error code | ||
613 | */ | ||
614 | static void | ||
615 | mq_error (void *cls, | ||
616 | enum GNUNET_MQ_Error error) | ||
617 | { | ||
618 | struct Queue *queue = cls; | ||
619 | |||
620 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
621 | "TCP MQ error in queue to %s: %d\n", | ||
622 | GNUNET_i2s (&queue->target), | ||
623 | (int) error); | ||
624 | queue_destroy (queue); | ||
625 | } | ||
626 | |||
627 | |||
628 | /** | ||
629 | * Creates a new outbound queue the transport service will use to send | ||
630 | * data to another peer. | ||
631 | * | ||
632 | * @param peer the target peer | ||
633 | * @param cs inbound or outbound queue | ||
634 | * @param in the address | ||
635 | * @param in_len number of bytes in @a in | ||
636 | * @return the queue or NULL of max connections exceeded | ||
637 | */ | ||
638 | static struct Queue * | ||
639 | setup_queue (struct GNUNET_NETWORK_Handle *sock, | ||
640 | enum GNUNET_TRANSPORT_ConnectionStatus cs, | ||
641 | const struct sockaddr *in, | ||
642 | socklen_t in_len) | ||
643 | { | ||
644 | struct Queue *queue; | ||
645 | |||
646 | queue = GNUNET_new (struct Queue); | ||
647 | // queue->target = *target; // FIXME: handle case that we don't know the target yet! | ||
648 | queue->address = GNUNET_memdup (in, | ||
649 | in_len); | ||
650 | queue->address_len = in_len; | ||
651 | queue->sock = sock; | ||
652 | queue->nt = 0; // FIXME: determine NT! | ||
653 | (void) GNUNET_CONTAINER_multipeermap_put (queue_map, | ||
654 | &queue->target, | ||
655 | queue, | ||
656 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
657 | GNUNET_STATISTICS_set (stats, | ||
658 | "# queues active", | ||
659 | GNUNET_CONTAINER_multipeermap_size (queue_map), | ||
660 | GNUNET_NO); | ||
661 | queue->timeout | ||
662 | = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); | ||
663 | queue->read_task | ||
664 | = GNUNET_SCHEDULER_add_read_net (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, | ||
665 | queue->sock, | ||
666 | &queue_read, | ||
667 | queue); | ||
668 | queue->mq | ||
669 | = GNUNET_MQ_queue_for_callbacks (&mq_send, | ||
670 | &mq_destroy, | ||
671 | &mq_cancel, | ||
672 | queue, | ||
673 | NULL, | ||
674 | &mq_error, | ||
675 | queue); | ||
676 | { | ||
677 | char *foreign_addr; | ||
678 | |||
679 | switch (queue->address->sa_family) | ||
680 | { | ||
681 | case AF_INET: | ||
682 | GNUNET_asprintf (&foreign_addr, | ||
683 | "%s-%s:%d", | ||
684 | COMMUNICATOR_ADDRESS_PREFIX, | ||
685 | "inet-ntop-fixme", | ||
686 | 4242); | ||
687 | break; | ||
688 | case AF_INET6: | ||
689 | GNUNET_asprintf (&foreign_addr, | ||
690 | "%s-%s:%d", | ||
691 | COMMUNICATOR_ADDRESS_PREFIX, | ||
692 | "inet-ntop-fixme", | ||
693 | 4242); | ||
694 | break; | ||
695 | default: | ||
696 | GNUNET_assert (0); | ||
697 | } | ||
698 | queue->qh | ||
699 | = GNUNET_TRANSPORT_communicator_mq_add (ch, | ||
700 | &queue->target, | ||
701 | foreign_addr, | ||
702 | 0 /* no MTU */, | ||
703 | queue->nt, | ||
704 | cs, | ||
705 | queue->mq); | ||
706 | GNUNET_free (foreign_addr); | ||
707 | } | ||
708 | return queue; | ||
709 | } | ||
710 | |||
711 | |||
712 | /** | ||
713 | * We have been notified that our listen socket has something to | ||
714 | * read. Do the read and reschedule this function to be called again | ||
715 | * once more is available. | ||
716 | * | ||
717 | * @param cls NULL | ||
718 | */ | ||
719 | static void | ||
720 | listen_cb (void *cls); | ||
721 | |||
722 | |||
723 | /** | ||
724 | * We have been notified that our listen socket has something to | ||
725 | * read. Do the read and reschedule this function to be called again | ||
726 | * once more is available. | ||
727 | * | ||
728 | * @param cls NULL | ||
729 | */ | ||
730 | static void | ||
731 | listen_cb (void *cls) | ||
732 | { | ||
733 | char buf[65536] GNUNET_ALIGN; | ||
734 | struct Queue *queue; | ||
735 | struct sockaddr_storage in; | ||
736 | socklen_t addrlen; | ||
737 | ssize_t ret; | ||
738 | uint16_t msize; | ||
739 | struct GNUNET_NETWORK_Handle *sock; | ||
740 | |||
741 | listen_task = NULL; | ||
742 | GNUNET_assert (NULL != listen_sock); | ||
743 | addrlen = sizeof (in); | ||
744 | memset (&in, | ||
745 | 0, | ||
746 | sizeof (in)); | ||
747 | sock = GNUNET_NETWORK_socket_accept (listen_sock, | ||
748 | (struct sockaddr *) &in, | ||
749 | &addrlen); | ||
750 | if ( (NULL == sock) && | ||
751 | ( (EMFILE == errno) || | ||
752 | (ENFILE == errno) ) ) | ||
753 | return; /* system limit reached, wait until connection goes down */ | ||
754 | listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
755 | listen_sock, | ||
756 | &listen_cb, | ||
757 | NULL); | ||
758 | if ( (NULL == sock) && | ||
759 | ( (EAGAIN == errno) || | ||
760 | (ENOBUFS == errno) ) ) | ||
761 | return; | ||
762 | if (NULL == sock) | ||
763 | { | ||
764 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, | ||
765 | "accept"); | ||
766 | return; | ||
767 | } | ||
768 | queue = setup_queue (sock, | ||
769 | GNUNET_TRANSPORT_CS_INBOUND, | ||
770 | (struct sockaddr *) &in, | ||
771 | addrlen); | ||
772 | if (NULL == queue) | ||
773 | { | ||
774 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
775 | _("Maximum number of TCP connections exceeded, dropping incoming connection\n")); | ||
776 | return; | ||
777 | } | ||
778 | } | ||
779 | |||
780 | |||
781 | /** | ||
782 | * Function called by the transport service to initialize a | ||
783 | * message queue given address information about another peer. | ||
784 | * If and when the communication channel is established, the | ||
785 | * communicator must call #GNUNET_TRANSPORT_communicator_mq_add() | ||
786 | * to notify the service that the channel is now up. It is | ||
787 | * the responsibility of the communicator to manage sane | ||
788 | * retries and timeouts for any @a peer/@a address combination | ||
789 | * provided by the transport service. Timeouts and retries | ||
790 | * do not need to be signalled to the transport service. | ||
791 | * | ||
792 | * @param cls closure | ||
793 | * @param peer identity of the other peer | ||
794 | * @param address where to send the message, human-readable | ||
795 | * communicator-specific format, 0-terminated, UTF-8 | ||
796 | * @return #GNUNET_OK on success, #GNUNET_SYSERR if the provided address is invalid | ||
797 | */ | ||
798 | static int | ||
799 | mq_init (void *cls, | ||
800 | const struct GNUNET_PeerIdentity *peer, | ||
801 | const char *address) | ||
802 | { | ||
803 | struct Queue *queue; | ||
804 | const char *path; | ||
805 | struct sockaddr *in; | ||
806 | socklen_t in_len; | ||
807 | |||
808 | if (0 != strncmp (address, | ||
809 | COMMUNICATOR_ADDRESS_PREFIX "-", | ||
810 | strlen (COMMUNICATOR_ADDRESS_PREFIX "-"))) | ||
811 | { | ||
812 | GNUNET_break_op (0); | ||
813 | return GNUNET_SYSERR; | ||
814 | } | ||
815 | path = &address[strlen (COMMUNICATOR_ADDRESS_PREFIX "-")]; | ||
816 | in = tcp_address_to_sockaddr (path, | ||
817 | &in_len); | ||
818 | #if FIXME | ||
819 | queue = setup_queue (peer, | ||
820 | GNUNET_TRANSPORT_CS_OUTBOUND, | ||
821 | in, | ||
822 | in_len); | ||
823 | #endif | ||
824 | GNUNET_free (in); | ||
825 | if (NULL == queue) | ||
826 | { | ||
827 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
828 | "Failed to setup queue to %s at `%s'\n", | ||
829 | GNUNET_i2s (peer), | ||
830 | path); | ||
831 | return GNUNET_NO; | ||
832 | } | ||
833 | return GNUNET_OK; | ||
834 | } | ||
835 | |||
836 | |||
837 | /** | ||
838 | * Iterator over all message queues to clean up. | ||
839 | * | ||
840 | * @param cls NULL | ||
841 | * @param target unused | ||
842 | * @param value the queue to destroy | ||
843 | * @return #GNUNET_OK to continue to iterate | ||
844 | */ | ||
845 | static int | ||
846 | get_queue_delete_it (void *cls, | ||
847 | const struct GNUNET_PeerIdentity *target, | ||
848 | void *value) | ||
849 | { | ||
850 | struct Queue *queue = value; | ||
851 | |||
852 | (void) cls; | ||
853 | (void) target; | ||
854 | queue_destroy (queue); | ||
855 | return GNUNET_OK; | ||
856 | } | ||
857 | |||
858 | |||
859 | /** | ||
860 | * Shutdown the UNIX communicator. | ||
861 | * | ||
862 | * @param cls NULL (always) | ||
863 | */ | ||
864 | static void | ||
865 | do_shutdown (void *cls) | ||
866 | { | ||
867 | if (NULL != listen_task) | ||
868 | { | ||
869 | GNUNET_SCHEDULER_cancel (listen_task); | ||
870 | listen_task = NULL; | ||
871 | } | ||
872 | if (NULL != listen_sock) | ||
873 | { | ||
874 | GNUNET_break (GNUNET_OK == | ||
875 | GNUNET_NETWORK_socket_close (listen_sock)); | ||
876 | listen_sock = NULL; | ||
877 | } | ||
878 | GNUNET_CONTAINER_multipeermap_iterate (queue_map, | ||
879 | &get_queue_delete_it, | ||
880 | NULL); | ||
881 | GNUNET_CONTAINER_multipeermap_destroy (queue_map); | ||
882 | if (NULL != ai) | ||
883 | { | ||
884 | GNUNET_TRANSPORT_communicator_address_remove (ai); | ||
885 | ai = NULL; | ||
886 | } | ||
887 | if (NULL != ch) | ||
888 | { | ||
889 | GNUNET_TRANSPORT_communicator_disconnect (ch); | ||
890 | ch = NULL; | ||
891 | } | ||
892 | if (NULL != stats) | ||
893 | { | ||
894 | GNUNET_STATISTICS_destroy (stats, | ||
895 | GNUNET_NO); | ||
896 | stats = NULL; | ||
897 | } | ||
898 | } | ||
899 | |||
900 | |||
901 | /** | ||
902 | * Function called when the transport service has received an | ||
903 | * acknowledgement for this communicator (!) via a different return | ||
904 | * path. | ||
905 | * | ||
906 | * Not applicable for UNIX. | ||
907 | * | ||
908 | * @param cls closure | ||
909 | * @param sender which peer sent the notification | ||
910 | * @param msg payload | ||
911 | */ | ||
912 | static void | ||
913 | enc_notify_cb (void *cls, | ||
914 | const struct GNUNET_PeerIdentity *sender, | ||
915 | const struct GNUNET_MessageHeader *msg) | ||
916 | { | ||
917 | (void) cls; | ||
918 | (void) sender; | ||
919 | (void) msg; | ||
920 | GNUNET_break_op (0); | ||
921 | } | ||
922 | |||
923 | |||
924 | /** | ||
925 | * Setup communicator and launch network interactions. | ||
926 | * | ||
927 | * @param cls NULL (always) | ||
928 | * @param args remaining command-line arguments | ||
929 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
930 | * @param cfg configuration | ||
931 | */ | ||
932 | static void | ||
933 | run (void *cls, | ||
934 | char *const *args, | ||
935 | const char *cfgfile, | ||
936 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
937 | { | ||
938 | char *bindto; | ||
939 | struct sockaddr *in; | ||
940 | socklen_t in_len; | ||
941 | char *my_addr; | ||
942 | (void) cls; | ||
943 | |||
944 | if (GNUNET_OK != | ||
945 | GNUNET_CONFIGURATION_get_value_filename (cfg, | ||
946 | COMMUNICATOR_CONFIG_SECTION, | ||
947 | "BINDTO", | ||
948 | &bindto)) | ||
949 | { | ||
950 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
951 | COMMUNICATOR_CONFIG_SECTION, | ||
952 | "BINDTO"); | ||
953 | return; | ||
954 | } | ||
955 | if (GNUNET_OK != | ||
956 | GNUNET_CONFIGURATION_get_value_number (cfg, | ||
957 | COMMUNICATOR_CONFIG_SECTION, | ||
958 | "MAX_QUEUE_LENGTH", | ||
959 | &max_queue_length)) | ||
960 | max_queue_length = DEFAULT_MAX_QUEUE_LENGTH; | ||
961 | |||
962 | in = tcp_address_to_sockaddr (bindto, | ||
963 | &in_len); | ||
964 | if (NULL == in) | ||
965 | { | ||
966 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
967 | "Failed to setup TCP socket address with path `%s'\n", | ||
968 | bindto); | ||
969 | GNUNET_free (bindto); | ||
970 | return; | ||
971 | } | ||
972 | listen_sock = GNUNET_NETWORK_socket_create (in->sa_family, | ||
973 | SOCK_STREAM, | ||
974 | IPPROTO_TCP); | ||
975 | if (NULL == listen_sock) | ||
976 | { | ||
977 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, | ||
978 | "socket"); | ||
979 | GNUNET_free (in); | ||
980 | GNUNET_free (bindto); | ||
981 | return; | ||
982 | } | ||
983 | if (GNUNET_OK != | ||
984 | GNUNET_NETWORK_socket_bind (listen_sock, | ||
985 | in, | ||
986 | in_len)) | ||
987 | { | ||
988 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, | ||
989 | "bind", | ||
990 | bindto); | ||
991 | GNUNET_NETWORK_socket_close (listen_sock); | ||
992 | listen_sock = NULL; | ||
993 | GNUNET_free (in); | ||
994 | GNUNET_free (bindto); | ||
995 | return; | ||
996 | } | ||
997 | GNUNET_free (in); | ||
998 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
999 | "Bound to `%s'\n", | ||
1000 | bindto); | ||
1001 | stats = GNUNET_STATISTICS_create ("C-TCP", | ||
1002 | cfg); | ||
1003 | GNUNET_SCHEDULER_add_shutdown (&do_shutdown, | ||
1004 | NULL); | ||
1005 | listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
1006 | listen_sock, | ||
1007 | &listen_cb, | ||
1008 | NULL); | ||
1009 | queue_map = GNUNET_CONTAINER_multipeermap_create (10, | ||
1010 | GNUNET_NO); | ||
1011 | ch = GNUNET_TRANSPORT_communicator_connect (cfg, | ||
1012 | COMMUNICATOR_CONFIG_SECTION, | ||
1013 | COMMUNICATOR_ADDRESS_PREFIX, | ||
1014 | GNUNET_TRANSPORT_CC_RELIABLE, | ||
1015 | &mq_init, | ||
1016 | NULL, | ||
1017 | &enc_notify_cb, | ||
1018 | NULL); | ||
1019 | if (NULL == ch) | ||
1020 | { | ||
1021 | GNUNET_break (0); | ||
1022 | GNUNET_SCHEDULER_shutdown (); | ||
1023 | GNUNET_free (bindto); | ||
1024 | return; | ||
1025 | } | ||
1026 | // FIXME: bindto is wrong here, we MUST get our external | ||
1027 | // IP address and really look at 'in' here as we might | ||
1028 | // be bound to loopback or some other specific IP address! | ||
1029 | GNUNET_asprintf (&my_addr, | ||
1030 | "%s-%s", | ||
1031 | COMMUNICATOR_ADDRESS_PREFIX, | ||
1032 | bindto); | ||
1033 | GNUNET_free (bindto); | ||
1034 | // FIXME: based on our bindto, we might not be able to tell the | ||
1035 | // network type yet! What to do here!? | ||
1036 | ai = GNUNET_TRANSPORT_communicator_address_add (ch, | ||
1037 | my_addr, | ||
1038 | GNUNET_NT_LOOPBACK, // FIXME: wrong NT! | ||
1039 | GNUNET_TIME_UNIT_FOREVER_REL); | ||
1040 | GNUNET_free (my_addr); | ||
1041 | } | ||
1042 | |||
1043 | |||
1044 | /** | ||
1045 | * The main function for the UNIX communicator. | ||
1046 | * | ||
1047 | * @param argc number of arguments from the command line | ||
1048 | * @param argv command line arguments | ||
1049 | * @return 0 ok, 1 on error | ||
1050 | */ | ||
1051 | int | ||
1052 | main (int argc, | ||
1053 | char *const *argv) | ||
1054 | { | ||
1055 | static const struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
1056 | GNUNET_GETOPT_OPTION_END | ||
1057 | }; | ||
1058 | int ret; | ||
1059 | |||
1060 | if (GNUNET_OK != | ||
1061 | GNUNET_STRINGS_get_utf8_args (argc, argv, | ||
1062 | &argc, &argv)) | ||
1063 | return 2; | ||
1064 | |||
1065 | ret = | ||
1066 | (GNUNET_OK == | ||
1067 | GNUNET_PROGRAM_run (argc, argv, | ||
1068 | "gnunet-communicator-tcp", | ||
1069 | _("GNUnet TCP communicator"), | ||
1070 | options, | ||
1071 | &run, | ||
1072 | NULL)) ? 0 : 1; | ||
1073 | GNUNET_free ((void*) argv); | ||
1074 | return ret; | ||
1075 | } | ||
1076 | |||
1077 | |||
1078 | #if defined(LINUX) && defined(__GLIBC__) | ||
1079 | #include <malloc.h> | ||
1080 | |||
1081 | /** | ||
1082 | * MINIMIZE heap size (way below 128k) since this process doesn't need much. | ||
1083 | */ | ||
1084 | void __attribute__ ((constructor)) | ||
1085 | GNUNET_ARM_memory_init () | ||
1086 | { | ||
1087 | mallopt (M_TRIM_THRESHOLD, 4 * 1024); | ||
1088 | mallopt (M_TOP_PAD, 1 * 1024); | ||
1089 | malloc_trim (0); | ||
1090 | } | ||
1091 | #endif | ||
1092 | |||
1093 | /* end of gnunet-communicator-tcp.c */ | ||