aboutsummaryrefslogtreecommitdiff
path: root/src/transport
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2009-05-29 00:46:26 +0000
committerChristian Grothoff <christian@grothoff.org>2009-05-29 00:46:26 +0000
commit0a217a8df1657b4334b55b0e4a6c7837a8dbcfd9 (patch)
tree6b552f40eb089db96409a312a98d9b12bd669102 /src/transport
downloadgnunet-0a217a8df1657b4334b55b0e4a6c7837a8dbcfd9.tar.gz
gnunet-0a217a8df1657b4334b55b0e4a6c7837a8dbcfd9.zip
ng
Diffstat (limited to 'src/transport')
-rw-r--r--src/transport/Makefile.am84
-rw-r--r--src/transport/NOTES46
-rw-r--r--src/transport/gnunet-service-transport.c2852
-rw-r--r--src/transport/gnunet-transport.c42
-rw-r--r--src/transport/plugin_transport.h468
-rw-r--r--src/transport/plugin_transport_http.c2085
-rw-r--r--src/transport/plugin_transport_smtp.c906
-rw-r--r--src/transport/plugin_transport_tcp.c1782
-rw-r--r--src/transport/plugin_transport_template.c335
-rw-r--r--src/transport/plugin_transport_udp.c592
-rw-r--r--src/transport/test_transport_api.c305
-rw-r--r--src/transport/test_transport_api_data.conf24
-rw-r--r--src/transport/test_transport_api_peer1.conf25
-rw-r--r--src/transport/test_transport_api_peer2.conf25
-rw-r--r--src/transport/transport.h238
-rw-r--r--src/transport/transport_api.c1863
16 files changed, 11672 insertions, 0 deletions
diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am
new file mode 100644
index 000000000..236dec7c4
--- /dev/null
+++ b/src/transport/Makefile.am
@@ -0,0 +1,84 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3plugindir = $(libdir)/gnunet
4
5if MINGW
6 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
7endif
8
9if USE_COVERAGE
10 AM_CFLAGS = -fprofile-arcs -ftest-coverage
11endif
12
13
14lib_LTLIBRARIES = \
15 libgnunettransport.la
16
17libgnunettransport_la_SOURCES = \
18 transport_api.c transport.h
19libgnunettransport_la_LIBADD = \
20 $(top_builddir)/src/arm/libgnunetarm.la \
21 $(top_builddir)/src/hello/libgnunethello.la \
22 $(top_builddir)/src/util/libgnunetutil.la \
23 $(GN_LIBINTL)
24libgnunettransport_la_LDFLAGS = \
25 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
26 -version-info 0:0:0
27
28
29bin_PROGRAMS = \
30 gnunet-transport \
31 gnunet-service-transport
32
33gnunet_transport_SOURCES = \
34 gnunet-transport.c
35gnunet_transport_LDADD = \
36 $(top_builddir)/src/transport/libgnunettransport.la \
37 $(top_builddir)/src/util/libgnunetutil.la \
38 $(GN_LIBINTL)
39
40gnunet_service_transport_SOURCES = \
41 gnunet-service-transport.c
42gnunet_service_transport_LDADD = \
43 $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \
44 $(top_builddir)/src/util/libgnunetutil.la \
45 $(GN_LIBINTL)
46
47
48
49plugin_LTLIBRARIES = \
50 libgnunet_plugin_transport_tcp.la \
51 libgnunet_plugin_transport_template.la
52# TODO: add udp, http, nat, etc.
53
54libgnunet_plugin_transport_tcp_la_SOURCES = \
55 plugin_transport_tcp.c
56libgnunet_plugin_transport_tcp_la_LIBADD = \
57 $(top_builddir)/src/resolver/libgnunetresolver.la \
58 $(top_builddir)/src/util/libgnunetutil.la
59libgnunet_plugin_transport_tcp_la_LDFLAGS = \
60 $(GN_PLUGIN_LDFLAGS)
61
62libgnunet_plugin_transport_template_la_SOURCES = \
63 plugin_transport_template.c
64libgnunet_plugin_transport_template_la_LDFLAGS = \
65 $(GN_PLUGIN_LDFLAGS)
66
67
68check_PROGRAMS = \
69 test_transport_api
70# TODO: add tests for tcp, udp, http, nat, etc.
71
72TESTS = $(check_PROGRAMS)
73
74test_transport_api_SOURCES = \
75 test_transport_api.c
76test_transport_api_LDADD = \
77 $(top_builddir)/src/transport/libgnunettransport.la \
78 $(top_builddir)/src/util/libgnunetutil.la
79
80
81EXTRA_DIST = \
82 test_transport_api_data.conf \
83 test_transport_api_peer1.conf \
84 test_transport_api_peer2.conf
diff --git a/src/transport/NOTES b/src/transport/NOTES
new file mode 100644
index 000000000..41404e1f9
--- /dev/null
+++ b/src/transport/NOTES
@@ -0,0 +1,46 @@
1KEY DESIGN CHOICES:
2 - who decides which connections to keep/create?
3 => higher level session/key management!
4 - who enforces things like F2F topology, etc?
5 => higher level session/key management!
6 - who tracks all known HELLOs & validates?
7 => We validate, PEERINFO tracks!
8 - who advertises our HELLO?
9 => us! (need background job; previously: advertising)
10 - who advertises other peers HELLOs?
11 => higher level (core?)
12 - who does bootstrapping?
13 => bootstrap service (external!)
14 - who enforces inbound bandwidth limits?
15 => transport-service and plugins! (previously: core);
16 either by limiting reads (TCP) or discarding packets
17 (transport-service)
18 - who enforces outbound bandwidth limits?
19 => transport_api!
20 - who decides outbound bandwidth limits?
21 => other peer, via core (need authenticated limits!)
22 - who decides inbound bandwidth limits?
23 => core / apps above core (need trust info)
24 - cost function for transports is latency estimate in ms
25 => plugin provides latency data, transport-service
26 selects plugin(s) for transmission
27 - who is responsible for fragmentation?
28 => plugins! (may use common shared library)
29 - should we require UDP to be reliable?
30 => NO. There are other places that may (rarely)
31 use messages that we can not fix
32 - how do we access the 50% of service that we need for TCP/UDP
33 from service.c without code replication or getting 50%
34 that we do not want (i.e. shutdown, pid-file-writing, etc.)
35 => use GNUNET_SERVICE_start/stop functions!
36 - At what level do we manage timeouts?
37 => At the plugin (TCP connections),
38 transport-service (neighbours) and
39 core (sessions) level!
40 => All plugins have to disconnect before service-level
41 disconnect occurs
42 => We can have a plugin-connection die, but the session
43 survives!
44 => We can have a session die (no further authenticated
45 communication) even if the plugin thinks it is still
46 up!
diff --git a/src/transport/gnunet-service-transport.c b/src/transport/gnunet-service-transport.c
new file mode 100644
index 000000000..08745c378
--- /dev/null
+++ b/src/transport/gnunet-service-transport.c
@@ -0,0 +1,2852 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file transport/gnunet-service-transport.c
23 * @brief low-level P2P messaging
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - if we do not receive an ACK in response to our
28 * HELLO, retransmit HELLO!
29 */
30#include "platform.h"
31#include "gnunet_client_lib.h"
32#include "gnunet_getopt_lib.h"
33#include "gnunet_hello_lib.h"
34#include "gnunet_os_lib.h"
35#include "gnunet_peerinfo_service.h"
36#include "gnunet_plugin_lib.h"
37#include "gnunet_protocols.h"
38#include "gnunet_service_lib.h"
39#include "gnunet_signatures.h"
40#include "plugin_transport.h"
41#include "transport.h"
42
43/**
44 * How many messages can we have pending for a given client process
45 * before we start to drop incoming messages? We typically should
46 * have only one client and so this would be the primary buffer for
47 * messages, so the number should be chosen rather generously.
48 *
49 * The expectation here is that most of the time the queue is large
50 * enough so that a drop is virtually never required.
51 */
52#define MAX_PENDING 128
53
54/**
55 * How often should we try to reconnect to a peer using a particular
56 * transport plugin before giving up? Note that the plugin may be
57 * added back to the list after PLUGIN_RETRY_FREQUENCY expires.
58 */
59#define MAX_CONNECT_RETRY 3
60
61/**
62 * How often must a peer violate bandwidth quotas before we start
63 * to simply drop its messages?
64 */
65#define QUOTA_VIOLATION_DROP_THRESHOLD 100
66
67/**
68 * How long until a HELLO verification attempt should time out?
69 */
70#define HELLO_VERIFICATION_TIMEOUT GNUNET_TIME_UNIT_MINUTES
71
72/**
73 * How often do we re-add (cheaper) plugins to our list of plugins
74 * to try for a given connected peer?
75 */
76#define PLUGIN_RETRY_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
77
78/**
79 * After how long do we expire an address in a HELLO
80 * that we just validated? This value is also used
81 * for our own addresses when we create a HELLO.
82 */
83#define HELLO_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)
84
85/**
86 * After how long do we consider a connection to a peer dead
87 * if we don't receive messages from the peer?
88 */
89#define IDLE_CONNECTION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
90
91
92/**
93 * Entry in linked list of network addresses.
94 */
95struct AddressList
96{
97 /**
98 * This is a linked list.
99 */
100 struct AddressList *next;
101
102 /**
103 * The address, actually a pointer to the end
104 * of this struct. Do not free!
105 */
106 void *addr;
107
108 /**
109 * How long until we auto-expire this address (unless it is
110 * re-confirmed by the transport)?
111 */
112 struct GNUNET_TIME_Absolute expires;
113
114 /**
115 * Length of addr.
116 */
117 size_t addrlen;
118
119};
120
121
122/**
123 * Entry in linked list of all of our plugins.
124 */
125struct TransportPlugin
126{
127
128 /**
129 * This is a linked list.
130 */
131 struct TransportPlugin *next;
132
133 /**
134 * API of the transport as returned by the plugin's
135 * initialization function.
136 */
137 struct GNUNET_TRANSPORT_PluginFunctions *api;
138
139 /**
140 * Short name for the plugin (i.e. "tcp").
141 */
142 char *short_name;
143
144 /**
145 * Name of the library (i.e. "gnunet_plugin_transport_tcp").
146 */
147 char *lib_name;
148
149 /**
150 * List of our known addresses for this transport.
151 */
152 struct AddressList *addresses;
153
154 /**
155 * Environment this transport service is using
156 * for this plugin.
157 */
158 struct GNUNET_TRANSPORT_PluginEnvironment env;
159
160 /**
161 * ID of task that is used to clean up expired addresses.
162 */
163 GNUNET_SCHEDULER_TaskIdentifier address_update_task;
164
165
166 /**
167 * Set to GNUNET_YES if we need to scrap the existing
168 * list of "addresses" and start fresh when we receive
169 * the next address update from a transport. Set to
170 * GNUNET_NO if we should just add the new address
171 * to the list and wait for the commit call.
172 */
173 int rebuild;
174};
175
176struct NeighbourList;
177
178/**
179 * For each neighbour we keep a list of messages
180 * that we still want to transmit to the neighbour.
181 */
182struct MessageQueue
183{
184
185 /**
186 * This is a linked list.
187 */
188 struct MessageQueue *next;
189
190 /**
191 * The message we want to transmit.
192 */
193 struct GNUNET_MessageHeader *message;
194
195 /**
196 * Client responsible for queueing the message;
197 * used to check that a client has not two messages
198 * pending for the same target. Can be NULL.
199 */
200 struct TransportClient *client;
201
202 /**
203 * Neighbour this entry belongs to.
204 */
205 struct NeighbourList *neighbour;
206
207 /**
208 * Plugin that we used for the transmission.
209 * NULL until we scheduled a transmission.
210 */
211 struct TransportPlugin *plugin;
212
213 /**
214 * Internal message of the transport system that should not be
215 * included in the usual SEND-SEND_OK transmission confirmation
216 * traffic management scheme. Typically, "internal_msg" will
217 * be set whenever "client" is NULL (but it is not strictly
218 * required).
219 */
220 int internal_msg;
221
222};
223
224
225/**
226 * For a given Neighbour, which plugins are available
227 * to talk to this peer and what are their costs?
228 */
229struct ReadyList
230{
231
232 /**
233 * This is a linked list.
234 */
235 struct ReadyList *next;
236
237 /**
238 * Which of our transport plugins does this entry
239 * represent?
240 */
241 struct TransportPlugin *plugin;
242
243 /**
244 * Neighbour this entry belongs to.
245 */
246 struct NeighbourList *neighbour;
247
248 /**
249 * Opaque handle (specific to the plugin) for the
250 * connection to our target; can be NULL.
251 */
252 void *plugin_handle;
253
254 /**
255 * What was the last latency observed for this plugin
256 * and peer? Invalid if connected is GNUNET_NO.
257 */
258 struct GNUNET_TIME_Relative latency;
259
260 /**
261 * If we did not successfully transmit a message to the
262 * given peer via this connection during the specified
263 * time, we should consider the connection to be dead.
264 * This is used in the case that a TCP transport simply
265 * stalls writing to the stream but does not formerly
266 * get a signal that the other peer died.
267 */
268 struct GNUNET_TIME_Absolute timeout;
269
270 /**
271 * Is this plugin currently connected? The first time
272 * we transmit or send data to a peer via a particular
273 * plugin, we set this to GNUNET_YES. If we later get
274 * an error (disconnect notification or transmission
275 * failure), we set it back to GNUNET_NO. Each time the
276 * value is set to GNUNET_YES, we increment the
277 * "connect_attempts" counter. If that one reaches a
278 * particular threshold, we consider the plugin to not
279 * be working properly at this time for the given peer
280 * and remove it from the eligible list.
281 */
282 int connected;
283
284 /**
285 * How often have we tried to connect using this plugin?
286 */
287 unsigned int connect_attempts;
288
289 /**
290 * Is this plugin ready to transmit to the specific
291 * target? GNUNET_NO if not. Initially, all plugins
292 * are marked ready. If a transmission is in progress,
293 * "transmit_ready" is set to GNUNET_NO.
294 */
295 int transmit_ready;
296
297};
298
299
300/**
301 * Entry in linked list of all of our current neighbours.
302 */
303struct NeighbourList
304{
305
306 /**
307 * This is a linked list.
308 */
309 struct NeighbourList *next;
310
311 /**
312 * Which of our transports is connected to this peer
313 * and what is their status?
314 */
315 struct ReadyList *plugins;
316
317 /**
318 * List of messages we would like to send to this peer;
319 * must contain at most one message per client.
320 */
321 struct MessageQueue *messages;
322
323 /**
324 * Identity of this neighbour.
325 */
326 struct GNUNET_PeerIdentity id;
327
328 /**
329 * ID of task scheduled to run when this peer is about to
330 * time out (will free resources associated with the peer).
331 */
332 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
333
334 /**
335 * How long until we should consider this peer dead
336 * (if we don't receive another message in the
337 * meantime)?
338 */
339 struct GNUNET_TIME_Absolute peer_timeout;
340
341 /**
342 * At what time did we reset last_received last?
343 */
344 struct GNUNET_TIME_Absolute last_quota_update;
345
346 /**
347 * At what time should we try to again add plugins to
348 * our ready list?
349 */
350 struct GNUNET_TIME_Absolute retry_plugins_time;
351
352 /**
353 * How many bytes have we received since the "last_quota_update"
354 * timestamp?
355 */
356 uint64_t last_received;
357
358 /**
359 * Global quota for outbound traffic for the neighbour in bytes/ms.
360 */
361 uint32_t quota_in;
362
363 /**
364 * What is the latest version of our HELLO that we have
365 * sent to this neighbour?
366 */
367 unsigned int hello_version_sent;
368
369 /**
370 * How often has the other peer (recently) violated the
371 * inbound traffic limit? Incremented by 10 per violation,
372 * decremented by 1 per non-violation (for each
373 * time interval).
374 */
375 unsigned int quota_violation_count;
376
377 /**
378 * Have we seen an ACK from this neighbour in the past?
379 * (used to make up a fake ACK for clients connecting after
380 * the neighbour connected to us).
381 */
382 int saw_ack;
383
384};
385
386
387/**
388 * Linked list of messages to be transmitted to
389 * the client. Each entry is followed by the
390 * actual message.
391 */
392struct ClientMessageQueueEntry
393{
394 /**
395 * This is a linked list.
396 */
397 struct ClientMessageQueueEntry *next;
398};
399
400
401/**
402 * Client connected to the transport service.
403 */
404struct TransportClient
405{
406
407 /**
408 * This is a linked list.
409 */
410 struct TransportClient *next;
411
412 /**
413 * Handle to the client.
414 */
415 struct GNUNET_SERVER_Client *client;
416
417 /**
418 * Linked list of messages yet to be transmitted to
419 * the client.
420 */
421 struct ClientMessageQueueEntry *message_queue_head;
422
423 /**
424 * Tail of linked list of messages yet to be transmitted to the
425 * client.
426 */
427 struct ClientMessageQueueEntry *message_queue_tail;
428
429 /**
430 * Is a call to "transmit_send_continuation" pending? If so, we
431 * must not free this struct (even if the corresponding client
432 * disconnects) and instead only remove it from the linked list and
433 * set the "client" field to NULL.
434 */
435 int tcs_pending;
436
437 /**
438 * Length of the list of messages pending for this client.
439 */
440 unsigned int message_count;
441
442};
443
444
445/**
446 * Message used to ask a peer to validate receipt (to check an address
447 * from a HELLO). Followed by the address used. Note that the
448 * recipients response does not affirm that he has this address,
449 * only that he got the challenge message.
450 */
451struct ValidationChallengeMessage
452{
453
454 /**
455 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PING
456 */
457 struct GNUNET_MessageHeader header;
458
459 /**
460 * What are we signing and why?
461 */
462 struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
463
464 /**
465 * Random challenge number (in network byte order).
466 */
467 uint32_t challenge GNUNET_PACKED;
468
469 /**
470 * Who is the intended recipient?
471 */
472 struct GNUNET_PeerIdentity target;
473};
474
475
476/**
477 * Message used to validate a HELLO. If this was
478 * the right recipient, the response is a signature
479 * of the original validation request. The
480 * challenge is included in the confirmation to make
481 * matching of replies to requests possible.
482 */
483struct ValidationChallengeResponse
484{
485
486 /**
487 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PONG
488 */
489 struct GNUNET_MessageHeader header;
490
491 /**
492 * Random challenge number (in network byte order).
493 */
494 uint32_t challenge GNUNET_PACKED;
495
496 /**
497 * Who signed this message?
498 */
499 struct GNUNET_PeerIdentity sender;
500
501 /**
502 * Signature.
503 */
504 struct GNUNET_CRYPTO_RsaSignature signature;
505
506};
507
508
509/**
510 * For each HELLO, we may have to validate multiple addresses;
511 * each address gets its own request entry.
512 */
513struct ValidationAddress
514{
515 /**
516 * This is a linked list.
517 */
518 struct ValidationAddress *next;
519
520 /**
521 * Our challenge message. Points to after this
522 * struct, so this field should not be freed.
523 */
524 struct ValidationChallengeMessage *msg;
525
526 /**
527 * Name of the transport.
528 */
529 char *transport_name;
530
531 /**
532 * When should this validated address expire?
533 */
534 struct GNUNET_TIME_Absolute expiration;
535
536 /**
537 * Length of the address we are validating.
538 */
539 size_t addr_len;
540
541 /**
542 * Set to GNUNET_YES if the challenge was met,
543 * GNUNET_SYSERR if we know it failed, GNUNET_NO
544 * if we are waiting on a response.
545 */
546 int ok;
547};
548
549
550/**
551 * Entry in linked list of all HELLOs awaiting validation.
552 */
553struct ValidationList
554{
555
556 /**
557 * This is a linked list.
558 */
559 struct ValidationList *next;
560
561 /**
562 * Linked list with one entry per address from the HELLO
563 * that needs to be validated.
564 */
565 struct ValidationAddress *addresses;
566
567 /**
568 * The public key of the peer.
569 */
570 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey;
571
572 /**
573 * When does this record time-out? (assuming the
574 * challenge goes unanswered)
575 */
576 struct GNUNET_TIME_Absolute timeout;
577
578};
579
580
581/**
582 * HELLOs awaiting validation.
583 */
584static struct ValidationList *pending_validations;
585
586/**
587 * Our HELLO message.
588 */
589static struct GNUNET_HELLO_Message *our_hello;
590
591/**
592 * "version" of "our_hello". Used to see if a given
593 * neighbour has already been sent the latest version
594 * of our HELLO message.
595 */
596static unsigned int our_hello_version;
597
598/**
599 * Our public key.
600 */
601static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key;
602
603/**
604 * Our identity.
605 */
606static struct GNUNET_PeerIdentity my_identity;
607
608/**
609 * Our private key.
610 */
611static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
612
613/**
614 * Our scheduler.
615 */
616struct GNUNET_SCHEDULER_Handle *sched;
617
618/**
619 * Our configuration.
620 */
621struct GNUNET_CONFIGURATION_Handle *cfg;
622
623/**
624 * Linked list of all clients to this service.
625 */
626static struct TransportClient *clients;
627
628/**
629 * All loaded plugins.
630 */
631static struct TransportPlugin *plugins;
632
633/**
634 * Our server.
635 */
636static struct GNUNET_SERVER_Handle *server;
637
638/**
639 * All known neighbours and their HELLOs.
640 */
641static struct NeighbourList *neighbours;
642
643/**
644 * Default bandwidth quota for receiving for new peers in bytes/ms.
645 */
646static uint32_t default_quota_in;
647
648/**
649 * Default bandwidth quota for sending for new peers in bytes/ms.
650 */
651static uint32_t default_quota_out;
652
653/**
654 * Number of neighbours we'd like to have.
655 */
656static uint32_t max_connect_per_transport;
657
658
659/**
660 * Find an entry in the neighbour list for a particular peer.
661 *
662 * @return NULL if not found.
663 */
664static struct NeighbourList *
665find_neighbour (const struct GNUNET_PeerIdentity *key)
666{
667 struct NeighbourList *head = neighbours;
668 while ((head != NULL) &&
669 (0 != memcmp (key, &head->id, sizeof (struct GNUNET_PeerIdentity))))
670 head = head->next;
671 return head;
672}
673
674
675/**
676 * Find an entry in the transport list for a particular transport.
677 *
678 * @return NULL if not found.
679 */
680static struct TransportPlugin *
681find_transport (const char *short_name)
682{
683 struct TransportPlugin *head = plugins;
684 while ((head != NULL) && (0 != strcmp (short_name, head->short_name)))
685 head = head->next;
686 return head;
687}
688
689
690/**
691 * Update the quota values for the given neighbour now.
692 */
693static void
694update_quota (struct NeighbourList *n)
695{
696 struct GNUNET_TIME_Relative delta;
697 uint64_t allowed;
698 uint64_t remaining;
699
700 delta = GNUNET_TIME_absolute_get_duration (n->last_quota_update);
701 if (delta.value < MIN_QUOTA_REFRESH_TIME)
702 return; /* not enough time passed for doing quota update */
703 allowed = delta.value * n->quota_in;
704 if (n->last_received < allowed)
705 {
706 remaining = allowed - n->last_received;
707 if (n->quota_in > 0)
708 remaining /= n->quota_in;
709 else
710 remaining = 0;
711 if (remaining > MAX_BANDWIDTH_CARRY)
712 remaining = MAX_BANDWIDTH_CARRY;
713 n->last_received = 0;
714 n->last_quota_update = GNUNET_TIME_absolute_get ();
715 n->last_quota_update.value -= remaining;
716 if (n->quota_violation_count > 0)
717 n->quota_violation_count--;
718 }
719 else
720 {
721 n->last_received -= allowed;
722 n->last_quota_update = GNUNET_TIME_absolute_get ();
723 if (n->last_received > allowed)
724 {
725 /* more than twice the allowed rate! */
726 n->quota_violation_count += 10;
727 }
728 }
729}
730
731
732/**
733 * Function called to notify a client about the socket
734 * being ready to queue more data. "buf" will be
735 * NULL and "size" zero if the socket was closed for
736 * writing in the meantime.
737 *
738 * @param cls closure
739 * @param size number of bytes available in buf
740 * @param buf where the callee should write the message
741 * @return number of bytes written to buf
742 */
743static size_t
744transmit_to_client_callback (void *cls, size_t size, void *buf)
745{
746 struct TransportClient *client = cls;
747 struct ClientMessageQueueEntry *q;
748 uint16_t msize;
749 size_t tsize;
750 const struct GNUNET_MessageHeader *msg;
751 struct GNUNET_NETWORK_TransmitHandle *th;
752 char *cbuf;
753
754 if (buf == NULL)
755 {
756 /* fatal error with client, free message queue! */
757 while (NULL != (q = client->message_queue_head))
758 {
759 client->message_queue_head = q->next;
760 GNUNET_free (q);
761 }
762 client->message_queue_tail = NULL;
763 client->message_count = 0;
764 return 0;
765 }
766 cbuf = buf;
767 tsize = 0;
768 while (NULL != (q = client->message_queue_head))
769 {
770 msg = (const struct GNUNET_MessageHeader *) &q[1];
771 msize = ntohs (msg->size);
772 if (msize + tsize > size)
773 break;
774 client->message_queue_head = q->next;
775 if (q->next == NULL)
776 client->message_queue_tail = NULL;
777 memcpy (&cbuf[tsize], msg, msize);
778 tsize += msize;
779 GNUNET_free (q);
780 client->message_count--;
781 }
782 GNUNET_assert (tsize > 0);
783 if (NULL != q)
784 {
785 th = GNUNET_SERVER_notify_transmit_ready (client->client,
786 msize,
787 GNUNET_TIME_UNIT_FOREVER_REL,
788 &transmit_to_client_callback,
789 client);
790 GNUNET_assert (th != NULL);
791 }
792 return tsize;
793}
794
795
796/**
797 * Send the specified message to the specified client. Since multiple
798 * messages may be pending for the same client at a time, this code
799 * makes sure that no message is lost.
800 *
801 * @param client client to transmit the message to
802 * @param msg the message to send
803 * @param may_drop can this message be dropped if the
804 * message queue for this client is getting far too large?
805 */
806static void
807transmit_to_client (struct TransportClient *client,
808 const struct GNUNET_MessageHeader *msg, int may_drop)
809{
810 struct ClientMessageQueueEntry *q;
811 uint16_t msize;
812 struct GNUNET_NETWORK_TransmitHandle *th;
813
814 if ((client->message_count >= MAX_PENDING) && (GNUNET_YES == may_drop))
815 {
816 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
817 _
818 ("Dropping message, have %u messages pending (%u is the soft limit)\n"),
819 client->message_count, MAX_PENDING);
820 /* TODO: call to statistics... */
821 return;
822 }
823 client->message_count++;
824 msize = ntohs (msg->size);
825 q = GNUNET_malloc (sizeof (struct ClientMessageQueueEntry) + msize);
826 memcpy (&q[1], msg, msize);
827 /* append to message queue */
828 if (client->message_queue_tail == NULL)
829 {
830 client->message_queue_tail = q;
831 }
832 else
833 {
834 client->message_queue_tail->next = q;
835 client->message_queue_tail = q;
836 }
837 if (client->message_queue_head == NULL)
838 {
839 client->message_queue_head = q;
840 th = GNUNET_SERVER_notify_transmit_ready (client->client,
841 msize,
842 GNUNET_TIME_UNIT_FOREVER_REL,
843 &transmit_to_client_callback,
844 client);
845 GNUNET_assert (th != NULL);
846 }
847}
848
849
850/**
851 * Find alternative plugins for communication.
852 *
853 * @param neighbour for which neighbour should we try to find
854 * more plugins?
855 */
856static void
857try_alternative_plugins (struct NeighbourList *neighbour)
858{
859 struct ReadyList *rl;
860
861 if ((neighbour->plugins != NULL) &&
862 (neighbour->retry_plugins_time.value >
863 GNUNET_TIME_absolute_get ().value))
864 return; /* don't try right now */
865 neighbour->retry_plugins_time
866 = GNUNET_TIME_relative_to_absolute (PLUGIN_RETRY_FREQUENCY);
867
868 rl = neighbour->plugins;
869 while (rl != NULL)
870 {
871 if (rl->connect_attempts > 0)
872 rl->connect_attempts--; /* amnesty */
873 rl = rl->next;
874 }
875
876}
877
878
879/**
880 * Check the ready list for the given neighbour and
881 * if a plugin is ready for transmission (and if we
882 * have a message), do so!
883 *
884 * @param neighbour target peer for which to check the plugins
885 */
886static void try_transmission_to_peer (struct NeighbourList *neighbour);
887
888
889/**
890 * Function called by the GNUNET_TRANSPORT_TransmitFunction
891 * upon "completion" of a send request. This tells the API
892 * that it is now legal to send another message to the given
893 * peer.
894 *
895 * @param cls closure, identifies the entry on the
896 * message queue that was transmitted and the
897 * client responsible for queueing the message
898 * @param rl identifies plugin used for the transmission for
899 * this neighbour; needs to be re-enabled for
900 * future transmissions
901 * @param target the peer receiving the message
902 * @param result GNUNET_OK on success, if the transmission
903 * failed, we should not tell the client to transmit
904 * more messages
905 */
906static void
907transmit_send_continuation (void *cls,
908 struct ReadyList *rl,
909 const struct GNUNET_PeerIdentity *target,
910 int result)
911{
912 struct MessageQueue *mq = cls;
913 struct SendOkMessage send_ok_msg;
914 struct NeighbourList *n;
915
916 GNUNET_assert (mq != NULL);
917 n = mq->neighbour;
918 GNUNET_assert (0 ==
919 memcmp (&n->id, target,
920 sizeof (struct GNUNET_PeerIdentity)));
921 if (rl == NULL)
922 {
923 rl = n->plugins;
924 while ((rl != NULL) && (rl->plugin != mq->plugin))
925 rl = rl->next;
926 GNUNET_assert (rl != NULL);
927 }
928 if (result == GNUNET_OK)
929 rl->timeout = GNUNET_TIME_relative_to_absolute (IDLE_CONNECTION_TIMEOUT);
930 else
931 rl->connected = GNUNET_NO;
932 if (!mq->internal_msg)
933 rl->transmit_ready = GNUNET_YES;
934 if (mq->client != NULL)
935 {
936 send_ok_msg.header.size = htons (sizeof (send_ok_msg));
937 send_ok_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK);
938 send_ok_msg.success = htonl (result);
939 send_ok_msg.peer = n->id;
940 transmit_to_client (mq->client, &send_ok_msg.header, GNUNET_NO);
941 }
942 GNUNET_free (mq->message);
943 GNUNET_free (mq);
944 /* one plugin just became ready again, try transmitting
945 another message (if available) */
946 try_transmission_to_peer (n);
947}
948
949
950
951
952/**
953 * We could not use an existing (or validated) connection to
954 * talk to a peer. Try addresses that have not yet been
955 * validated.
956 *
957 * @param n neighbour we want to communicate with
958 * @return plugin ready to talk, or NULL if none is available
959 */
960static struct ReadyList *
961try_unvalidated_addresses (struct NeighbourList *n)
962{
963 struct ValidationList *vl;
964 struct ValidationAddress *va;
965 struct GNUNET_PeerIdentity id;
966 struct GNUNET_TIME_Absolute now;
967 unsigned int total;
968 unsigned int cnt;
969 struct ReadyList *rl;
970 struct TransportPlugin *plugin;
971
972#if DEBUG_TRANSPORT
973 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
974 "Trying to connect to `%4s' using unvalidated addresses\n",
975 GNUNET_i2s (&n->id));
976#endif
977 /* NOTE: this function needs to not only identify the
978 plugin but also setup "plugin_handle", binding it to the
979 right address using the plugin's "send_to" API */
980 now = GNUNET_TIME_absolute_get ();
981 vl = pending_validations;
982 while (vl != NULL)
983 {
984 GNUNET_CRYPTO_hash (&vl->publicKey,
985 sizeof (struct
986 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
987 &id.hashPubKey);
988 if (0 == memcmp (&id, &n->id, sizeof (struct GNUNET_PeerIdentity)))
989 break;
990 vl = vl->next;
991 }
992 if (vl == NULL)
993 {
994#if DEBUG_TRANSPORT
995 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
996 "No unvalidated address found for peer `%4s'\n",
997 GNUNET_i2s (&n->id));
998#endif
999 return NULL;
1000 }
1001 total = 0;
1002 cnt = 0;
1003 va = vl->addresses;
1004 while (va != NULL)
1005 {
1006 cnt++;
1007 if (va->expiration.value > now.value)
1008 total++;
1009 va = va->next;
1010 }
1011 if (total == 0)
1012 {
1013#if DEBUG_TRANSPORT
1014 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1015 "All %u unvalidated addresses for peer have expired\n",
1016 cnt);
1017#endif
1018 return NULL;
1019 }
1020 total = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
1021 for (va = vl->addresses; va != NULL; va = va->next)
1022 {
1023 if (va->expiration.value <= now.value)
1024 continue;
1025 if (total > 0)
1026 {
1027 total--;
1028 continue;
1029 }
1030#if DEBUG_TRANSPORT
1031 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
1032 "Trying unvalidated address of `%s' transport\n",
1033 va->transport_name);
1034#endif
1035 plugin = find_transport (va->transport_name);
1036 if (plugin == NULL)
1037 {
1038 GNUNET_break (0);
1039 break;
1040 }
1041 rl = GNUNET_malloc (sizeof (struct ReadyList));
1042 rl->next = n->plugins;
1043 n->plugins = rl;
1044 rl->plugin = plugin;
1045 rl->plugin_handle = plugin->api->send_to (plugin->api->cls,
1046 &n->id,
1047 NULL,
1048 NULL,
1049 GNUNET_TIME_UNIT_ZERO,
1050 &va->msg[1], va->addr_len);
1051 rl->transmit_ready = GNUNET_YES;
1052 return rl;
1053 }
1054 return NULL;
1055}
1056
1057
1058/**
1059 * Check the ready list for the given neighbour and
1060 * if a plugin is ready for transmission (and if we
1061 * have a message), do so!
1062 */
1063static void
1064try_transmission_to_peer (struct NeighbourList *neighbour)
1065{
1066 struct ReadyList *pos;
1067 struct GNUNET_TIME_Relative min_latency;
1068 struct ReadyList *rl;
1069 struct MessageQueue *mq;
1070 struct GNUNET_TIME_Absolute now;
1071
1072 if (neighbour->messages == NULL)
1073 return; /* nothing to do */
1074 try_alternative_plugins (neighbour);
1075 min_latency = GNUNET_TIME_UNIT_FOREVER_REL;
1076 rl = NULL;
1077 mq = neighbour->messages;
1078 now = GNUNET_TIME_absolute_get ();
1079 pos = neighbour->plugins;
1080 while (pos != NULL)
1081 {
1082 /* set plugins that are inactive for a long time back to disconnected */
1083 if ((pos->timeout.value < now.value) && (pos->connected == GNUNET_YES))
1084 {
1085#if DEBUG_TRANSPORT
1086 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1087 "Marking long-time inactive connection to `%4s' as down.\n",
1088 GNUNET_i2s (&neighbour->id));
1089#endif
1090 pos->connected = GNUNET_NO;
1091 }
1092 if (((GNUNET_YES == pos->transmit_ready) ||
1093 (mq->internal_msg)) &&
1094 (pos->connect_attempts < MAX_CONNECT_RETRY) &&
1095 ((rl == NULL) || (min_latency.value > pos->latency.value)))
1096 {
1097 rl = pos;
1098 min_latency = pos->latency;
1099 }
1100 pos = pos->next;
1101 }
1102 if (rl == NULL)
1103 rl = try_unvalidated_addresses (neighbour);
1104 if (rl == NULL)
1105 {
1106#if DEBUG_TRANSPORT
1107 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1108 "No plugin ready to transmit message\n");
1109#endif
1110 return; /* nobody ready */
1111 }
1112 if (GNUNET_NO == rl->connected)
1113 {
1114 rl->connect_attempts++;
1115 rl->connected = GNUNET_YES;
1116 }
1117 neighbour->messages = mq->next;
1118 mq->plugin = rl->plugin;
1119 if (!mq->internal_msg)
1120 rl->transmit_ready = GNUNET_NO;
1121#if DEBUG_TRANSPORT
1122 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1123 "Giving message of type `%u' for `%4s' to plugin `%s'\n",
1124 ntohs (mq->message->type),
1125 GNUNET_i2s (&neighbour->id), rl->plugin->short_name);
1126#endif
1127 rl->plugin_handle
1128 = rl->plugin->api->send (rl->plugin->api->cls,
1129 rl->plugin_handle,
1130 rl,
1131 &neighbour->id,
1132 mq->message,
1133 IDLE_CONNECTION_TIMEOUT,
1134 &transmit_send_continuation, mq);
1135}
1136
1137
1138/**
1139 * Send the specified message to the specified peer.
1140 *
1141 * @param client source of the transmission request (can be NULL)
1142 * @param msg message to send
1143 * @param is_internal is this an internal message
1144 * @param neighbour handle to the neighbour for transmission
1145 */
1146static void
1147transmit_to_peer (struct TransportClient *client,
1148 const struct GNUNET_MessageHeader *msg,
1149 int is_internal, struct NeighbourList *neighbour)
1150{
1151 struct MessageQueue *mq;
1152 struct MessageQueue *mqe;
1153 struct GNUNET_MessageHeader *m;
1154
1155#if DEBUG_TRANSPORT
1156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1157 _("Sending message of type %u to peer `%4s'\n"),
1158 ntohs (msg->type), GNUNET_i2s (&neighbour->id));
1159#endif
1160 if (client != NULL)
1161 {
1162 /* check for duplicate submission */
1163 mq = neighbour->messages;
1164 while (NULL != mq)
1165 {
1166 if (mq->client == client)
1167 {
1168 /* client transmitted to same peer twice
1169 before getting SendOk! */
1170 GNUNET_break (0);
1171 return;
1172 }
1173 mq = mq->next;
1174 }
1175 }
1176 mq = GNUNET_malloc (sizeof (struct MessageQueue));
1177 mq->client = client;
1178 m = GNUNET_malloc (ntohs (msg->size));
1179 memcpy (m, msg, ntohs (msg->size));
1180 mq->message = m;
1181 mq->neighbour = neighbour;
1182 mq->internal_msg = is_internal;
1183
1184 /* find tail */
1185 mqe = neighbour->messages;
1186 if (mqe != NULL)
1187 while (mqe->next != NULL)
1188 mqe = mqe->next;
1189 if (mqe == NULL)
1190 {
1191 /* new head */
1192 neighbour->messages = mq;
1193 try_transmission_to_peer (neighbour);
1194 }
1195 else
1196 {
1197 /* append */
1198 mqe->next = mq;
1199 }
1200}
1201
1202
1203struct GeneratorContext
1204{
1205 struct TransportPlugin *plug_pos;
1206 struct AddressList *addr_pos;
1207 struct GNUNET_TIME_Absolute expiration;
1208};
1209
1210
1211static size_t
1212address_generator (void *cls, size_t max, void *buf)
1213{
1214 struct GeneratorContext *gc = cls;
1215 size_t ret;
1216
1217 while ((gc->addr_pos == NULL) && (gc->plug_pos != NULL))
1218 {
1219 gc->plug_pos = gc->plug_pos->next;
1220 gc->addr_pos = (gc->plug_pos != NULL) ? gc->plug_pos->addresses : NULL;
1221 }
1222 if (NULL == gc->plug_pos)
1223 return 0;
1224 ret = GNUNET_HELLO_add_address (gc->plug_pos->short_name,
1225 gc->expiration,
1226 gc->addr_pos->addr,
1227 gc->addr_pos->addrlen, buf, max);
1228 gc->addr_pos = gc->addr_pos->next;
1229 return ret;
1230}
1231
1232
1233/**
1234 * Construct our HELLO message from all of the addresses of
1235 * all of the transports.
1236 */
1237static void
1238refresh_hello ()
1239{
1240 struct GNUNET_HELLO_Message *hello;
1241 struct TransportClient *cpos;
1242 struct NeighbourList *npos;
1243 struct GeneratorContext gc;
1244
1245#if DEBUG_TRANSPORT
1246 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
1247 "Refreshing my HELLO\n");
1248#endif
1249 gc.plug_pos = plugins;
1250 gc.addr_pos = plugins != NULL ? plugins->addresses : NULL;
1251 gc.expiration = GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION);
1252 hello = GNUNET_HELLO_create (&my_public_key, &address_generator, &gc);
1253 cpos = clients;
1254 while (cpos != NULL)
1255 {
1256 transmit_to_client (cpos,
1257 (const struct GNUNET_MessageHeader *) hello,
1258 GNUNET_NO);
1259 cpos = cpos->next;
1260 }
1261
1262 GNUNET_free_non_null (our_hello);
1263 our_hello = hello;
1264 our_hello_version++;
1265 npos = neighbours;
1266 while (npos != NULL)
1267 {
1268 transmit_to_peer (NULL,
1269 (const struct GNUNET_MessageHeader *) our_hello,
1270 GNUNET_YES, npos);
1271 npos = npos->next;
1272 }
1273}
1274
1275
1276/**
1277 * Task used to clean up expired addresses for a plugin.
1278 *
1279 * @param cls closure
1280 * @param tc context
1281 */
1282static void
1283expire_address_task (void *cls,
1284 const struct GNUNET_SCHEDULER_TaskContext *tc);
1285
1286
1287/**
1288 * Update the list of addresses for this plugin,
1289 * expiring those that are past their expiration date.
1290 *
1291 * @param plugin addresses of which plugin should be recomputed?
1292 * @param fresh set to GNUNET_YES if a new address was added
1293 * and we need to regenerate the HELLO even if nobody
1294 * expired
1295 */
1296static void
1297update_addresses (struct TransportPlugin *plugin, int fresh)
1298{
1299 struct GNUNET_TIME_Relative min_remaining;
1300 struct GNUNET_TIME_Relative remaining;
1301 struct GNUNET_TIME_Absolute now;
1302 struct AddressList *pos;
1303 struct AddressList *prev;
1304 struct AddressList *next;
1305 int expired;
1306
1307 if (plugin->address_update_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1308 GNUNET_SCHEDULER_cancel (plugin->env.sched, plugin->address_update_task);
1309 plugin->address_update_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1310 now = GNUNET_TIME_absolute_get ();
1311 min_remaining = GNUNET_TIME_UNIT_FOREVER_REL;
1312 expired = GNUNET_NO;
1313 prev = NULL;
1314 pos = plugin->addresses;
1315 while (pos != NULL)
1316 {
1317 next = pos->next;
1318 if (pos->expires.value < now.value)
1319 {
1320 expired = GNUNET_YES;
1321 if (prev == NULL)
1322 plugin->addresses = pos->next;
1323 else
1324 prev->next = pos->next;
1325 GNUNET_free (pos);
1326 }
1327 else
1328 {
1329 remaining = GNUNET_TIME_absolute_get_remaining (pos->expires);
1330 if (remaining.value < min_remaining.value)
1331 min_remaining = remaining;
1332 prev = pos;
1333 }
1334 pos = next;
1335 }
1336
1337 if (expired || fresh)
1338 refresh_hello ();
1339 if (min_remaining.value < GNUNET_TIME_UNIT_FOREVER_REL.value)
1340 plugin->address_update_task
1341 = GNUNET_SCHEDULER_add_delayed (plugin->env.sched,
1342 GNUNET_NO,
1343 GNUNET_SCHEDULER_PRIORITY_IDLE,
1344 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1345 min_remaining,
1346 &expire_address_task, plugin);
1347
1348}
1349
1350
1351/**
1352 * Task used to clean up expired addresses for a plugin.
1353 *
1354 * @param cls closure
1355 * @param tc context
1356 */
1357static void
1358expire_address_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1359{
1360 struct TransportPlugin *plugin = cls;
1361 plugin->address_update_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1362 update_addresses (plugin, GNUNET_NO);
1363}
1364
1365
1366/**
1367 * Function that must be called by each plugin to notify the
1368 * transport service about the addresses under which the transport
1369 * provided by the plugin can be reached.
1370 *
1371 * @param cls closure
1372 * @param name name of the transport that generated the address
1373 * @param addr one of the addresses of the host, NULL for the last address
1374 * the specific address format depends on the transport
1375 * @param addrlen length of the address
1376 * @param expires when should this address automatically expire?
1377 */
1378static void
1379plugin_env_notify_address (void *cls,
1380 const char *name,
1381 const void *addr,
1382 size_t addrlen,
1383 struct GNUNET_TIME_Relative expires)
1384{
1385 struct TransportPlugin *p = cls;
1386 struct AddressList *al;
1387 struct GNUNET_TIME_Absolute abex;
1388
1389#if DEBUG_TRANSPORT
1390 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1391 "Plugin `%s' informs us about a new address\n", name);
1392#endif
1393 abex = GNUNET_TIME_relative_to_absolute (expires);
1394 GNUNET_assert (p == find_transport (name));
1395
1396 al = p->addresses;
1397 while (al != NULL)
1398 {
1399 if ((addrlen == al->addrlen) && (0 == memcmp (addr, &al[1], addrlen)))
1400 {
1401 if (al->expires.value < abex.value)
1402 al->expires = abex;
1403 return;
1404 }
1405 al = al->next;
1406 }
1407 al = GNUNET_malloc (sizeof (struct AddressList) + addrlen);
1408 al->addr = &al[1];
1409 al->next = p->addresses;
1410 p->addresses = al;
1411 al->expires = abex;
1412 al->addrlen = addrlen;
1413 memcpy (&al[1], addr, addrlen);
1414 update_addresses (p, GNUNET_YES);
1415}
1416
1417
1418struct LookupHelloContext
1419{
1420 GNUNET_TRANSPORT_AddressCallback iterator;
1421
1422 void *iterator_cls;
1423};
1424
1425
1426static int
1427lookup_address_callback (void *cls,
1428 const char *tname,
1429 struct GNUNET_TIME_Absolute expiration,
1430 const void *addr, size_t addrlen)
1431{
1432 struct LookupHelloContext *lhc = cls;
1433 lhc->iterator (lhc->iterator_cls, tname, addr, addrlen);
1434 return GNUNET_OK;
1435}
1436
1437
1438static void
1439lookup_hello_callback (void *cls,
1440 const struct GNUNET_PeerIdentity *peer,
1441 const struct GNUNET_HELLO_Message *h, uint32_t trust)
1442{
1443 struct LookupHelloContext *lhc = cls;
1444
1445 if (peer == NULL)
1446 {
1447 lhc->iterator (lhc->iterator_cls, NULL, NULL, 0);
1448 GNUNET_free (lhc);
1449 return;
1450 }
1451 if (h == NULL)
1452 return;
1453 GNUNET_HELLO_iterate_addresses (h,
1454 GNUNET_NO, &lookup_address_callback, lhc);
1455}
1456
1457
1458/**
1459 * Function that allows a transport to query the known
1460 * network addresses for a given peer.
1461 *
1462 * @param cls closure
1463 * @param timeout after how long should we time out?
1464 * @param target which peer are we looking for?
1465 * @param iter function to call for each known address
1466 * @param iter_cls closure for iter
1467 */
1468static void
1469plugin_env_lookup_address (void *cls,
1470 struct GNUNET_TIME_Relative timeout,
1471 const struct GNUNET_PeerIdentity *target,
1472 GNUNET_TRANSPORT_AddressCallback iter,
1473 void *iter_cls)
1474{
1475 struct LookupHelloContext *lhc;
1476
1477 lhc = GNUNET_malloc (sizeof (struct LookupHelloContext));
1478 lhc->iterator = iter;
1479 lhc->iterator_cls = iter_cls;
1480 GNUNET_PEERINFO_for_all (cfg,
1481 sched,
1482 target, 0, timeout, &lookup_hello_callback, &lhc);
1483}
1484
1485
1486/**
1487 * Notify all of our clients about a peer connecting.
1488 */
1489static void
1490notify_clients_connect (const struct GNUNET_PeerIdentity *peer,
1491 struct GNUNET_TIME_Relative latency)
1492{
1493 struct ConnectInfoMessage cim;
1494 struct TransportClient *cpos;
1495
1496#if DEBUG_TRANSPORT
1497 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1498 "Informing clients about peer `%4s' connecting to us\n",
1499 GNUNET_i2s (peer));
1500#endif
1501 cim.header.size = htons (sizeof (struct ConnectInfoMessage));
1502 cim.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
1503 cim.quota_out = htonl (default_quota_out);
1504 cim.latency = GNUNET_TIME_relative_hton (latency);
1505 memcpy (&cim.id, peer, sizeof (struct GNUNET_PeerIdentity));
1506 cpos = clients;
1507 while (cpos != NULL)
1508 {
1509 transmit_to_client (cpos, &cim.header, GNUNET_NO);
1510 cpos = cpos->next;
1511 }
1512}
1513
1514
1515/**
1516 * Notify all of our clients about a peer disconnecting.
1517 */
1518static void
1519notify_clients_disconnect (const struct GNUNET_PeerIdentity *peer)
1520{
1521 struct DisconnectInfoMessage dim;
1522 struct TransportClient *cpos;
1523
1524#if DEBUG_TRANSPORT
1525 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1526 "Informing clients about peer `%4s' disconnecting\n",
1527 GNUNET_i2s (peer));
1528#endif
1529 dim.header.size = htons (sizeof (struct DisconnectInfoMessage));
1530 dim.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT);
1531 dim.reserved = htonl (0);
1532 memcpy (&dim.peer, peer, sizeof (struct GNUNET_PeerIdentity));
1533 cpos = clients;
1534 while (cpos != NULL)
1535 {
1536 transmit_to_client (cpos, &dim.header, GNUNET_NO);
1537 cpos = cpos->next;
1538 }
1539}
1540
1541
1542/**
1543 * Copy any validated addresses to buf.
1544 *
1545 * @return 0 once all addresses have been
1546 * returned
1547 */
1548static size_t
1549list_validated_addresses (void *cls, size_t max, void *buf)
1550{
1551 struct ValidationAddress **va = cls;
1552 size_t ret;
1553
1554 while ((NULL != *va) && ((*va)->ok != GNUNET_YES))
1555 *va = (*va)->next;
1556 if (NULL == *va)
1557 return 0;
1558 ret = GNUNET_HELLO_add_address ((*va)->transport_name,
1559 (*va)->expiration,
1560 &(*va)->msg[1], (*va)->addr_len, buf, max);
1561 *va = (*va)->next;
1562 return ret;
1563}
1564
1565
1566/**
1567 * HELLO validation cleanup task.
1568 */
1569static void
1570cleanup_validation (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1571{
1572 struct ValidationAddress *va;
1573 struct ValidationList *pos;
1574 struct ValidationList *prev;
1575 struct GNUNET_TIME_Absolute now;
1576 struct GNUNET_HELLO_Message *hello;
1577 struct GNUNET_PeerIdentity pid;
1578
1579#if DEBUG_TRANSPORT
1580 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
1581 "HELLO validation cleanup background task running...\n");
1582#endif
1583 now = GNUNET_TIME_absolute_get ();
1584 prev = NULL;
1585 pos = pending_validations;
1586 while (pos != NULL)
1587 {
1588 if (pos->timeout.value < now.value)
1589 {
1590 if (prev == NULL)
1591 pending_validations = pos->next;
1592 else
1593 prev->next = pos->next;
1594 va = pos->addresses;
1595 hello = GNUNET_HELLO_create (&pos->publicKey,
1596 &list_validated_addresses, &va);
1597 GNUNET_CRYPTO_hash (&pos->publicKey,
1598 sizeof (struct
1599 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1600 &pid.hashPubKey);
1601#if DEBUG_TRANSPORT
1602 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1603 "Creating persistent `%s' message for peer `%4s' based on confirmed addresses.\n",
1604 "HELLO", GNUNET_i2s (&pid));
1605#endif
1606 GNUNET_PEERINFO_add_peer (cfg, sched, &pid, hello);
1607 GNUNET_free (hello);
1608 while (NULL != (va = pos->addresses))
1609 {
1610 pos->addresses = va->next;
1611 GNUNET_free (va->transport_name);
1612 GNUNET_free (va);
1613 }
1614 GNUNET_free (pos);
1615 if (prev == NULL)
1616 pos = pending_validations;
1617 else
1618 pos = prev->next;
1619 continue;
1620 }
1621 prev = pos;
1622 pos = pos->next;
1623 }
1624
1625 /* finally, reschedule cleanup if needed; list is
1626 ordered by timeout, so we need the last element... */
1627 pos = pending_validations;
1628 while ((pos != NULL) && (pos->next != NULL))
1629 pos = pos->next;
1630 if (NULL != pos)
1631 GNUNET_SCHEDULER_add_delayed (sched,
1632 GNUNET_NO,
1633 GNUNET_SCHEDULER_PRIORITY_IDLE,
1634 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1635 GNUNET_TIME_absolute_get_remaining (pos->
1636 timeout),
1637 &cleanup_validation, NULL);
1638}
1639
1640
1641struct CheckHelloValidatedContext
1642{
1643 /**
1644 * Plugin for which we are validating.
1645 */
1646 struct TransportPlugin *plugin;
1647
1648 /**
1649 * Hello that we are validating.
1650 */
1651 struct GNUNET_HELLO_Message *hello;
1652
1653 /**
1654 * Validation list being build.
1655 */
1656 struct ValidationList *e;
1657};
1658
1659
1660/**
1661 * Append the given address to the list of entries
1662 * that need to be validated.
1663 */
1664static int
1665run_validation (void *cls,
1666 const char *tname,
1667 struct GNUNET_TIME_Absolute expiration,
1668 const void *addr, size_t addrlen)
1669{
1670 struct ValidationList *e = cls;
1671 struct TransportPlugin *tp;
1672 struct ValidationAddress *va;
1673 struct ValidationChallengeMessage *vcm;
1674
1675 tp = find_transport (tname);
1676 if (tp == NULL)
1677 {
1678 GNUNET_log (GNUNET_ERROR_TYPE_INFO |
1679 GNUNET_ERROR_TYPE_BULK,
1680 _
1681 ("Transport `%s' not loaded, will not try to validate peer address using this transport.\n"),
1682 tname);
1683 return GNUNET_OK;
1684 }
1685 va = GNUNET_malloc (sizeof (struct ValidationAddress) +
1686 sizeof (struct ValidationChallengeMessage) + addrlen);
1687 va->next = e->addresses;
1688 e->addresses = va;
1689 vcm = (struct ValidationChallengeMessage *) &va[1];
1690 va->msg = vcm;
1691 va->transport_name = GNUNET_strdup (tname);
1692 va->addr_len = addrlen;
1693 vcm->header.size =
1694 htons (sizeof (struct ValidationChallengeMessage) + addrlen);
1695 vcm->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PING);
1696 vcm->purpose.size =
1697 htonl (sizeof (struct ValidationChallengeMessage) + addrlen -
1698 sizeof (struct GNUNET_MessageHeader));
1699 vcm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO);
1700 vcm->challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1701 (unsigned int) -1);
1702 /* Note: vcm->target is set in check_hello_validated */
1703 memcpy (&vcm[1], addr, addrlen);
1704 return GNUNET_OK;
1705}
1706
1707
1708/**
1709 * Check if addresses in validated hello "h" overlap with
1710 * those in "chvc->hello" and update "chvc->hello" accordingly,
1711 * removing those addresses that have already been validated.
1712 */
1713static void
1714check_hello_validated (void *cls,
1715 const struct GNUNET_PeerIdentity *peer,
1716 const struct GNUNET_HELLO_Message *h, uint32_t trust)
1717{
1718 struct CheckHelloValidatedContext *chvc = cls;
1719 struct ValidationAddress *va;
1720 struct TransportPlugin *tp;
1721 int first_call;
1722
1723 first_call = GNUNET_NO;
1724 if (chvc->e == NULL)
1725 {
1726 first_call = GNUNET_YES;
1727 chvc->e = GNUNET_malloc (sizeof (struct ValidationList));
1728 GNUNET_HELLO_get_key (h != NULL ? h : chvc->hello, &chvc->e->publicKey);
1729 chvc->e->timeout =
1730 GNUNET_TIME_relative_to_absolute (HELLO_VERIFICATION_TIMEOUT);
1731 chvc->e->next = pending_validations;
1732 pending_validations = chvc->e;
1733 }
1734 if (h != NULL)
1735 {
1736 GNUNET_HELLO_iterate_new_addresses (chvc->hello,
1737 h,
1738 GNUNET_TIME_absolute_get (),
1739 &run_validation, chvc->e);
1740 }
1741 else if (GNUNET_YES == first_call)
1742 {
1743 /* no existing HELLO, all addresses are new */
1744 GNUNET_HELLO_iterate_addresses (chvc->hello,
1745 GNUNET_NO, &run_validation, chvc->e);
1746 }
1747 if (h != NULL)
1748 return; /* wait for next call */
1749 /* finally, transmit validation attempts */
1750 va = chvc->e->addresses;
1751 while (va != NULL)
1752 {
1753 GNUNET_CRYPTO_hash (&chvc->e->publicKey,
1754 sizeof (struct
1755 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1756 &va->msg->target.hashPubKey);
1757#if DEBUG_TRANSPORT
1758 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1759 "Establishing `%s' connection to validate `%s' of `%4s' (sending our `%s')\n",
1760 va->transport_name,
1761 "HELLO", GNUNET_i2s (&va->msg->target), "HELLO");
1762#endif
1763 tp = find_transport (va->transport_name);
1764 GNUNET_assert (tp != NULL);
1765 if (NULL ==
1766 tp->api->send_to (tp->api->cls,
1767 &va->msg->target,
1768 (const struct GNUNET_MessageHeader *) our_hello,
1769 &va->msg->header,
1770 HELLO_VERIFICATION_TIMEOUT,
1771 &va->msg[1], va->addr_len))
1772 va->ok = GNUNET_SYSERR;
1773 va = va->next;
1774 }
1775 if (chvc->e->next == NULL)
1776 GNUNET_SCHEDULER_add_delayed (sched,
1777 GNUNET_NO,
1778 GNUNET_SCHEDULER_PRIORITY_IDLE,
1779 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1780 GNUNET_TIME_absolute_get_remaining (chvc->
1781 e->
1782 timeout),
1783 &cleanup_validation, NULL);
1784 GNUNET_free (chvc);
1785}
1786
1787
1788/**
1789 * Process HELLO-message.
1790 *
1791 * @param plugin transport involved, may be NULL
1792 * @param message the actual message
1793 * @return GNUNET_OK if the HELLO was well-formed, GNUNET_SYSERR otherwise
1794 */
1795static int
1796process_hello (struct TransportPlugin *plugin,
1797 const struct GNUNET_MessageHeader *message)
1798{
1799 struct ValidationList *e;
1800 uint16_t hsize;
1801 struct GNUNET_PeerIdentity target;
1802 const struct GNUNET_HELLO_Message *hello;
1803 struct CheckHelloValidatedContext *chvc;
1804 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey;
1805
1806 hsize = ntohs (message->size);
1807 if ((ntohs (message->type) != GNUNET_MESSAGE_TYPE_HELLO) ||
1808 (hsize < sizeof (struct GNUNET_MessageHeader)))
1809 {
1810 GNUNET_break (0);
1811 return GNUNET_SYSERR;
1812 }
1813 /* first, check if load is too high */
1814 if (GNUNET_OS_load_cpu_get (cfg) > 100)
1815 {
1816 /* TODO: call to stats? */
1817 return GNUNET_OK;
1818 }
1819 hello = (const struct GNUNET_HELLO_Message *) message;
1820 if (GNUNET_OK != GNUNET_HELLO_get_key (hello, &publicKey))
1821 {
1822 GNUNET_break_op (0);
1823 return GNUNET_SYSERR;
1824 }
1825 GNUNET_CRYPTO_hash (&publicKey,
1826 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1827 &target.hashPubKey);
1828#if DEBUG_TRANSPORT
1829 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1830 "Processing `%s' message for `%4s'\n",
1831 "HELLO", GNUNET_i2s (&target));
1832#endif
1833 /* check if a HELLO for this peer is already on the validation list */
1834 e = pending_validations;
1835 while (e != NULL)
1836 {
1837 if (0 == memcmp (&e->publicKey,
1838 &publicKey,
1839 sizeof (struct
1840 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)))
1841 {
1842 /* TODO: call to stats? */
1843 return GNUNET_OK;
1844 }
1845 e = e->next;
1846 }
1847 chvc = GNUNET_malloc (sizeof (struct CheckHelloValidatedContext) + hsize);
1848 chvc->plugin = plugin;
1849 chvc->hello = (struct GNUNET_HELLO_Message *) &chvc[1];
1850 memcpy (chvc->hello, hello, hsize);
1851 /* finally, check if HELLO was previously validated
1852 (continuation will then schedule actual validation) */
1853 GNUNET_PEERINFO_for_all (cfg,
1854 sched,
1855 &target,
1856 0,
1857 HELLO_VERIFICATION_TIMEOUT,
1858 &check_hello_validated, chvc);
1859 return GNUNET_OK;
1860}
1861
1862
1863/**
1864 * Handle PING-message. If the plugin that gave us the message is
1865 * able to queue the PONG immediately, we only queue one PONG.
1866 * Otherwise we send at most TWO PONG messages, one via an unconfirmed
1867 * transport and one via a confirmed transport. Both addresses are
1868 * selected randomly among those available.
1869 *
1870 * @param plugin plugin that gave us the message
1871 * @param sender claimed sender of the PING
1872 * @param plugin_context context that might be used to send response
1873 * @param message the actual message
1874 */
1875static void
1876process_ping (struct TransportPlugin *plugin,
1877 const struct GNUNET_PeerIdentity *sender,
1878 void *plugin_context,
1879 const struct GNUNET_MessageHeader *message)
1880{
1881 const struct ValidationChallengeMessage *vcm;
1882 struct ValidationChallengeResponse vcr;
1883 uint16_t msize;
1884 struct NeighbourList *n;
1885
1886#if DEBUG_TRANSPORT
1887 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
1888 "Processing PING\n");
1889#endif
1890 msize = ntohs (message->size);
1891 if (msize < sizeof (struct ValidationChallengeMessage))
1892 {
1893 GNUNET_break_op (0);
1894 return;
1895 }
1896 vcm = (const struct ValidationChallengeMessage *) message;
1897 if (0 != memcmp (&vcm->target,
1898 &my_identity, sizeof (struct GNUNET_PeerIdentity)))
1899 {
1900 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1901 _("Received `%s' message not destined for me!\n"), "PING");
1902 /* TODO: call statistics */
1903 return;
1904 }
1905 if ((ntohl (vcm->purpose.size) !=
1906 msize - sizeof (struct GNUNET_MessageHeader))
1907 || (ntohl (vcm->purpose.purpose) !=
1908 GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO))
1909 {
1910 GNUNET_break_op (0);
1911 return;
1912 }
1913 msize -= sizeof (struct ValidationChallengeMessage);
1914 if (GNUNET_OK !=
1915 plugin->api->address_suggested (plugin->api->cls, &vcm[1], msize))
1916 {
1917 GNUNET_break_op (0);
1918 return;
1919 }
1920 vcr.header.size = htons (sizeof (struct ValidationChallengeResponse));
1921 vcr.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
1922 vcr.challenge = vcm->challenge;
1923 vcr.sender = my_identity;
1924 GNUNET_assert (GNUNET_OK ==
1925 GNUNET_CRYPTO_rsa_sign (my_private_key,
1926 &vcm->purpose, &vcr.signature));
1927#if EXTRA_CHECKS
1928 GNUNET_assert (GNUNET_OK ==
1929 GNUNET_CRYPTO_rsa_verify
1930 (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO, &vcm->purpose,
1931 &vcr.signature, &my_public_key));
1932#endif
1933#if DEBUG_TRANSPORT
1934 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
1935 "Trying to transmit PONG using inbound connection\n");
1936#endif
1937 n = find_neighbour (sender);
1938 transmit_to_peer (NULL, &vcr.header, GNUNET_YES, n);
1939}
1940
1941
1942/**
1943 * Handle PONG-message.
1944 *
1945 * @param message the actual message
1946 */
1947static void
1948process_pong (struct TransportPlugin *plugin,
1949 const struct GNUNET_MessageHeader *message)
1950{
1951 const struct ValidationChallengeResponse *vcr;
1952 struct ValidationList *pos;
1953 struct GNUNET_PeerIdentity peer;
1954 struct ValidationAddress *va;
1955 int all_done;
1956 int matched;
1957
1958#if DEBUG_TRANSPORT
1959 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
1960 "Processing PONG\n");
1961#endif
1962 vcr = (const struct ValidationChallengeResponse *) message;
1963 pos = pending_validations;
1964 while (pos != NULL)
1965 {
1966 GNUNET_CRYPTO_hash (&pos->publicKey,
1967 sizeof (struct
1968 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1969 &peer.hashPubKey);
1970 if (0 ==
1971 memcmp (&peer, &vcr->sender, sizeof (struct GNUNET_PeerIdentity)))
1972 break;
1973 pos = pos->next;
1974 }
1975 if (pos == NULL)
1976 {
1977 /* TODO: call statistics (unmatched PONG) */
1978 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1979 _
1980 ("Received `%s' message but have no record of a matching `%s' message. Ignoring.\n"),
1981 "PONG", "PING");
1982 return;
1983 }
1984 all_done = GNUNET_YES;
1985 matched = GNUNET_NO;
1986 va = pos->addresses;
1987 while (va != NULL)
1988 {
1989 if (va->msg->challenge == vcr->challenge)
1990 {
1991 if (GNUNET_OK !=
1992 GNUNET_CRYPTO_rsa_verify
1993 (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO, &va->msg->purpose,
1994 &vcr->signature, &pos->publicKey))
1995 {
1996 /* this could rarely happen if we used the same
1997 challenge number for the peer for two different
1998 transports / addresses, but the likelihood is
1999 very small... */
2000 GNUNET_break_op (0);
2001 }
2002 else
2003 {
2004#if DEBUG_TRANSPORT
2005 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2006 "Confirmed validity of peer address.\n");
2007#endif
2008 va->ok = GNUNET_YES;
2009 va->expiration =
2010 GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION);
2011 matched = GNUNET_YES;
2012 }
2013 }
2014 if (va->ok != GNUNET_YES)
2015 all_done = GNUNET_NO;
2016 va = va->next;
2017 }
2018 if (GNUNET_NO == matched)
2019 {
2020 /* TODO: call statistics (unmatched PONG) */
2021 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2022 _
2023 ("Received `%s' message but have no record of a matching `%s' message. Ignoring.\n"),
2024 "PONG", "PING");
2025 }
2026 if (GNUNET_YES == all_done)
2027 {
2028 pos->timeout.value = 0;
2029 GNUNET_SCHEDULER_add_delayed (sched,
2030 GNUNET_NO,
2031 GNUNET_SCHEDULER_PRIORITY_IDLE,
2032 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
2033 GNUNET_TIME_UNIT_ZERO,
2034 &cleanup_validation, NULL);
2035 }
2036}
2037
2038
2039/**
2040 * The peer specified by the given neighbour has timed-out. Update
2041 * our state and do the necessary notifications. Also notifies
2042 * our clients that the neighbour is now officially gone.
2043 *
2044 * @param n the neighbour list entry for the peer
2045 */
2046static void
2047disconnect_neighbour (struct NeighbourList *n)
2048{
2049 struct ReadyList *rpos;
2050 struct NeighbourList *npos;
2051 struct NeighbourList *nprev;
2052 struct MessageQueue *mq;
2053
2054#if DEBUG_TRANSPORT
2055 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
2056 "Disconnecting from neighbour\n");
2057#endif
2058 /* remove n from neighbours list */
2059 nprev = NULL;
2060 npos = neighbours;
2061 while ((npos != NULL) && (npos != n))
2062 {
2063 nprev = npos;
2064 npos = npos->next;
2065 }
2066 GNUNET_assert (npos != NULL);
2067 if (nprev == NULL)
2068 neighbours = n->next;
2069 else
2070 nprev->next = n->next;
2071
2072 /* notify all clients about disconnect */
2073 notify_clients_disconnect (&n->id);
2074
2075 /* clean up all plugins, cancel connections & pending transmissions */
2076 while (NULL != (rpos = n->plugins))
2077 {
2078 n->plugins = rpos->next;
2079 GNUNET_assert (rpos->neighbour == n);
2080 rpos->plugin->api->cancel (rpos->plugin->api->cls,
2081 rpos->plugin_handle, rpos, &n->id);
2082 GNUNET_free (rpos);
2083 }
2084
2085 /* free all messages on the queue */
2086 while (NULL != (mq = n->messages))
2087 {
2088 n->messages = mq->next;
2089 GNUNET_assert (mq->neighbour == n);
2090 GNUNET_free (mq);
2091 }
2092
2093 /* finally, free n itself */
2094 GNUNET_free (n);
2095}
2096
2097
2098/**
2099 * Add an entry for each of our transport plugins
2100 * (that are able to send) to the list of plugins
2101 * for this neighbour.
2102 *
2103 * @param neighbour to initialize
2104 */
2105static void
2106add_plugins (struct NeighbourList *neighbour)
2107{
2108 struct TransportPlugin *tp;
2109 struct ReadyList *rl;
2110
2111 neighbour->retry_plugins_time
2112 = GNUNET_TIME_relative_to_absolute (PLUGIN_RETRY_FREQUENCY);
2113 tp = plugins;
2114 while (tp != NULL)
2115 {
2116 if (tp->api->send != NULL)
2117 {
2118 rl = GNUNET_malloc (sizeof (struct ReadyList));
2119 rl->next = neighbour->plugins;
2120 neighbour->plugins = rl;
2121 rl->plugin = tp;
2122 rl->neighbour = neighbour;
2123 rl->transmit_ready = GNUNET_YES;
2124 }
2125 tp = tp->next;
2126 }
2127}
2128
2129
2130static void
2131neighbour_timeout_task (void *cls,
2132 const struct GNUNET_SCHEDULER_TaskContext *tc)
2133{
2134 struct NeighbourList *n = cls;
2135
2136#if DEBUG_TRANSPORT
2137 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
2138 "Neighbour has timed out!\n");
2139#endif
2140 n->timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
2141 disconnect_neighbour (n);
2142}
2143
2144
2145
2146/**
2147 * Create a fresh entry in our neighbour list for the given peer.
2148 * Will try to transmit our current HELLO to the new neighbour. Also
2149 * notifies our clients about the new "connection".
2150 *
2151 * @param peer the peer for which we create the entry
2152 * @return the new neighbour list entry
2153 */
2154static struct NeighbourList *
2155setup_new_neighbour (const struct GNUNET_PeerIdentity *peer)
2156{
2157 struct NeighbourList *n;
2158
2159#if DEBUG_TRANSPORT
2160 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
2161 "Setting up new neighbour `%4s', sending our HELLO to introduce ourselves\n",
2162 GNUNET_i2s (peer));
2163#endif
2164 GNUNET_assert (our_hello != NULL);
2165 n = GNUNET_malloc (sizeof (struct NeighbourList));
2166 n->next = neighbours;
2167 neighbours = n;
2168 n->id = *peer;
2169 n->last_quota_update = GNUNET_TIME_absolute_get ();
2170 n->peer_timeout =
2171 GNUNET_TIME_relative_to_absolute (IDLE_CONNECTION_TIMEOUT);
2172 n->quota_in = default_quota_in;
2173 add_plugins (n);
2174 n->hello_version_sent = our_hello_version;
2175 n->timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
2176 GNUNET_NO,
2177 GNUNET_SCHEDULER_PRIORITY_IDLE,
2178 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
2179 IDLE_CONNECTION_TIMEOUT,
2180 &neighbour_timeout_task, n);
2181 transmit_to_peer (NULL,
2182 (const struct GNUNET_MessageHeader *) our_hello,
2183 GNUNET_YES, n);
2184 notify_clients_connect (peer, GNUNET_TIME_UNIT_FOREVER_REL);
2185 return n;
2186}
2187
2188
2189/**
2190 * Function called by the plugin for each received message.
2191 * Update data volumes, possibly notify plugins about
2192 * reducing the rate at which they read from the socket
2193 * and generally forward to our receive callback.
2194 *
2195 * @param plugin_context value to pass to this plugin
2196 * to respond to the given peer (use is optional,
2197 * but may speed up processing)
2198 * @param service_context value passed to the transport-service
2199 * to identify the neighbour; will be NULL on the first
2200 * call for a given peer
2201 * @param latency estimated latency for communicating with the
2202 * given peer
2203 * @param peer (claimed) identity of the other peer
2204 * @param message the message, NULL if peer was disconnected
2205 * @return the new service_context that the plugin should use
2206 * for future receive calls for messages from this
2207 * particular peer
2208 */
2209static struct ReadyList *
2210plugin_env_receive (void *cls,
2211 void *plugin_context,
2212 struct ReadyList *service_context,
2213 struct GNUNET_TIME_Relative latency,
2214 const struct GNUNET_PeerIdentity *peer,
2215 const struct GNUNET_MessageHeader *message)
2216{
2217 const struct GNUNET_MessageHeader ack = {
2218 htons (sizeof (struct GNUNET_MessageHeader)),
2219 htons (GNUNET_MESSAGE_TYPE_TRANSPORT_ACK)
2220 };
2221 struct TransportPlugin *plugin = cls;
2222 struct TransportClient *cpos;
2223 struct InboundMessage *im;
2224 uint16_t msize;
2225 struct NeighbourList *n;
2226
2227 if (service_context != NULL)
2228 {
2229 n = service_context->neighbour;
2230 GNUNET_assert (n != NULL);
2231 }
2232 else
2233 {
2234 n = find_neighbour (peer);
2235 if (n == NULL)
2236 {
2237 if (message == NULL)
2238 return NULL; /* disconnect of peer already marked down */
2239 n = setup_new_neighbour (peer);
2240 }
2241 service_context = n->plugins;
2242 while ((service_context != NULL) && (plugin != service_context->plugin))
2243 service_context = service_context->next;
2244 GNUNET_assert ((plugin->api->send == NULL) ||
2245 (service_context != NULL));
2246 }
2247 if (message == NULL)
2248 {
2249 if ((service_context != NULL) &&
2250 (service_context->plugin_handle == plugin_context))
2251 {
2252 service_context->connected = GNUNET_NO;
2253 service_context->plugin_handle = NULL;
2254 }
2255 /* TODO: call stats */
2256 return NULL;
2257 }
2258#if DEBUG_TRANSPORT
2259 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
2260 "Processing message of type `%u' received by plugin...\n",
2261 ntohs (message->type));
2262#endif
2263 if (service_context != NULL)
2264 {
2265 if (service_context->connected == GNUNET_NO)
2266 {
2267 service_context->connected = GNUNET_YES;
2268 service_context->transmit_ready = GNUNET_YES;
2269 service_context->connect_attempts++;
2270 }
2271 service_context->timeout
2272 = GNUNET_TIME_relative_to_absolute (IDLE_CONNECTION_TIMEOUT);
2273 service_context->plugin_handle = plugin_context;
2274 service_context->latency = latency;
2275 }
2276 /* update traffic received amount ... */
2277 msize = ntohs (message->size);
2278 n->last_received += msize;
2279 GNUNET_SCHEDULER_cancel (sched, n->timeout_task);
2280 n->peer_timeout =
2281 GNUNET_TIME_relative_to_absolute (IDLE_CONNECTION_TIMEOUT);
2282 n->timeout_task =
2283 GNUNET_SCHEDULER_add_delayed (sched, GNUNET_NO,
2284 GNUNET_SCHEDULER_PRIORITY_IDLE,
2285 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
2286 IDLE_CONNECTION_TIMEOUT,
2287 &neighbour_timeout_task, n);
2288 update_quota (n);
2289 if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
2290 {
2291 /* dropping message due to frequent inbound volume violations! */
2292 GNUNET_log (GNUNET_ERROR_TYPE_WARNING |
2293 GNUNET_ERROR_TYPE_BULK,
2294 _
2295 ("Dropping incoming message due to repeated bandwidth quota violations.\n"));
2296 /* TODO: call stats */
2297 return service_context;
2298 }
2299 switch (ntohs (message->type))
2300 {
2301 case GNUNET_MESSAGE_TYPE_HELLO:
2302#if DEBUG_TRANSPORT
2303 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2304 "Receiving `%s' message from other peer.\n", "HELLO");
2305#endif
2306 process_hello (plugin, message);
2307#if DEBUG_TRANSPORT
2308 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2309 "Sending `%s' message to connecting peer.\n", "ACK");
2310#endif
2311 transmit_to_peer (NULL, &ack, GNUNET_YES, n);
2312 break;
2313 case GNUNET_MESSAGE_TYPE_TRANSPORT_PING:
2314 process_ping (plugin, peer, plugin_context, message);
2315 break;
2316 case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG:
2317 process_pong (plugin, message);
2318 break;
2319 case GNUNET_MESSAGE_TYPE_TRANSPORT_ACK:
2320 n->saw_ack = GNUNET_YES;
2321 /* intentional fall-through! */
2322 default:
2323#if DEBUG_TRANSPORT
2324 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2325 "Received message of type %u from other peer, sending to all clients.\n",
2326 ntohs (message->type));
2327#endif
2328 /* transmit message to all clients */
2329 im = GNUNET_malloc (sizeof (struct InboundMessage) + msize);
2330 im->header.size = htons (sizeof (struct InboundMessage) + msize);
2331 im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV);
2332 im->latency = GNUNET_TIME_relative_hton (latency);
2333 im->peer = *peer;
2334 memcpy (&im[1], message, msize);
2335
2336 cpos = clients;
2337 while (cpos != NULL)
2338 {
2339 transmit_to_client (cpos, &im->header, GNUNET_YES);
2340 cpos = cpos->next;
2341 }
2342 GNUNET_free (im);
2343 }
2344 return service_context;
2345}
2346
2347
2348/**
2349 * Handle START-message. This is the first message sent to us
2350 * by any client which causes us to add it to our list.
2351 *
2352 * @param cls closure (always NULL)
2353 * @param server the server handling the message
2354 * @param client identification of the client
2355 * @param message the actual message
2356 */
2357static void
2358handle_start (void *cls,
2359 struct GNUNET_SERVER_Handle *server,
2360 struct GNUNET_SERVER_Client *client,
2361 const struct GNUNET_MessageHeader *message)
2362{
2363 struct TransportClient *c;
2364 struct ConnectInfoMessage cim;
2365 struct NeighbourList *n;
2366 struct InboundMessage *im;
2367 struct GNUNET_MessageHeader *ack;
2368
2369#if DEBUG_TRANSPORT
2370 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2371 "Received `%s' request from client\n", "START");
2372#endif
2373 c = clients;
2374 while (c != NULL)
2375 {
2376 if (c->client == client)
2377 {
2378 /* client already on our list! */
2379 GNUNET_break (0);
2380 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
2381 return;
2382 }
2383 c = c->next;
2384 }
2385 c = GNUNET_malloc (sizeof (struct TransportClient));
2386 c->next = clients;
2387 clients = c;
2388 c->client = client;
2389 if (our_hello != NULL)
2390 {
2391#if DEBUG_TRANSPORT
2392 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2393 "Sending our own HELLO to new client\n");
2394#endif
2395 transmit_to_client (c,
2396 (const struct GNUNET_MessageHeader *) our_hello,
2397 GNUNET_NO);
2398 /* tell new client about all existing connections */
2399 cim.header.size = htons (sizeof (struct ConnectInfoMessage));
2400 cim.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
2401 cim.quota_out = htonl (default_quota_out);
2402 cim.latency = GNUNET_TIME_relative_hton (GNUNET_TIME_UNIT_ZERO); /* FIXME? */
2403 im = GNUNET_malloc (sizeof (struct InboundMessage) +
2404 sizeof (struct GNUNET_MessageHeader));
2405 im->header.size = htons (sizeof (struct InboundMessage) +
2406 sizeof (struct GNUNET_MessageHeader));
2407 im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV);
2408 im->latency = GNUNET_TIME_relative_hton (GNUNET_TIME_UNIT_ZERO); /* FIXME? */
2409 ack = (struct GNUNET_MessageHeader *) &im[1];
2410 ack->size = htons (sizeof (struct GNUNET_MessageHeader));
2411 ack->type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_ACK);
2412 for (n = neighbours; n != NULL; n = n->next)
2413 {
2414 cim.id = n->id;
2415 transmit_to_client (c, &cim.header, GNUNET_NO);
2416 if (n->saw_ack)
2417 {
2418 im->peer = n->id;
2419 transmit_to_client (c, &im->header, GNUNET_NO);
2420 }
2421 }
2422 GNUNET_free (im);
2423 }
2424 GNUNET_SERVER_receive_done (client, GNUNET_OK);
2425}
2426
2427
2428/**
2429 * Handle HELLO-message.
2430 *
2431 * @param cls closure (always NULL)
2432 * @param server the server handling the message
2433 * @param client identification of the client
2434 * @param message the actual message
2435 */
2436static void
2437handle_hello (void *cls,
2438 struct GNUNET_SERVER_Handle *server,
2439 struct GNUNET_SERVER_Client *client,
2440 const struct GNUNET_MessageHeader *message)
2441{
2442 int ret;
2443
2444#if DEBUG_TRANSPORT
2445 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2446 "Received `%s' request from client\n", "HELLO");
2447#endif
2448 ret = process_hello (NULL, message);
2449 GNUNET_SERVER_receive_done (client, ret);
2450}
2451
2452
2453/**
2454 * Handle SEND-message.
2455 *
2456 * @param cls closure (always NULL)
2457 * @param server the server handling the message
2458 * @param client identification of the client
2459 * @param message the actual message
2460 */
2461static void
2462handle_send (void *cls,
2463 struct GNUNET_SERVER_Handle *server,
2464 struct GNUNET_SERVER_Client *client,
2465 const struct GNUNET_MessageHeader *message)
2466{
2467 struct TransportClient *tc;
2468 struct NeighbourList *n;
2469 const struct OutboundMessage *obm;
2470 const struct GNUNET_MessageHeader *obmm;
2471 uint16_t size;
2472 uint16_t msize;
2473
2474 size = ntohs (message->size);
2475 if (size <
2476 sizeof (struct OutboundMessage) + sizeof (struct GNUNET_MessageHeader))
2477 {
2478 GNUNET_break (0);
2479 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
2480 return;
2481 }
2482 obm = (const struct OutboundMessage *) message;
2483#if DEBUG_TRANSPORT
2484 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2485 "Received `%s' request from client with target `%4s'\n",
2486 "SEND", GNUNET_i2s (&obm->peer));
2487#endif
2488 obmm = (const struct GNUNET_MessageHeader *) &obm[1];
2489 msize = ntohs (obmm->size);
2490 if (size != msize + sizeof (struct OutboundMessage))
2491 {
2492 GNUNET_break (0);
2493 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
2494 return;
2495 }
2496 n = find_neighbour (&obm->peer);
2497 if (n == NULL)
2498 n = setup_new_neighbour (&obm->peer);
2499 tc = clients;
2500 while ((tc != NULL) && (tc->client != client))
2501 tc = tc->next;
2502
2503#if DEBUG_TRANSPORT
2504 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2505 "Client asked to transmit %u-byte message of type %u to `%4s'\n",
2506 ntohs (obmm->size),
2507 ntohs (obmm->type), GNUNET_i2s (&obm->peer));
2508#endif
2509 transmit_to_peer (tc, obmm, GNUNET_NO, n);
2510 GNUNET_SERVER_receive_done (client, GNUNET_OK);
2511}
2512
2513
2514/**
2515 * Handle SET_QUOTA-message.
2516 *
2517 * @param cls closure (always NULL)
2518 * @param server the server handling the message
2519 * @param client identification of the client
2520 * @param message the actual message
2521 */
2522static void
2523handle_set_quota (void *cls,
2524 struct GNUNET_SERVER_Handle *server,
2525 struct GNUNET_SERVER_Client *client,
2526 const struct GNUNET_MessageHeader *message)
2527{
2528 const struct QuotaSetMessage *qsm =
2529 (const struct QuotaSetMessage *) message;
2530 struct NeighbourList *n;
2531 struct TransportPlugin *p;
2532 struct ReadyList *rl;
2533
2534#if DEBUG_TRANSPORT
2535 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2536 "Received `%s' request from client for peer `%4s'\n",
2537 "SET_QUOTA", GNUNET_i2s (&qsm->peer));
2538#endif
2539 n = find_neighbour (&qsm->peer);
2540 if (n == NULL)
2541 {
2542 GNUNET_SERVER_receive_done (client, GNUNET_OK);
2543 return;
2544 }
2545 update_quota (n);
2546 if (n->quota_in < ntohl (qsm->quota_in))
2547 n->last_quota_update = GNUNET_TIME_absolute_get ();
2548 n->quota_in = ntohl (qsm->quota_in);
2549 rl = n->plugins;
2550 while (rl != NULL)
2551 {
2552 p = rl->plugin;
2553 p->api->set_receive_quota (p->api->cls,
2554 &qsm->peer, ntohl (qsm->quota_in));
2555 rl = rl->next;
2556 }
2557 GNUNET_SERVER_receive_done (client, GNUNET_OK);
2558}
2559
2560
2561/**
2562 * Handle TRY_CONNECT-message.
2563 *
2564 * @param cls closure (always NULL)
2565 * @param server the server handling the message
2566 * @param client identification of the client
2567 * @param message the actual message
2568 */
2569static void
2570handle_try_connect (void *cls,
2571 struct GNUNET_SERVER_Handle *server,
2572 struct GNUNET_SERVER_Client *client,
2573 const struct GNUNET_MessageHeader *message)
2574{
2575 const struct TryConnectMessage *tcm;
2576
2577 tcm = (const struct TryConnectMessage *) message;
2578#if DEBUG_TRANSPORT
2579 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2580 "Received `%s' request from client asking to connect to `%4s'\n",
2581 "TRY_CONNECT", GNUNET_i2s (&tcm->peer));
2582#endif
2583 if (NULL == find_neighbour (&tcm->peer))
2584 setup_new_neighbour (&tcm->peer);
2585 GNUNET_SERVER_receive_done (client, GNUNET_OK);
2586}
2587
2588
2589/**
2590 * List of handlers for the messages understood by this
2591 * service.
2592 */
2593static struct GNUNET_SERVER_MessageHandler handlers[] = {
2594 {&handle_start, NULL,
2595 GNUNET_MESSAGE_TYPE_TRANSPORT_START, 0},
2596 {&handle_hello, NULL,
2597 GNUNET_MESSAGE_TYPE_HELLO, 0},
2598 {&handle_send, NULL,
2599 GNUNET_MESSAGE_TYPE_TRANSPORT_SEND, 0},
2600 {&handle_set_quota, NULL,
2601 GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA, sizeof (struct QuotaSetMessage)},
2602 {&handle_try_connect, NULL,
2603 GNUNET_MESSAGE_TYPE_TRANSPORT_TRY_CONNECT,
2604 sizeof (struct TryConnectMessage)},
2605 {NULL, NULL, 0, 0}
2606};
2607
2608
2609/**
2610 * Setup the environment for this plugin.
2611 */
2612static void
2613create_environment (struct TransportPlugin *plug)
2614{
2615 plug->env.cfg = cfg;
2616 plug->env.sched = sched;
2617 plug->env.my_public_key = &my_public_key;
2618 plug->env.cls = plug;
2619 plug->env.receive = &plugin_env_receive;
2620 plug->env.lookup = &plugin_env_lookup_address;
2621 plug->env.notify_address = &plugin_env_notify_address;
2622 plug->env.default_quota_in = default_quota_in;
2623 plug->env.max_connections = max_connect_per_transport;
2624}
2625
2626
2627/**
2628 * Start the specified transport (load the plugin).
2629 */
2630static void
2631start_transport (struct GNUNET_SERVER_Handle *server, const char *name)
2632{
2633 struct TransportPlugin *plug;
2634 char *libname;
2635
2636 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2637 _("Loading `%s' transport plugin\n"), name);
2638 GNUNET_asprintf (&libname, "libgnunet_plugin_transport_%s", name);
2639 plug = GNUNET_malloc (sizeof (struct TransportPlugin));
2640 create_environment (plug);
2641 plug->short_name = GNUNET_strdup (name);
2642 plug->lib_name = libname;
2643 plug->next = plugins;
2644 plugins = plug;
2645 plug->api = GNUNET_PLUGIN_load (libname, &plug->env);
2646 if (plug->api == NULL)
2647 {
2648 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2649 _("Failed to load transport plugin for `%s'\n"), name);
2650 GNUNET_free (plug->short_name);
2651 plugins = plug->next;
2652 GNUNET_free (libname);
2653 GNUNET_free (plug);
2654 }
2655}
2656
2657
2658/**
2659 * Called whenever a client is disconnected. Frees our
2660 * resources associated with that client.
2661 *
2662 * @param cls closure
2663 * @param client identification of the client
2664 */
2665static void
2666client_disconnect_notification (void *cls,
2667 struct GNUNET_SERVER_Client *client)
2668{
2669 struct TransportClient *pos;
2670 struct TransportClient *prev;
2671 struct ClientMessageQueueEntry *mqe;
2672
2673#if DEBUG_TRANSPORT
2674 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
2675 "Client disconnected, cleaning up.\n");
2676#endif
2677 prev = NULL;
2678 pos = clients;
2679 while ((pos != NULL) && (pos->client != client))
2680 {
2681 prev = pos;
2682 pos = pos->next;
2683 }
2684 if (pos == NULL)
2685 return;
2686 while (NULL != (mqe = pos->message_queue_head))
2687 {
2688 pos->message_queue_head = mqe->next;
2689 GNUNET_free (mqe);
2690 }
2691 pos->message_queue_head = NULL;
2692 if (prev == NULL)
2693 clients = pos->next;
2694 else
2695 prev->next = pos->next;
2696 if (GNUNET_YES == pos->tcs_pending)
2697 {
2698 pos->client = NULL;
2699 return;
2700 }
2701 GNUNET_free (pos);
2702}
2703
2704
2705/**
2706 * Initiate transport service.
2707 *
2708 * @param cls closure
2709 * @param s scheduler to use
2710 * @param serv the initialized server
2711 * @param c configuration to use
2712 */
2713static void
2714run (void *cls,
2715 struct GNUNET_SCHEDULER_Handle *s,
2716 struct GNUNET_SERVER_Handle *serv, struct GNUNET_CONFIGURATION_Handle *c)
2717{
2718 char *plugs;
2719 char *pos;
2720 int no_transports;
2721 unsigned long long qin;
2722 unsigned long long qout;
2723 unsigned long long tneigh;
2724 char *keyfile;
2725
2726 sched = s;
2727 cfg = c;
2728 /* parse configuration */
2729 if ((GNUNET_OK !=
2730 GNUNET_CONFIGURATION_get_value_number (c,
2731 "TRANSPORT",
2732 "DEFAULT_QUOTA_IN",
2733 &qin)) ||
2734 (GNUNET_OK !=
2735 GNUNET_CONFIGURATION_get_value_number (c,
2736 "TRANSPORT",
2737 "DEFAULT_QUOTA_OUT",
2738 &qout)) ||
2739 (GNUNET_OK !=
2740 GNUNET_CONFIGURATION_get_value_number (c,
2741 "TRANSPORT",
2742 "NEIGHBOUR_LIMIT",
2743 &tneigh)) ||
2744 (GNUNET_OK !=
2745 GNUNET_CONFIGURATION_get_value_filename (c,
2746 "GNUNETD",
2747 "HOSTKEY", &keyfile)))
2748 {
2749 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2750 _
2751 ("Transport service is lacking key configuration settings. Exiting.\n"));
2752 GNUNET_SCHEDULER_shutdown (s);
2753 return;
2754 }
2755 max_connect_per_transport = (uint32_t) tneigh;
2756 default_quota_in = (uint32_t) qin;
2757 default_quota_out = (uint32_t) qout;
2758 my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
2759 GNUNET_free (keyfile);
2760 if (my_private_key == NULL)
2761 {
2762 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2763 _
2764 ("Transport service could not access hostkey. Exiting.\n"));
2765 GNUNET_SCHEDULER_shutdown (s);
2766 return;
2767 }
2768 GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key);
2769 GNUNET_CRYPTO_hash (&my_public_key,
2770 sizeof (my_public_key), &my_identity.hashPubKey);
2771 /* setup notification */
2772 server = serv;
2773 GNUNET_SERVER_disconnect_notify (server,
2774 &client_disconnect_notification, NULL);
2775 /* load plugins... */
2776 no_transports = 1;
2777 if (GNUNET_OK ==
2778 GNUNET_CONFIGURATION_get_value_string (c,
2779 "TRANSPORT", "PLUGINS", &plugs))
2780 {
2781 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2782 _("Starting transport plugins `%s'\n"), plugs);
2783 pos = strtok (plugs, " ");
2784 while (pos != NULL)
2785 {
2786 start_transport (server, pos);
2787 no_transports = 0;
2788 pos = strtok (NULL, " ");
2789 }
2790 GNUNET_free (plugs);
2791 }
2792 if (no_transports)
2793 refresh_hello ();
2794 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transport service ready.\n"));
2795 /* process client requests */
2796 GNUNET_SERVER_add_handlers (server, handlers);
2797}
2798
2799
2800/**
2801 * Function called when the service shuts
2802 * down. Unloads our plugins.
2803 *
2804 * @param cls closure
2805 * @param cfg configuration to use
2806 */
2807static void
2808unload_plugins (void *cls, struct GNUNET_CONFIGURATION_Handle *cfg)
2809{
2810 struct TransportPlugin *plug;
2811 struct AddressList *al;
2812
2813#if DEBUG_TRANSPORT
2814 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2815 "Transport service is unloading plugins...\n");
2816#endif
2817 while (NULL != (plug = plugins))
2818 {
2819 plugins = plug->next;
2820 GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api));
2821 GNUNET_free (plug->lib_name);
2822 GNUNET_free (plug->short_name);
2823 while (NULL != (al = plug->addresses))
2824 {
2825 plug->addresses = al->next;
2826 GNUNET_free (al);
2827 }
2828 GNUNET_free (plug);
2829 }
2830 if (my_private_key != NULL)
2831 GNUNET_CRYPTO_rsa_key_free (my_private_key);
2832}
2833
2834
2835/**
2836 * The main function for the transport service.
2837 *
2838 * @param argc number of arguments from the command line
2839 * @param argv command line arguments
2840 * @return 0 ok, 1 on error
2841 */
2842int
2843main (int argc, char *const *argv)
2844{
2845 return (GNUNET_OK ==
2846 GNUNET_SERVICE_run (argc,
2847 argv,
2848 "transport",
2849 &run, NULL, &unload_plugins, NULL)) ? 0 : 1;
2850}
2851
2852/* end of gnunet-service-transport.c */
diff --git a/src/transport/gnunet-transport.c b/src/transport/gnunet-transport.c
new file mode 100644
index 000000000..52475e3eb
--- /dev/null
+++ b/src/transport/gnunet-transport.c
@@ -0,0 +1,42 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file server/gnunet-transport.c
23 * @brief Tool to help configure the transports.
24 * @author Christian Grothoff
25 *
26 * This utility can be used to test if a transport mechanism for
27 * GNUnet is properly configured.
28 */
29
30#include "platform.h"
31#include "gnunet_program_lib.h"
32#include "gnunet_protocols.h"
33#include "gnunet_transport_service.h"
34
35int
36main (int argc, char *const *argv)
37{
38 return 0;
39}
40
41
42/* end of gnunet-transport.c */
diff --git a/src/transport/plugin_transport.h b/src/transport/plugin_transport.h
new file mode 100644
index 000000000..57df9affe
--- /dev/null
+++ b/src/transport/plugin_transport.h
@@ -0,0 +1,468 @@
1/*
2 This file is part of GNUnet
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file transport/plugin_transport.h
23 * @brief API for the transport services. This header
24 * specifies the struct that is given to the plugin's entry
25 * method and the other struct that must be returned.
26 * Note that the destructors of transport plugins will
27 * be given the value returned by the constructor
28 * and is expected to return a NULL pointer.
29 *
30 * TODO:
31 * - consider moving DATA message (latency measurement)
32 * to service; avoids encapsulation overheads and
33 * would enable latency measurements for non-bidi
34 * transports.
35 * -
36 *
37 * @author Christian Grothoff
38 */
39#ifndef PLUGIN_TRANSPORT_H
40#define PLUGIN_TRANSPORT_H
41
42#include "gnunet_configuration_lib.h"
43#include "gnunet_scheduler_lib.h"
44#include "gnunet_transport_service.h"
45
46/**
47 * Opaque internal context for a particular peer of the transport
48 * service. Plugins will be given a pointer to this type and, if
49 * cheaply possible, should pass this pointer back to the transport
50 * service whenever additional messages from the same peer are
51 * received.
52 */
53struct ReadyList;
54
55/**
56 * Function called by the transport for each received message.
57 * This function should also be called with "NULL" for the
58 * message to signal that the other peer disconnected.
59 *
60 * @param cls closure
61 * @param plugin_context value to pass to this plugin
62 * to respond to the given peer (use is optional,
63 * but may speed up processing)
64 * @param service_context value passed to the transport-service
65 * to identify the neighbour; will be NULL on the first
66 * call for a given peer
67 * @param latency estimated latency for communicating with the
68 * given peer; should be set to GNUNET_TIME_UNIT_FOREVER_REL
69 * until the transport has seen messages transmitted in
70 * BOTH directions (and hence been able to do an actual
71 * round-trip observation); a non-FOREVER latency is also used
72 * by the transport to know that communication in both directions
73 * using this one plugin actually works
74 * @param peer (claimed) identity of the other peer
75 * @param message the message, NULL if peer was disconnected
76 * @return the new service_context that the plugin should use
77 * for future receive calls for messages from this
78 * particular peer
79 */
80typedef struct ReadyList *
81 (*GNUNET_TRANSPORT_PluginReceiveCallback) (void *cls,
82 void *plugin_context,
83 struct ReadyList *
84 service_context,
85 struct GNUNET_TIME_Relative
86 latency,
87 const struct GNUNET_PeerIdentity
88 * peer,
89 const struct GNUNET_MessageHeader
90 * message);
91
92
93/**
94 * Function that will be called for each address the transport
95 * is aware that it might be reachable under.
96 *
97 * @param cls closure
98 * @param name name of the transport that generated the address
99 * @param addr one of the addresses of the host, NULL for the last address
100 * the specific address format depends on the transport
101 * @param addrlen length of the address
102 * @param expires when should this address automatically expire?
103 */
104typedef void (*GNUNET_TRANSPORT_AddressNotification) (void *cls,
105 const char *name,
106 const void *addr,
107 size_t addrlen,
108 struct
109 GNUNET_TIME_Relative
110 expires);
111
112
113/**
114 * Function that will be called for each address obtained from the HELLO.
115 *
116 * @param cls closure
117 * @param name name of the transport that generated the address
118 * @param addr one of the addresses of the host, NULL for the last address
119 * the specific address format depends on the transport
120 * @param addrlen length of the address
121 */
122typedef void (*GNUNET_TRANSPORT_AddressCallback) (void *cls,
123 const char *name,
124 const void *addr,
125 size_t addrlen);
126
127
128/**
129 * Function that allows a transport to query the known
130 * network addresses for a given peer.
131 *
132 * @param cls closure
133 * @param timeout after how long should we time out?
134 * @param target which peer are we looking for?
135 * @param iter function to call for each known address
136 * @param iter_cls closure for iter
137 */
138typedef void (*GNUNET_TRANSPORT_LookupAddress) (void *cls,
139 struct GNUNET_TIME_Relative
140 timeout,
141 const struct
142 GNUNET_PeerIdentity * target,
143 GNUNET_TRANSPORT_AddressCallback
144 iter, void *iter_cls);
145
146
147/**
148 * The transport service will pass a pointer to a struct
149 * of this type as the first and only argument to the
150 * entry point of each transport plugin.
151 */
152struct GNUNET_TRANSPORT_PluginEnvironment
153{
154 /**
155 * Configuration to use.
156 */
157 struct GNUNET_CONFIGURATION_Handle *cfg;
158
159 /**
160 * Scheduler to use.
161 */
162 struct GNUNET_SCHEDULER_Handle *sched;
163
164 /**
165 * Our public key.
166 */
167 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *my_public_key;
168
169 /**
170 * Closure for the various callbacks.
171 */
172 void *cls;
173
174 /**
175 * Function that should be called by the transport plugin
176 * whenever a message is received.
177 */
178 GNUNET_TRANSPORT_PluginReceiveCallback receive;
179
180 /**
181 * Address lookup function.
182 */
183 GNUNET_TRANSPORT_LookupAddress lookup;
184
185 /**
186 * Function that must be called by each plugin to notify the
187 * transport service about the addresses under which the transport
188 * provided by the plugin can be reached.
189 */
190 GNUNET_TRANSPORT_AddressNotification notify_address;
191
192 /**
193 * What is the default quota (in terms of incoming bytes per
194 * ms) for new connections?
195 */
196 uint32_t default_quota_in;
197
198 /**
199 * What is the maximum number of connections that this transport
200 * should allow? Transports that do not have sessions (such as
201 * UDP) can ignore this value.
202 */
203 uint32_t max_connections;
204
205};
206
207
208/**
209 * Function that can be used by the transport service to transmit
210 * a message using the plugin using a fresh connection (even if
211 * we already have a connection to this peer, this function is
212 * required to establish a new one).
213 *
214 * @param cls closure
215 * @param target who should receive this message
216 * @param msg1 first message to transmit
217 * @param msg2 second message to transmit (can be NULL)
218 * @param timeout how long should we try to transmit these?
219 * @param addrlen length of the address
220 * @param addr the address
221 * @return session instance if the transmission has been scheduled
222 * NULL if the address format is invalid
223 */
224typedef void *
225 (*GNUNET_TRANSPORT_TransmitToAddressFunction) (void *cls,
226 const struct
227 GNUNET_PeerIdentity * target,
228 const struct
229 GNUNET_MessageHeader * msg1,
230 const struct
231 GNUNET_MessageHeader * msg2,
232 struct GNUNET_TIME_Relative
233 timeout, const void *addr,
234 size_t addrlen);
235
236/**
237 * Function called by the GNUNET_TRANSPORT_TransmitFunction
238 * upon "completion".
239 *
240 * @param cls closure
241 * @param service_context value passed to the transport-service
242 * to identify the neighbour
243 * @param target who was the recipient of the message?
244 * @param result GNUNET_OK on success
245 * GNUNET_SYSERR if the target disconnected;
246 * disconnect will ALSO be signalled using
247 * the ReceiveCallback.
248 */
249typedef void
250 (*GNUNET_TRANSPORT_TransmitContinuation) (void *cls,
251 struct ReadyList *
252 service_context,
253 const struct GNUNET_PeerIdentity *
254 target, int result);
255
256/**
257 * Function that can be used by the transport service to transmit
258 * a message using the plugin. Note that in the case of a
259 * peer disconnecting, the continuation MUST be called
260 * prior to the disconnect notification itself. This function
261 * will be called with this peer's HELLO message to initiate
262 * a fresh connection to another peer.
263 *
264 * @param cls closure
265 * @param plugin_context value we were asked to pass to this plugin
266 * to respond to the given peer (use is optional,
267 * but may speed up processing), can be NULL
268 * @param service_context value passed to the transport-service
269 * to identify the neighbour; NULL is used to indicate
270 * an urgent message. If the urgent message can not be
271 * scheduled for immediate transmission, the plugin is to
272 * call the continuation with failure immediately
273 * @param target who should receive this message
274 * @param msg the message to transmit
275 * @param timeout how long to wait at most for the transmission
276 * @param cont continuation to call once the message has
277 * been transmitted (or if the transport is ready
278 * for the next transmission call; or if the
279 * peer disconnected...); can be NULL
280 * @param cont_cls closure for cont
281 * @return plugin_context that should be used next time for
282 * sending messages to the specified peer
283 */
284typedef void *
285 (*GNUNET_TRANSPORT_TransmitFunction) (void *cls,
286 void *plugin_context,
287 struct ReadyList * service_context,
288 const struct GNUNET_PeerIdentity *
289 target,
290 const struct GNUNET_MessageHeader *
291 msg,
292 struct GNUNET_TIME_Relative timeout,
293 GNUNET_TRANSPORT_TransmitContinuation
294 cont, void *cont_cls);
295
296
297/**
298 * Function that can be called to force a disconnect from the
299 * specified neighbour. This should also cancel all previously
300 * scheduled transmissions. Obviously the transmission may have been
301 * partially completed already, which is OK. The plugin is supposed
302 * to close the connection (if applicable) and no longer call the
303 * transmit continuation(s).
304 *
305 * Finally, plugin MUST NOT call the services's receive function to
306 * notify the service that the connection to the specified target was
307 * closed after a getting this call.
308 *
309 * @param cls closure
310 * @param plugin_context value we were asked to pass to this plugin
311 * to respond to the given peer (use is optional,
312 * but may speed up processing), can be NULL (if
313 * NULL was returned from the transmit function)
314 * @param service_context must correspond to the service context
315 * of the corresponding Transmit call; the plugin should
316 * not cancel a send call made with a different service
317 * context pointer! Never NULL.
318 * @param target peer for which the last transmission is
319 * to be cancelled
320 */
321typedef void
322 (*GNUNET_TRANSPORT_CancelFunction) (void *cls,
323 void *plugin_context,
324 struct ReadyList * service_context,
325 const struct GNUNET_PeerIdentity *
326 target);
327
328
329/**
330 * Function called by the pretty printer for the resolved address for
331 * each human-readable address obtained.
332 *
333 * @param cls closure
334 * @param hostname one of the names for the host, NULL
335 * on the last call to the callback
336 */
337typedef void (*GNUNET_TRANSPORT_AddressStringCallback) (void *cls,
338 const char *address);
339
340
341/**
342 * Convert the transports address to a nice, human-readable
343 * format.
344 *
345 * @param cls closure
346 * @param name name of the transport that generated the address
347 * @param addr one of the addresses of the host, NULL for the last address
348 * the specific address format depends on the transport
349 * @param addrlen length of the address
350 * @param numeric should (IP) addresses be displayed in numeric form?
351 * @param timeout after how long should we give up?
352 * @param asc function to call on each string
353 * @param asc_cls closure for asc
354 */
355typedef void
356 (*GNUNET_TRANSPORT_AddressPrettyPrinter) (void *cls,
357 const char *type,
358 const void *addr,
359 size_t addrlen,
360 int numeric,
361 struct GNUNET_TIME_Relative
362 timeout,
363 GNUNET_TRANSPORT_AddressStringCallback
364 asc, void *asc_cls);
365
366
367/**
368 * Set a quota for receiving data from the given peer; this is a
369 * per-transport limit. The transport should limit its read/select
370 * calls to stay below the quota (in terms of incoming data).
371 *
372 * @param cls closure
373 * @param peer the peer for whom the quota is given
374 * @param quota_in quota for receiving/sending data in bytes per ms
375 */
376typedef void
377 (*GNUNET_TRANSPORT_SetQuota) (void *cls,
378 const struct GNUNET_PeerIdentity * target,
379 uint32_t quota_in);
380
381
382/**
383 * Another peer has suggested an address for this
384 * peer and transport plugin. Check that this could be a valid
385 * address. If so, consider adding it to the list
386 * of addresses.
387 *
388 * @param addr pointer to the address
389 * @param addrlen length of addr
390 * @return GNUNET_OK if this is a plausible address for this peer
391 * and transport
392 */
393typedef int
394 (*GNUNET_TRANSPORT_SuggestAddress) (void *cls,
395 const void *addr, size_t addrlen);
396
397/**
398 * Each plugin is required to return a pointer to a struct of this
399 * type as the return value from its entry point.
400 */
401struct GNUNET_TRANSPORT_PluginFunctions
402{
403
404 /**
405 * Closure for all of the callbacks.
406 */
407 void *cls;
408
409 /**
410 * Function used to send a single message to a particular
411 * peer using the specified address. Used to validate
412 * HELLOs.
413 */
414 GNUNET_TRANSPORT_TransmitToAddressFunction send_to;
415
416 /**
417 * Function that the transport service will use to transmit data to
418 * another peer. May be null for plugins that only support
419 * receiving data. After this call, the plugin call the specified
420 * continuation with success or error before notifying us about the
421 * target having disconnected.
422 */
423 GNUNET_TRANSPORT_TransmitFunction send;
424
425 /**
426 * Function that can be used to force the plugin to disconnect
427 * from the given peer and cancel all previous transmissions
428 * (and their continuationc).
429 */
430 GNUNET_TRANSPORT_CancelFunction cancel;
431
432 /**
433 * Function to pretty-print addresses. NOTE: this function is not
434 * yet used by transport-service, but will be used in the future
435 * once the transport-API has been completed.
436 */
437 GNUNET_TRANSPORT_AddressPrettyPrinter address_pretty_printer;
438
439 /**
440 * Function that the transport service can use to try to enforce a
441 * quota for the number of bytes received via this transport.
442 * Transports that can not refuse incoming data (such as UDP)
443 * are free to ignore these calls.
444 */
445 GNUNET_TRANSPORT_SetQuota set_receive_quota;
446
447 /**
448 * Function that will be called if another peer suggested that
449 * we should use a particular address (since he is reaching
450 * us at that address) for this transport.
451 */
452 GNUNET_TRANSPORT_SuggestAddress address_suggested;
453
454 /**
455 * Relative cost of this transport compared to others. This
456 * is supposed to be a static cost estimate which determines
457 * which plugins should not even be attempted if other,
458 * cheaper transports are already working. The idea is that
459 * the costs have roughly this relationship:
460 * <pre>
461 * TCP < UDP < HTTP == HTTPS < SMTP
462 * </pre>
463 */
464 unsigned int cost_estimate;
465};
466
467
468#endif
diff --git a/src/transport/plugin_transport_http.c b/src/transport/plugin_transport_http.c
new file mode 100644
index 000000000..eb69f4386
--- /dev/null
+++ b/src/transport/plugin_transport_http.c
@@ -0,0 +1,2085 @@
1/*
2 This file is part of GNUnet
3 (C) 2003, 2004, 2005, 2006, 2007, 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file transports/http.c
23 * @brief Implementation of the HTTP transport service
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util.h"
29#include "gnunet_protocols.h"
30#include "gnunet_transport.h"
31#include "gnunet_stats_service.h"
32#include "gnunet_upnp_service.h"
33#include <stdint.h>
34#include <microhttpd.h>
35#include <curl/curl.h>
36#include "ip.h"
37
38#define DEBUG_HTTP GNUNET_NO
39
40/**
41 * Disable GET (for debugging only!). Must be GNUNET_YES
42 * in production use!
43 */
44#define DO_GET GNUNET_YES
45
46/**
47 * After how much time of the core not being associated with a http
48 * connection anymore do we close it?
49 *
50 * Needs to be larger than SECONDS_INACTIVE_DROP in
51 * core's connection.s
52 */
53#define HTTP_TIMEOUT (600 * GNUNET_CRON_SECONDS)
54
55/**
56 * How often do we re-issue GET requests?
57 */
58#define HTTP_GET_REFRESH (5 * GNUNET_CRON_SECONDS)
59
60/**
61 * Default maximum size of the HTTP read and write buffer.
62 */
63#define HTTP_BUF_SIZE (64 * 1024)
64
65/**
66 * Text of the response sent back after the last bytes of a PUT
67 * request have been received (just to formally obey the HTTP
68 * protocol).
69 */
70#define HTTP_PUT_RESPONSE "Thank you!"
71
72#define MY_TRANSPORT_NAME "HTTP"
73#include "common.c"
74
75/**
76 * Client-side data per PUT request.
77 */
78struct HTTPPutData
79{
80 /**
81 * This is a linked list.
82 */
83 struct HTTPPutData *next;
84
85 /**
86 * Handle to our CURL request.
87 */
88 CURL *curl_put;
89
90 /**
91 * Last time we made progress with the PUT.
92 */
93 GNUNET_CronTime last_activity;
94
95 /**
96 * The message we are sending.
97 */
98 char *msg;
99
100 /**
101 * Size of msg.
102 */
103 unsigned int size;
104
105 /**
106 * Current position in msg.
107 */
108 unsigned int pos;
109
110 /**
111 * Are we done sending? Set to 1 after we
112 * completed sending and started to receive
113 * a response ("Thank you!") or once the
114 * timeout has been reached.
115 */
116 int done;
117
118};
119
120/**
121 * Server-side data per PUT request.
122 */
123struct MHDPutData
124{
125 /**
126 * This is a linked list.
127 */
128 struct MHDPutData *next;
129
130 /**
131 * MHD connection handle for this request.
132 */
133 struct MHD_Connection *session;
134
135 /**
136 * Last time we received data on this PUT
137 * connection.
138 */
139 GNUNET_CronTime last_activity;
140
141 /**
142 * Read buffer for the header (from PUT)
143 */
144 char rbuff1[sizeof (GNUNET_MessageHeader)];
145
146 /**
147 * The read buffer (used only receiving PUT data).
148 */
149 char *rbuff2;
150
151 /**
152 * Number of valid bytes in rbuff1
153 */
154 unsigned int rpos1;
155
156 /**
157 * Number of valid bytes in rbuff2
158 */
159 unsigned int rpos2;
160
161
162 /**
163 * Size of the rbuff2 buffer.
164 */
165 unsigned int rsize2;
166
167 /**
168 * Should we sent a response for this PUT yet?
169 */
170 int ready;
171
172 /**
173 * Have we sent a response for this PUT yet?
174 */
175 int done;
176
177};
178
179/**
180 * Server-side data for a GET request.
181 */
182struct MHDGetData
183{
184
185 /**
186 * This is a linked list.
187 */
188 struct MHDGetData *next;
189
190 /**
191 * MHD connection handle for this request.
192 */
193 struct MHD_Connection *session;
194
195 /**
196 * GET session response handle
197 */
198 struct MHD_Response *get;
199
200 /**
201 * My HTTP session.
202 */
203 struct HTTPSession *httpsession;
204
205 /**
206 * The write buffer (for sending GET response)
207 */
208 char *wbuff;
209
210 /**
211 * What was the last time we were able to
212 * transmit data using the current get handle?
213 */
214 GNUNET_CronTime last_get_activity;
215
216 /**
217 * Current write position in wbuff
218 */
219 unsigned int woff;
220
221 /**
222 * Number of valid bytes in wbuff (starting at woff)
223 */
224 unsigned int wpos;
225
226 /**
227 * Size of the write buffer.
228 */
229 unsigned int wsize;
230
231};
232
233/**
234 * Transport Session handle.
235 */
236typedef struct HTTPSession
237{
238
239 /**
240 * GNUNET_TSession for this session.
241 */
242 GNUNET_TSession *tsession;
243
244 /**
245 * To whom are we talking to.
246 */
247 GNUNET_PeerIdentity sender;
248
249 /**
250 * number of users of this session
251 */
252 unsigned int users;
253
254 /**
255 * Has this session been destroyed?
256 */
257 int destroyed;
258
259 /**
260 * Are we client or server? Determines which of the
261 * structs in the union below is being used for this
262 * connection!
263 */
264 int is_client;
265
266 /**
267 * Is MHD still using this session handle?
268 */
269 int is_mhd_active;
270
271 /**
272 * Data maintained for the http client-server connection
273 * (depends on if we are client or server).
274 */
275 union
276 {
277
278 struct
279 {
280 /**
281 * Active PUT requests (linked list).
282 */
283 struct MHDPutData *puts;
284
285#if DO_GET
286 /**
287 * Active GET requests (linked list; most
288 * recent received GET is the head of the list).
289 */
290 struct MHDGetData *gets;
291#endif
292
293 } server;
294
295 struct
296 {
297
298 /**
299 * Address of the other peer.
300 */
301 HostAddress address;
302
303#if DO_GET
304 /**
305 * Last time the GET was active.
306 */
307 GNUNET_CronTime last_get_activity;
308
309 /**
310 * What was the last time we were able to
311 * transmit data using the current get handle?
312 */
313 GNUNET_CronTime last_get_initiated;
314
315 /**
316 * GET operation
317 */
318 CURL *get;
319
320 /**
321 * Read buffer for the header (from GET).
322 */
323 char rbuff1[sizeof (GNUNET_MessageHeader)];
324
325 /**
326 * The read buffer (used only receiving GET data).
327 */
328 char *rbuff2;
329
330 /**
331 * Number of valid bytes in rbuff1
332 */
333 unsigned int rpos1;
334
335 /**
336 * Number of valid bytes in rbuff2
337 */
338 unsigned int rpos2;
339
340 /**
341 * Current size of the read buffer rbuff2.
342 */
343 unsigned int rsize2;
344#endif
345
346 /**
347 * URL of the get and put operations.
348 */
349 char *url;
350
351 /**
352 * Linked list of PUT operations.
353 */
354 struct HTTPPutData *puts;
355
356 } client;
357
358 } cs;
359
360} HTTPSession;
361
362/* *********** globals ************* */
363
364static int stat_bytesReceived;
365
366static int stat_bytesSent;
367
368static int stat_bytesDropped;
369
370static int stat_get_issued;
371
372static int stat_get_received;
373
374static int stat_put_issued;
375
376static int stat_put_received;
377
378static int stat_select_calls;
379
380static int stat_send_calls;
381
382static int stat_connect_calls;
383
384static int stat_curl_send_callbacks;
385
386static int stat_curl_receive_callbacks;
387
388static int stat_mhd_access_callbacks;
389
390static int stat_mhd_read_callbacks;
391
392static int stat_mhd_close_callbacks;
393
394static int stat_connect_calls;
395
396/**
397 * How many requests do we have currently pending
398 * (with libcurl)?
399 */
400static unsigned int http_requests_pending;
401
402static int signal_pipe[2];
403
404static char *proxy;
405
406/**
407 * Daemon for listening for new connections.
408 */
409static struct MHD_Daemon *mhd_daemon;
410
411/**
412 * Curl multi for managing client operations.
413 */
414static CURLM *curl_multi;
415
416/**
417 * Set to GNUNET_YES while the transport is running.
418 */
419static int http_running;
420
421/**
422 * Thread running libcurl activities.
423 */
424static struct GNUNET_ThreadHandle *curl_thread;
425
426/**
427 * Array of currently active HTTP sessions.
428 */
429static GNUNET_TSession **tsessions;
430
431/**
432 * Number of valid entries in tsessions.
433 */
434static unsigned int tsessionCount;
435
436/**
437 * Sie of the tsessions array.
438 */
439static unsigned int tsessionArrayLength;
440
441/**
442 * Lock for concurrent access to all structures used
443 * by http, including CURL.
444 */
445static struct GNUNET_Mutex *lock;
446
447
448/**
449 * Signal select thread that its selector
450 * set may have changed.
451 */
452static void
453signal_select ()
454{
455 static char c;
456 WRITE (signal_pipe[1], &c, sizeof (c));
457}
458
459/**
460 * Check if we are allowed to connect to the given IP.
461 */
462static int
463acceptPolicyCallback (void *cls,
464 const struct sockaddr *addr, socklen_t addr_len)
465{
466 if (GNUNET_NO != is_rejected_tester (addr, addr_len))
467 return MHD_NO;
468 return MHD_YES;
469}
470
471/**
472 * Disconnect from a remote node. May only be called
473 * on sessions that were acquired by the caller first.
474 * For the core, aquiration means to call associate or
475 * connect. The number of disconnects must match the
476 * number of calls to connect+associate.
477 *
478 * Sessions are actually discarded in cleanup_connections.
479 *
480 *
481 * @param tsession the session that is closed
482 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
483 */
484static int
485httpDisconnect (GNUNET_TSession * tsession)
486{
487 HTTPSession *httpsession = tsession->internal;
488 if (httpsession == NULL)
489 {
490 GNUNET_free (tsession);
491 return GNUNET_OK;
492 }
493 GNUNET_mutex_lock (lock);
494 httpsession->users--;
495 GNUNET_mutex_unlock (lock);
496 return GNUNET_OK;
497}
498
499static void
500destroy_tsession (GNUNET_TSession * tsession)
501{
502 HTTPSession *httpsession = tsession->internal;
503 struct HTTPPutData *pos;
504 struct HTTPPutData *next;
505#if DO_GET
506 struct MHDGetData *gpos;
507 struct MHDGetData *gnext;
508#endif
509 struct MHD_Response *r;
510 int i;
511
512 GNUNET_mutex_lock (lock);
513 for (i = 0; i < tsessionCount; i++)
514 {
515 if (tsessions[i] == tsession)
516 {
517 tsessions[i] = tsessions[--tsessionCount];
518 break;
519 }
520 }
521 if (httpsession->is_client)
522 {
523#if DO_GET
524 curl_multi_remove_handle (curl_multi, httpsession->cs.client.get);
525 http_requests_pending--;
526 signal_select ();
527 curl_easy_cleanup (httpsession->cs.client.get);
528 GNUNET_array_grow (httpsession->cs.client.rbuff2,
529 httpsession->cs.client.rsize2, 0);
530#endif
531 GNUNET_free_non_null (httpsession->cs.client.url);
532 pos = httpsession->cs.client.puts;
533 while (pos != NULL)
534 {
535 next = pos->next;
536 curl_multi_remove_handle (curl_multi, pos->curl_put);
537 http_requests_pending--;
538 signal_select ();
539 curl_easy_cleanup (pos->curl_put);
540 GNUNET_free (pos->msg);
541 GNUNET_free (pos);
542 pos = next;
543 }
544 GNUNET_free (httpsession);
545 GNUNET_free (tsession);
546 }
547 else
548 {
549 httpsession->destroyed = GNUNET_YES;
550 GNUNET_GE_BREAK (NULL, httpsession->cs.server.puts == NULL);
551#if DO_GET
552 gpos = httpsession->cs.server.gets;
553 while (gpos != NULL)
554 {
555 GNUNET_array_grow (gpos->wbuff, gpos->wsize, 0);
556 r = gpos->get;
557 gpos->get = NULL;
558 gnext = gpos->next;
559 MHD_destroy_response (r);
560 gpos = gnext;
561 }
562 httpsession->cs.server.gets = NULL;
563#endif
564 GNUNET_free (httpsession->tsession);
565 GNUNET_free (httpsession);
566 }
567 GNUNET_mutex_unlock (lock);
568}
569
570/**
571 * MHD is done handling a request. Cleanup
572 * the respective transport state.
573 */
574static void
575requestCompletedCallback (void *unused,
576 struct MHD_Connection *session,
577 void **httpSessionCache)
578{
579 HTTPSession *httpsession = *httpSessionCache;
580 struct MHDPutData *pprev;
581 struct MHDPutData *ppos;
582#if DO_GET
583 struct MHDGetData *gprev;
584 struct MHDGetData *gpos;
585#endif
586
587 if (stats != NULL)
588 stats->change (stat_mhd_close_callbacks, 1);
589 if (httpsession == NULL)
590 return; /* oops */
591 GNUNET_GE_ASSERT (NULL, !httpsession->is_client);
592 pprev = NULL;
593 ppos = httpsession->cs.server.puts;
594 while (ppos != NULL)
595 {
596 if (ppos->session == session)
597 {
598 ppos->last_activity = 0;
599 signal_select ();
600 return;
601 }
602 pprev = ppos;
603 ppos = ppos->next;
604 }
605#if DO_GET
606 gprev = NULL;
607 gpos = httpsession->cs.server.gets;
608 while (gpos != NULL)
609 {
610 if (gpos->session == session)
611 {
612 gpos->last_get_activity = 0;
613 signal_select ();
614 return;
615 }
616 gprev = gpos;
617 gpos = gpos->next;
618 }
619#endif
620 httpsession->is_mhd_active--;
621}
622
623/**
624 * A (core) Session is to be associated with a transport session. The
625 * transport service may want to know in order to call back on the
626 * core if the connection is being closed. Associate can also be
627 * called to test if it would be possible to associate the session
628 * later, in this case the argument session is NULL. This can be used
629 * to test if the connection must be closed by the core or if the core
630 * can assume that it is going to be self-managed (if associate
631 * returns GNUNET_OK and session was NULL, the transport layer is responsible
632 * for eventually freeing resources associated with the tesession). If
633 * session is not NULL, the core takes responsbility for eventually
634 * calling disconnect.
635 *
636 * @param tsession the session handle passed along
637 * from the call to receive that was made by the transport
638 * layer
639 * @return GNUNET_OK if the session could be associated,
640 * GNUNET_SYSERR if not.
641 */
642static int
643httpAssociate (GNUNET_TSession * tsession)
644{
645 HTTPSession *httpSession;
646
647 if (tsession == NULL)
648 {
649 GNUNET_GE_BREAK (NULL, 0);
650 return GNUNET_SYSERR;
651 }
652 httpSession = tsession->internal;
653 GNUNET_mutex_lock (lock);
654 if (httpSession->destroyed == GNUNET_YES)
655 {
656 GNUNET_mutex_unlock (lock);
657 return GNUNET_SYSERR;
658 }
659 httpSession->users++;
660 GNUNET_mutex_unlock (lock);
661 return GNUNET_OK;
662}
663
664/**
665 * Add a new session to the array watched by the select thread. Grows
666 * the array if needed. If the caller wants to do anything useful
667 * with the return value, it must have the lock before
668 * calling. It is ok to call this function without holding lock if
669 * the return value is ignored.
670 */
671static unsigned int
672addTSession (GNUNET_TSession * tsession)
673{
674 unsigned int i;
675
676 GNUNET_mutex_lock (lock);
677 if (tsessionCount == tsessionArrayLength)
678 GNUNET_array_grow (tsessions, tsessionArrayLength,
679 tsessionArrayLength * 2);
680 i = tsessionCount;
681 tsessions[tsessionCount++] = tsession;
682 GNUNET_mutex_unlock (lock);
683 return i;
684}
685
686#if DO_GET
687/**
688 * Callback for processing GET requests if our side is the
689 * MHD HTTP server.
690 *
691 * @param cls the HTTP session
692 * @param pos read-offset in the stream
693 * @param buf where to write the data
694 * @param max how much data to write (at most)
695 * @return number of bytes written, 0 is allowed!
696 */
697static int
698contentReaderCallback (void *cls, uint64_t pos, char *buf, int max)
699{
700 struct MHDGetData *mgd = cls;
701
702 if (stats != NULL)
703 stats->change (stat_mhd_read_callbacks, 1);
704 GNUNET_mutex_lock (lock);
705 if (mgd->wpos < max)
706 max = mgd->wpos;
707 memcpy (buf, &mgd->wbuff[mgd->woff], max);
708 mgd->wpos -= max;
709 mgd->woff += max;
710 if (max > 0)
711 mgd->last_get_activity = GNUNET_get_time ();
712 if (mgd->wpos == 0)
713 mgd->woff = 0;
714 GNUNET_mutex_unlock (lock);
715#if DEBUG_HTTP
716 GNUNET_GE_LOG (coreAPI->ectx,
717 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
718 "HTTP returns %u bytes in MHD's GET handler.\n", max);
719#endif
720 if (stats != NULL)
721 stats->change (stat_bytesSent, max);
722 if ((max == 0) && (mgd->httpsession->cs.server.gets != mgd))
723 return -1; /* end of response (another GET replaces this one) */
724 return max;
725}
726#endif
727
728#if DO_GET
729/**
730 * Notification that libmicrohttpd no longer needs the
731 * response object.
732 */
733static void
734contentReaderFreeCallback (void *cls)
735{
736 struct MHDGetData *mgd = cls;
737
738 GNUNET_GE_ASSERT (NULL, mgd->get == NULL);
739 GNUNET_array_grow (mgd->wbuff, mgd->wsize, 0);
740 GNUNET_free (mgd);
741}
742#endif
743
744/**
745 * Process GET or PUT request received via MHD. For
746 * GET, queue response that will send back our pending
747 * messages. For PUT, process incoming data and send
748 * to GNUnet core. In either case, check if a session
749 * already exists and create a new one if not.
750 */
751static int
752accessHandlerCallback (void *cls,
753 struct MHD_Connection *session,
754 const char *url,
755 const char *method,
756 const char *version,
757 const char *upload_data,
758 size_t * upload_data_size, void **httpSessionCache)
759{
760 GNUNET_TSession *tsession;
761 struct MHDPutData *put;
762 struct MHDGetData *get;
763 HTTPSession *httpSession;
764 struct MHD_Response *response;
765 GNUNET_HashCode client;
766 int i;
767 unsigned int have;
768 GNUNET_MessageHeader *hdr;
769 GNUNET_TransportPacket *mp;
770 unsigned int cpy;
771 unsigned int poff;
772
773 if (stats != NULL)
774 stats->change (stat_mhd_access_callbacks, 1);
775#if DEBUG_HTTP
776 GNUNET_GE_LOG (coreAPI->ectx,
777 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
778 "HTTP/MHD receives `%s' request.\n", method);
779#endif
780 /* convert URL to sender peer id */
781 if ((strlen (url) < 2)
782 || (GNUNET_OK != GNUNET_enc_to_hash (&url[1], &client)))
783 {
784 /* invalid request */
785 /* GNUNET_GE_BREAK_OP (NULL, 0); -- this happens a lot, most likely
786 somebody scanning for MyDoom.X-opened backdoors */
787 return MHD_NO;
788 }
789
790 /* check if we already have a session for this */
791 httpSession = *httpSessionCache;
792 if (httpSession == NULL)
793 {
794 /* new http connection */
795 if (stats != NULL)
796 {
797 if (0 == strcasecmp (MHD_HTTP_METHOD_PUT, method))
798 stats->change (stat_put_received, 1);
799 else
800 stats->change (stat_get_received, 1);
801 }
802 GNUNET_mutex_lock (lock);
803 for (i = 0; i < tsessionCount; i++)
804 {
805 tsession = tsessions[i];
806 httpSession = tsession->internal;
807 if ((0 ==
808 memcmp (&httpSession->sender, &client,
809 sizeof (GNUNET_HashCode)))
810 && (httpSession->is_client == GNUNET_NO))
811 break;
812 tsession = NULL;
813 httpSession = NULL;
814 }
815 GNUNET_mutex_unlock (lock);
816 }
817 /* create new session if necessary */
818 if (httpSession == NULL)
819 {
820#if DEBUG_HTTP
821 GNUNET_GE_LOG (coreAPI->ectx,
822 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
823 "HTTP/MHD creates new session for request from `%s'.\n",
824 &url[1]);
825#endif
826 httpSession = GNUNET_malloc (sizeof (HTTPSession));
827 memset (httpSession, 0, sizeof (HTTPSession));
828 httpSession->sender.hashPubKey = client;
829 httpSession->users = 0; /* MHD */
830 tsession = GNUNET_malloc (sizeof (GNUNET_TSession));
831 memset (tsession, 0, sizeof (GNUNET_TSession));
832 tsession->ttype = GNUNET_TRANSPORT_PROTOCOL_NUMBER_HTTP;
833 tsession->internal = httpSession;
834 tsession->peer.hashPubKey = client;
835 httpSession->tsession = tsession;
836 addTSession (tsession);
837 }
838 if (*httpSessionCache == NULL)
839 {
840 httpSession->is_mhd_active++;
841 *httpSessionCache = httpSession;
842 }
843 GNUNET_mutex_lock (lock);
844#if DO_GET
845 if (0 == strcasecmp (MHD_HTTP_METHOD_GET, method))
846 {
847#if DEBUG_HTTP
848 GNUNET_GE_LOG (coreAPI->ectx,
849 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
850 "HTTP/MHD receives GET request from `%s'.\n", &url[1]);
851#endif
852
853 /* handle get; create response object if we do not
854 have one already */
855 get = GNUNET_malloc (sizeof (struct MHDGetData));
856 memset (get, 0, sizeof (struct MHDGetData));
857 get->next = httpSession->cs.server.gets;
858 httpSession->cs.server.gets = get;
859 get->session = session;
860 get->httpsession = httpSession;
861 get->last_get_activity = GNUNET_get_time ();
862 get->get = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
863 64 * 1024,
864 contentReaderCallback,
865 get,
866 contentReaderFreeCallback);
867 MHD_queue_response (session, MHD_HTTP_OK, get->get);
868 GNUNET_mutex_unlock (lock);
869 return MHD_YES;
870 }
871#endif
872 if (0 == strcasecmp (MHD_HTTP_METHOD_PUT, method))
873 {
874#if DEBUG_HTTP
875 GNUNET_GE_LOG (coreAPI->ectx,
876 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
877 "HTTP/MHD receives PUT request from `%s' with %u bytes.\n",
878 &url[1], *upload_data_size);
879#endif
880 put = httpSession->cs.server.puts;
881 while ((put != NULL) && (put->session != session))
882 put = put->next;
883 if (put == NULL)
884 {
885 put = GNUNET_malloc (sizeof (struct MHDPutData));
886 memset (put, 0, sizeof (struct MHDPutData));
887 put->next = httpSession->cs.server.puts;
888 httpSession->cs.server.puts = put;
889 put->session = session;
890 }
891 put->last_activity = GNUNET_get_time ();
892
893 /* handle put (upload_data!) */
894 poff = 0;
895 have = *upload_data_size;
896 if (stats != NULL)
897 stats->change (stat_bytesReceived, have);
898 *upload_data_size = 0; /* we will always process everything */
899 if ((have == 0) && (put->done == GNUNET_NO)
900 && (put->ready == GNUNET_YES))
901 {
902 put->done = GNUNET_YES;
903 /* end of upload, send response! */
904#if DEBUG_HTTP
905 GNUNET_GE_LOG (coreAPI->ectx,
906 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
907 "HTTP/MHD queues dummy response to completed PUT request.\n");
908#endif
909 response =
910 MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),
911 HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
912 MHD_queue_response (session, MHD_HTTP_OK, response);
913 MHD_destroy_response (response);
914 GNUNET_mutex_unlock (lock);
915 return MHD_YES;
916 }
917 while (have > 0)
918 {
919 put->ready = GNUNET_NO;
920 if (put->rpos1 < sizeof (GNUNET_MessageHeader))
921 {
922 cpy = sizeof (GNUNET_MessageHeader) - put->rpos1;
923 if (cpy > have)
924 cpy = have;
925 memcpy (&put->rbuff1[put->rpos1], &upload_data[poff], cpy);
926 put->rpos1 += cpy;
927 have -= cpy;
928 poff += cpy;
929 put->rpos2 = 0;
930 }
931 if (put->rpos1 < sizeof (GNUNET_MessageHeader))
932 break;
933 hdr = (GNUNET_MessageHeader *) put->rbuff1;
934 GNUNET_array_grow (put->rbuff2,
935 put->rsize2,
936 ntohs (hdr->size) -
937 sizeof (GNUNET_MessageHeader));
938 if (put->rpos2 < ntohs (hdr->size) - sizeof (GNUNET_MessageHeader))
939 {
940 cpy =
941 ntohs (hdr->size) - sizeof (GNUNET_MessageHeader) -
942 put->rpos2;
943 if (cpy > have)
944 cpy = have;
945 memcpy (&put->rbuff2[put->rpos2], &upload_data[poff], cpy);
946 have -= cpy;
947 poff += cpy;
948 put->rpos2 += cpy;
949 }
950 if (put->rpos2 < ntohs (hdr->size) - sizeof (GNUNET_MessageHeader))
951 break;
952 mp = GNUNET_malloc (sizeof (GNUNET_TransportPacket));
953 mp->msg = put->rbuff2;
954 mp->sender = httpSession->sender;
955 mp->tsession = httpSession->tsession;
956 mp->size = ntohs (hdr->size) - sizeof (GNUNET_MessageHeader);
957#if DEBUG_HTTP
958 GNUNET_GE_LOG (coreAPI->ectx,
959 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
960 "HTTP/MHD passes %u bytes to core (received via PUT request).\n",
961 mp->size);
962#endif
963 coreAPI->receive (mp);
964 put->rbuff2 = NULL;
965 put->rpos2 = 0;
966 put->rsize2 = 0;
967 put->rpos1 = 0;
968 put->ready = GNUNET_YES;
969 }
970 GNUNET_mutex_unlock (lock);
971 return MHD_YES;
972 }
973 GNUNET_mutex_unlock (lock);
974 GNUNET_GE_BREAK_OP (NULL, 0); /* invalid request */
975 return MHD_NO;
976}
977
978#if DO_GET
979/**
980 * Process downloaded bits (from GET via CURL).
981 */
982static size_t
983receiveContentCallback (void *ptr, size_t size, size_t nmemb, void *ctx)
984{
985 HTTPSession *httpSession = ctx;
986 const char *inbuf = ptr;
987 size_t have = size * nmemb;
988 size_t poff = 0;
989 size_t cpy;
990 GNUNET_MessageHeader *hdr;
991 GNUNET_TransportPacket *mp;
992
993 if (stats != NULL)
994 stats->change (stat_curl_receive_callbacks, 1);
995 httpSession->cs.client.last_get_activity = GNUNET_get_time ();
996#if DEBUG_HTTP
997 GNUNET_GE_LOG (coreAPI->ectx,
998 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
999 "HTTP/CURL receives %u bytes as response to GET.\n",
1000 size * nmemb);
1001#endif
1002 while (have > 0)
1003 {
1004 if (httpSession->cs.client.rpos1 < sizeof (GNUNET_MessageHeader))
1005 {
1006 cpy = sizeof (GNUNET_MessageHeader) - httpSession->cs.client.rpos1;
1007 if (cpy > have)
1008 cpy = have;
1009 memcpy (&httpSession->cs.client.
1010 rbuff1[httpSession->cs.client.rpos1], &inbuf[poff], cpy);
1011 httpSession->cs.client.rpos1 += cpy;
1012 have -= cpy;
1013 poff += cpy;
1014 httpSession->cs.client.rpos2 = 0;
1015 }
1016 if (httpSession->cs.client.rpos1 < sizeof (GNUNET_MessageHeader))
1017 break;
1018 hdr = (GNUNET_MessageHeader *) httpSession->cs.client.rbuff1;
1019 GNUNET_array_grow (httpSession->cs.client.rbuff2,
1020 httpSession->cs.client.rsize2,
1021 ntohs (hdr->size) - sizeof (GNUNET_MessageHeader));
1022 if (httpSession->cs.client.rpos2 <
1023 ntohs (hdr->size) - sizeof (GNUNET_MessageHeader))
1024 {
1025 cpy =
1026 ntohs (hdr->size) - sizeof (GNUNET_MessageHeader) -
1027 httpSession->cs.client.rpos2;
1028 if (cpy > have)
1029 cpy = have;
1030 memcpy (&httpSession->cs.client.
1031 rbuff2[httpSession->cs.client.rpos2], &inbuf[poff], cpy);
1032 have -= cpy;
1033 poff += cpy;
1034 httpSession->cs.client.rpos2 += cpy;
1035 }
1036 if (httpSession->cs.client.rpos2 <
1037 ntohs (hdr->size) - sizeof (GNUNET_MessageHeader))
1038 break;
1039 mp = GNUNET_malloc (sizeof (GNUNET_TransportPacket));
1040 mp->msg = httpSession->cs.client.rbuff2;
1041 mp->sender = httpSession->sender;
1042 mp->tsession = httpSession->tsession;
1043 mp->size = ntohs (hdr->size) - sizeof (GNUNET_MessageHeader);
1044 coreAPI->receive (mp);
1045 httpSession->cs.client.rbuff2 = NULL;
1046 httpSession->cs.client.rpos2 = 0;
1047 httpSession->cs.client.rsize2 = 0;
1048 httpSession->cs.client.rpos1 = 0;
1049 }
1050 if (stats != NULL)
1051 stats->change (stat_bytesReceived, size * nmemb);
1052 return size * nmemb;
1053}
1054#endif
1055
1056/**
1057 * Provide bits for upload: we're using CURL for a PUT request
1058 * and now need to provide data from the message we are transmitting.
1059 */
1060static size_t
1061sendContentCallback (void *ptr, size_t size, size_t nmemb, void *ctx)
1062{
1063 struct HTTPPutData *put = ctx;
1064 size_t max = size * nmemb;
1065
1066 if (stats != NULL)
1067 stats->change (stat_curl_send_callbacks, 1);
1068 put->last_activity = GNUNET_get_time ();
1069 if (max > put->size - put->pos)
1070 max = put->size - put->pos;
1071 memcpy (ptr, &put->msg[put->pos], max);
1072 put->pos += max;
1073#if DEBUG_HTTP
1074 GNUNET_GE_LOG (coreAPI->ectx,
1075 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1076 "HTTP/CURL sends %u bytes in PUT request.\n", max);
1077#endif
1078 if (stats != NULL)
1079 stats->change (stat_bytesSent, max);
1080 return max;
1081}
1082
1083#define CURL_EASY_SETOPT(c, a, b) do { ret = curl_easy_setopt(c, a, b); if (ret != CURLE_OK) GNUNET_GE_LOG(coreAPI->ectx, GNUNET_GE_WARNING | GNUNET_GE_USER | GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"), "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret)); } while (0);
1084#define IP_BUF_LEN 128
1085
1086static void
1087create_session_url (HTTPSession * httpSession)
1088{
1089 char buf[IP_BUF_LEN];
1090 char *url;
1091 GNUNET_EncName enc;
1092 unsigned short available;
1093 const char *obr;
1094 const char *cbr;
1095 const HostAddress *haddr =
1096 (const HostAddress *) &httpSession->cs.client.address;
1097
1098 url = httpSession->cs.client.url;
1099 if (url == NULL)
1100 {
1101 GNUNET_hash_to_enc (&coreAPI->my_identity->hashPubKey, &enc);
1102 available = ntohs (haddr->availability) & available_protocols;
1103 if (available == (VERSION_AVAILABLE_IPV4 | VERSION_AVAILABLE_IPV6))
1104 {
1105 if (GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 2) == 0)
1106 available = VERSION_AVAILABLE_IPV4;
1107 else
1108 available = VERSION_AVAILABLE_IPV6;
1109 }
1110 if ((available & VERSION_AVAILABLE_IPV4) > 0)
1111 {
1112 if (NULL == inet_ntop (AF_INET, &haddr->ipv4, buf, IP_BUF_LEN))
1113 {
1114 /* log? */
1115 return;
1116 }
1117 obr = "";
1118 cbr = "";
1119 }
1120 else if ((available & VERSION_AVAILABLE_IPV6) > 0)
1121 {
1122 if (NULL == inet_ntop (AF_INET6, &haddr->ipv6, buf, IP_BUF_LEN))
1123 {
1124 /* log? */
1125 return;
1126 }
1127 obr = "[";
1128 cbr = "]";
1129 }
1130 else
1131 return; /* error */
1132 url = GNUNET_malloc (64 + sizeof (GNUNET_EncName) + strlen (buf));
1133 GNUNET_snprintf (url,
1134 64 + sizeof (GNUNET_EncName),
1135 "http://%s%s%s:%u/%s", obr, buf, cbr,
1136 ntohs (haddr->port), &enc);
1137 httpSession->cs.client.url = url;
1138 }
1139}
1140
1141#if DO_GET
1142/**
1143 * Try to do a GET on the other peer of the given
1144 * http session.
1145 *
1146 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1147 */
1148static int
1149create_curl_get (HTTPSession * httpSession)
1150{
1151 CURL *curl_get;
1152 CURLcode ret;
1153 CURLMcode mret;
1154 GNUNET_CronTime now;
1155
1156 if (httpSession->cs.client.url == NULL)
1157 return GNUNET_SYSERR;
1158 curl_get = httpSession->cs.client.get;
1159 if (curl_get != NULL)
1160 {
1161 GNUNET_mutex_lock (lock);
1162 curl_multi_remove_handle (curl_multi, curl_get);
1163 http_requests_pending--;
1164 signal_select ();
1165 curl_easy_cleanup (curl_get);
1166 GNUNET_mutex_unlock (lock);
1167 httpSession->cs.client.get = NULL;
1168 }
1169 curl_get = curl_easy_init ();
1170 if (curl_get == NULL)
1171 return GNUNET_SYSERR;
1172 /* create GET */
1173 CURL_EASY_SETOPT (curl_get, CURLOPT_FAILONERROR, 1);
1174 CURL_EASY_SETOPT (curl_get, CURLOPT_URL, httpSession->cs.client.url);
1175 if (strlen (proxy) > 0)
1176 CURL_EASY_SETOPT (curl_get, CURLOPT_PROXY, proxy);
1177 CURL_EASY_SETOPT (curl_get, CURLOPT_BUFFERSIZE, 32 * 1024);
1178 if (0 == strncmp (httpSession->cs.client.url, "http", 4))
1179 CURL_EASY_SETOPT (curl_get, CURLOPT_USERAGENT, "GNUnet-http");
1180#if 0
1181 CURL_EASY_SETOPT (curl_get, CURLOPT_VERBOSE, 1);
1182#endif
1183 CURL_EASY_SETOPT (curl_get, CURLOPT_CONNECTTIMEOUT, 150L);
1184 /* NOTE: use of CONNECTTIMEOUT without also
1185 setting NOSIGNAL results in really weird
1186 crashes on my system! */
1187 CURL_EASY_SETOPT (curl_get, CURLOPT_NOSIGNAL, 1);
1188 CURL_EASY_SETOPT (curl_get, CURLOPT_TIMEOUT, 150L);
1189 CURL_EASY_SETOPT (curl_get, CURLOPT_WRITEFUNCTION, &receiveContentCallback);
1190 CURL_EASY_SETOPT (curl_get, CURLOPT_WRITEDATA, httpSession);
1191 CURL_EASY_SETOPT (curl_get, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
1192 if (ret != CURLE_OK)
1193 {
1194 curl_easy_cleanup (curl_get);
1195 return GNUNET_SYSERR;
1196 }
1197 GNUNET_mutex_lock (lock);
1198 mret = curl_multi_add_handle (curl_multi, curl_get);
1199 http_requests_pending++;
1200 GNUNET_mutex_unlock (lock);
1201 if (stats != NULL)
1202 stats->change (stat_get_issued, 1);
1203 if (mret != CURLM_OK)
1204 {
1205 GNUNET_GE_LOG (coreAPI->ectx,
1206 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
1207 GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"),
1208 "curl_multi_add_handle", __FILE__, __LINE__,
1209 curl_multi_strerror (mret));
1210 curl_easy_cleanup (curl_get);
1211 return GNUNET_SYSERR;
1212 }
1213 signal_select ();
1214 now = GNUNET_get_time ();
1215 httpSession->cs.client.last_get_activity = now;
1216 httpSession->cs.client.get = curl_get;
1217 httpSession->cs.client.last_get_initiated = now;
1218#if DEBUG_HTTP
1219 GNUNET_GE_LOG (coreAPI->ectx,
1220 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1221 "HTTP/CURL initiated GET request.\n");
1222#endif
1223 return GNUNET_OK;
1224}
1225#endif
1226
1227/**
1228 * Establish a connection to a remote node.
1229 *
1230 * @param hello the hello-Message for the target node
1231 * @param tsessionPtr the session handle that is set
1232 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
1233 */
1234static int
1235httpConnect (const GNUNET_MessageHello * hello,
1236 GNUNET_TSession ** tsessionPtr, int may_reuse)
1237{
1238 const HostAddress *haddr = (const HostAddress *) &hello[1];
1239 GNUNET_TSession *tsession;
1240 HTTPSession *httpSession;
1241 int i;
1242
1243 if (stats != NULL)
1244 stats->change (stat_connect_calls, 1);
1245 /* check if we have a session pending for this peer */
1246 tsession = NULL;
1247 if (may_reuse)
1248 {
1249 GNUNET_mutex_lock (lock);
1250 for (i = 0; i < tsessionCount; i++)
1251 {
1252 if (0 == memcmp (&hello->senderIdentity,
1253 &tsessions[i]->peer, sizeof (GNUNET_PeerIdentity)))
1254 {
1255 tsession = tsessions[i];
1256 break;
1257 }
1258 }
1259 if ((tsession != NULL) && (GNUNET_OK == httpAssociate (tsession)))
1260 {
1261 *tsessionPtr = tsession;
1262 GNUNET_mutex_unlock (lock);
1263 return GNUNET_OK;
1264 }
1265 GNUNET_mutex_unlock (lock);
1266 }
1267 /* no session pending, initiate a new one! */
1268 httpSession = GNUNET_malloc (sizeof (HTTPSession));
1269 memset (httpSession, 0, sizeof (HTTPSession));
1270 httpSession->sender = hello->senderIdentity;
1271 httpSession->users = 1; /* us only, core has not seen this tsession! */
1272 httpSession->is_client = GNUNET_YES;
1273 httpSession->cs.client.address = *haddr;
1274 tsession = GNUNET_malloc (sizeof (GNUNET_TSession));
1275 memset (tsession, 0, sizeof (GNUNET_TSession));
1276 httpSession->tsession = tsession;
1277 tsession->ttype = GNUNET_TRANSPORT_PROTOCOL_NUMBER_HTTP;
1278 tsession->internal = httpSession;
1279 tsession->peer = hello->senderIdentity;
1280 create_session_url (httpSession);
1281#if DO_GET
1282 if (GNUNET_OK != create_curl_get (httpSession))
1283 {
1284 GNUNET_free (tsession);
1285 GNUNET_free (httpSession);
1286 return GNUNET_SYSERR;
1287 }
1288#endif
1289 /* PUTs will be created as needed */
1290 addTSession (tsession);
1291 *tsessionPtr = tsession;
1292#if DEBUG_HTTP
1293 GNUNET_GE_LOG (coreAPI->ectx,
1294 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1295 "HTTP/CURL initiated connection to `%s'.\n",
1296 httpSession->cs.client.url);
1297#endif
1298 return GNUNET_OK;
1299}
1300
1301/**
1302 * We received the "Thank you!" response to a PUT.
1303 * Discard the data (not useful) and mark the PUT
1304 * operation as completed.
1305 */
1306static size_t
1307discardContentCallback (void *data, size_t size, size_t nmemb, void *put_cls)
1308{
1309 struct HTTPPutData *put = put_cls;
1310 /* this condition should pretty much always be
1311 true; just checking here in case the PUT
1312 response comes early somehow */
1313 if (put->pos == put->size)
1314 put->done = GNUNET_YES;
1315 return size * nmemb;
1316}
1317
1318/**
1319 * Create a new PUT request for the given PUT data.
1320 */
1321static int
1322create_curl_put (HTTPSession * httpSession, struct HTTPPutData *put)
1323{
1324 CURL *curl_put;
1325 CURLcode ret;
1326 CURLMcode mret;
1327 long size;
1328
1329 /* we should have initiated a GET earlier,
1330 so URL must not be NULL here */
1331 if (httpSession->cs.client.url == NULL)
1332 return GNUNET_SYSERR;
1333 curl_put = curl_easy_init ();
1334 if (curl_put == NULL)
1335 return GNUNET_SYSERR;
1336 CURL_EASY_SETOPT (curl_put, CURLOPT_FAILONERROR, 1);
1337 CURL_EASY_SETOPT (curl_put, CURLOPT_URL, httpSession->cs.client.url);
1338 if (strlen (proxy) > 0)
1339 CURL_EASY_SETOPT (curl_put, CURLOPT_PROXY, proxy);
1340 CURL_EASY_SETOPT (curl_put, CURLOPT_BUFFERSIZE, put->size);
1341 if (0 == strncmp (httpSession->cs.client.url, "http", 4))
1342 CURL_EASY_SETOPT (curl_put, CURLOPT_USERAGENT, "GNUnet-http");
1343 CURL_EASY_SETOPT (curl_put, CURLOPT_UPLOAD, 1);
1344#if 0
1345 CURL_EASY_SETOPT (curl_put, CURLOPT_VERBOSE, 1);
1346#endif
1347 CURL_EASY_SETOPT (curl_put, CURLOPT_CONNECTTIMEOUT, 150L);
1348 /* NOTE: use of CONNECTTIMEOUT without also
1349 setting NOSIGNAL results in really weird
1350 crashes on my system! */
1351 CURL_EASY_SETOPT (curl_put, CURLOPT_NOSIGNAL, 1);
1352 CURL_EASY_SETOPT (curl_put, CURLOPT_TIMEOUT, 150L);
1353 size = put->size;
1354 CURL_EASY_SETOPT (curl_put, CURLOPT_INFILESIZE, size);
1355 CURL_EASY_SETOPT (curl_put, CURLOPT_READFUNCTION, &sendContentCallback);
1356 CURL_EASY_SETOPT (curl_put, CURLOPT_READDATA, put);
1357 CURL_EASY_SETOPT (curl_put, CURLOPT_WRITEFUNCTION, &discardContentCallback);
1358 CURL_EASY_SETOPT (curl_put, CURLOPT_WRITEDATA, put);
1359 CURL_EASY_SETOPT (curl_put, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
1360 if (ret != CURLE_OK)
1361 {
1362 curl_easy_cleanup (curl_put);
1363 return GNUNET_SYSERR;
1364 }
1365 GNUNET_mutex_lock (lock);
1366 mret = curl_multi_add_handle (curl_multi, curl_put);
1367 http_requests_pending++;
1368 GNUNET_mutex_unlock (lock);
1369 if (stats != NULL)
1370 stats->change (stat_put_issued, 1);
1371 if (mret != CURLM_OK)
1372 {
1373 GNUNET_GE_LOG (coreAPI->ectx,
1374 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
1375 GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"),
1376 "curl_multi_add_handle", __FILE__, __LINE__,
1377 curl_multi_strerror (mret));
1378 return GNUNET_SYSERR;
1379 }
1380 signal_select ();
1381 put->curl_put = curl_put;
1382#if DEBUG_HTTP
1383 GNUNET_GE_LOG (coreAPI->ectx,
1384 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1385 "HTTP/CURL initiated PUT request to `%s'.\n",
1386 httpSession->cs.client.url);
1387#endif
1388 return GNUNET_OK;
1389}
1390
1391
1392/**
1393 * Test if the transport would even try to send
1394 * a message of the given size and importance
1395 * for the given session.<br>
1396 * This function is used to check if the core should
1397 * even bother to construct (and encrypt) this kind
1398 * of message.
1399 *
1400 * @return GNUNET_YES if the transport would try (i.e. queue
1401 * the message or call the OS to send),
1402 * GNUNET_NO if the transport would just drop the message,
1403 * GNUNET_SYSERR if the size/session is invalid
1404 */
1405static int
1406httpTestWouldTry (GNUNET_TSession * tsession, const unsigned int size,
1407 int important)
1408{
1409 HTTPSession *httpSession = tsession->internal;
1410 struct MHDGetData *get;
1411 int ret;
1412
1413 if (size >= GNUNET_MAX_BUFFER_SIZE - sizeof (GNUNET_MessageHeader))
1414 {
1415 GNUNET_GE_BREAK (coreAPI->ectx, 0);
1416 return GNUNET_SYSERR;
1417 }
1418 if (size == 0)
1419 {
1420 GNUNET_GE_BREAK (coreAPI->ectx, 0);
1421 return GNUNET_SYSERR;
1422 }
1423 if (httpSession->is_client)
1424 {
1425 /* client */
1426 if ((important != GNUNET_YES) && (httpSession->cs.client.puts != NULL))
1427 return GNUNET_NO;
1428 return GNUNET_YES;
1429 }
1430 else
1431 {
1432 /* server */
1433 GNUNET_mutex_lock (lock);
1434 get = httpSession->cs.server.gets;
1435 if (get == NULL)
1436 ret = GNUNET_NO;
1437 else
1438 {
1439 if (get->wsize == 0)
1440 ret = GNUNET_YES;
1441 else if ((get->wpos + size > get->wsize)
1442 && (important != GNUNET_YES))
1443 ret = GNUNET_NO;
1444 else
1445 ret = GNUNET_YES;
1446 }
1447 GNUNET_mutex_unlock (lock);
1448 return ret;
1449 }
1450}
1451
1452
1453/**
1454 * Send a message to the specified remote node.
1455 *
1456 * @param tsession the GNUNET_MessageHello identifying the remote node
1457 * @param msg the message
1458 * @param size the size of the message
1459 * @return GNUNET_SYSERR on error, GNUNET_OK on success, GNUNET_NO if queue is full
1460 */
1461static int
1462httpSend (GNUNET_TSession * tsession,
1463 const void *msg, unsigned int size, int important)
1464{
1465 HTTPSession *httpSession = tsession->internal;
1466 struct HTTPPutData *putData;
1467 GNUNET_MessageHeader *hdr;
1468#if DO_GET
1469 struct MHDGetData *getData;
1470 char *tmp;
1471#endif
1472
1473 if (stats != NULL)
1474 stats->change (stat_send_calls, 1);
1475 if (httpSession->is_client)
1476 {
1477 /* we need to do a PUT (we are the client) */
1478 if (size >= GNUNET_MAX_BUFFER_SIZE)
1479 return GNUNET_SYSERR;
1480 if (size == 0)
1481 {
1482 GNUNET_GE_BREAK (NULL, 0);
1483 return GNUNET_SYSERR;
1484 }
1485 if (important != GNUNET_YES)
1486 {
1487 GNUNET_mutex_lock (lock);
1488 if (httpSession->cs.client.puts != NULL)
1489 {
1490 /* do not queue more than one unimportant PUT at a time */
1491 signal_select (); /* do clean up now! */
1492 GNUNET_mutex_unlock (lock);
1493 if (stats != NULL)
1494 stats->change (stat_bytesDropped, size);
1495
1496 return GNUNET_NO;
1497 }
1498 GNUNET_mutex_unlock (lock);
1499 }
1500 putData = GNUNET_malloc (sizeof (struct HTTPPutData));
1501 memset (putData, 0, sizeof (struct HTTPPutData));
1502 putData->msg = GNUNET_malloc (size + sizeof (GNUNET_MessageHeader));
1503 hdr = (GNUNET_MessageHeader *) putData->msg;
1504 hdr->size = htons (size + sizeof (GNUNET_MessageHeader));
1505 hdr->type = htons (0);
1506 memcpy (&putData->msg[sizeof (GNUNET_MessageHeader)], msg, size);
1507 putData->size = size + sizeof (GNUNET_MessageHeader);
1508 putData->last_activity = GNUNET_get_time ();
1509 if (GNUNET_OK != create_curl_put (httpSession, putData))
1510 {
1511 GNUNET_free (putData->msg);
1512 GNUNET_free (putData);
1513 return GNUNET_SYSERR;
1514 }
1515 GNUNET_mutex_lock (lock);
1516 putData->next = httpSession->cs.client.puts;
1517 httpSession->cs.client.puts = putData;
1518 GNUNET_mutex_unlock (lock);
1519 return GNUNET_OK;
1520 }
1521
1522 /* httpSession->isClient == false, respond to a GET (we
1523 hopefully have one or will have one soon) */
1524#if DEBUG_HTTP
1525 GNUNET_GE_LOG (coreAPI->ectx,
1526 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1527 "HTTP/MHD queues %u bytes to be sent as response to GET as soon as possible.\n",
1528 size);
1529#endif
1530#if DO_GET
1531 GNUNET_mutex_lock (lock);
1532 getData = httpSession->cs.server.gets;
1533 if (getData == NULL)
1534 {
1535 GNUNET_mutex_unlock (lock);
1536 return GNUNET_SYSERR;
1537 }
1538 if (getData->wsize == 0)
1539 GNUNET_array_grow (getData->wbuff, getData->wsize, HTTP_BUF_SIZE);
1540 size += sizeof (GNUNET_MessageHeader);
1541 if (getData->wpos + size > getData->wsize)
1542 {
1543 /* need to grow or discard */
1544 if (!important)
1545 {
1546 GNUNET_mutex_unlock (lock);
1547 return GNUNET_NO;
1548 }
1549 tmp = GNUNET_malloc (getData->wpos + size);
1550 memcpy (tmp, &getData->wbuff[getData->woff], getData->wpos);
1551 hdr = (GNUNET_MessageHeader *) & tmp[getData->wpos];
1552 hdr->type = htons (0);
1553 hdr->size = htons (size);
1554 memcpy (&hdr[1], msg, size - sizeof (GNUNET_MessageHeader));
1555 GNUNET_free (getData->wbuff);
1556 getData->wbuff = tmp;
1557 getData->wsize = getData->wpos + size;
1558 getData->woff = 0;
1559 getData->wpos = getData->wpos + size;
1560 }
1561 else
1562 {
1563 /* fits without growing */
1564 if (getData->wpos + getData->woff + size > getData->wsize)
1565 {
1566 /* need to compact first */
1567 memmove (getData->wbuff,
1568 &getData->wbuff[getData->woff], getData->wpos);
1569 getData->woff = 0;
1570 }
1571 /* append */
1572 hdr =
1573 (GNUNET_MessageHeader *) & getData->wbuff[getData->woff +
1574 getData->wpos];
1575 hdr->size = htons (size);
1576 hdr->type = htons (0);
1577 memcpy (&hdr[1], msg, size - sizeof (GNUNET_MessageHeader));
1578 getData->wpos += size;
1579 }
1580 signal_select ();
1581 GNUNET_mutex_unlock (lock);
1582#endif
1583 return GNUNET_OK;
1584}
1585
1586/**
1587 * Function called to cleanup dead connections
1588 * (completed PUTs, GETs that have timed out,
1589 * etc.). Also re-vives GETs that have timed out
1590 * if we are still interested in the connection.
1591 */
1592static void
1593cleanup_connections ()
1594{
1595 int i;
1596 HTTPSession *s;
1597 struct HTTPPutData *prev;
1598 struct HTTPPutData *pos;
1599 struct MHDPutData *mpos;
1600 struct MHDPutData *mprev;
1601#if DO_GET
1602 struct MHD_Response *r;
1603 struct MHDGetData *gpos;
1604 struct MHDGetData *gnext;
1605#endif
1606 GNUNET_CronTime now;
1607
1608 GNUNET_mutex_lock (lock);
1609 now = GNUNET_get_time ();
1610 for (i = 0; i < tsessionCount; i++)
1611 {
1612 s = tsessions[i]->internal;
1613 if (s->is_client)
1614 {
1615 if ((s->cs.client.puts == NULL) && (s->users == 0)
1616#if DO_GET
1617 && (s->cs.client.last_get_activity + HTTP_TIMEOUT < now)
1618#endif
1619 )
1620 {
1621#if DO_GET
1622#if DEBUG_HTTP
1623 GNUNET_GE_LOG (coreAPI->ectx,
1624 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
1625 GNUNET_GE_USER,
1626 "HTTP transport destroys old (%llu ms) unused client session\n",
1627 now - s->cs.client.last_get_activity);
1628#endif
1629#endif
1630 destroy_tsession (tsessions[i]);
1631 i--;
1632 continue;
1633 }
1634
1635 prev = NULL;
1636 pos = s->cs.client.puts;
1637 while (pos != NULL)
1638 {
1639 if (pos->last_activity + HTTP_TIMEOUT < now)
1640 pos->done = GNUNET_YES;
1641 if (pos->done)
1642 {
1643 if (prev == NULL)
1644 s->cs.client.puts = pos->next;
1645 else
1646 prev->next = pos->next;
1647 GNUNET_free (pos->msg);
1648 curl_multi_remove_handle (curl_multi, pos->curl_put);
1649 http_requests_pending--;
1650 signal_select ();
1651 curl_easy_cleanup (pos->curl_put);
1652 GNUNET_free (pos);
1653 if (prev == NULL)
1654 pos = s->cs.client.puts;
1655 else
1656 pos = prev->next;
1657 continue;
1658 }
1659 prev = pos;
1660 pos = pos->next;
1661 }
1662#if DO_GET
1663 if ((s->cs.client.last_get_activity + HTTP_TIMEOUT < now) &&
1664 ((s->users > 0) || (s->cs.client.puts != NULL)) &&
1665 ((s->cs.client.last_get_initiated + HTTP_GET_REFRESH > now) ||
1666 (s->cs.client.get == NULL)) &&
1667 ((s->cs.client.get == NULL) ||
1668 (s->cs.client.last_get_activity + HTTP_GET_REFRESH / 2 < now)))
1669 create_curl_get (s);
1670#endif
1671 }
1672 else
1673 {
1674 mpos = s->cs.server.puts;
1675 mprev = NULL;
1676 while (mpos != NULL)
1677 {
1678 if (mpos->last_activity == 0)
1679 {
1680 if (mprev == NULL)
1681 s->cs.server.puts = mpos->next;
1682 else
1683 mprev->next = mpos->next;
1684 GNUNET_array_grow (mpos->rbuff2, mpos->rsize2, 0);
1685 GNUNET_free (mpos);
1686 if (mprev == NULL)
1687 mpos = s->cs.server.puts;
1688 else
1689 mpos = mprev->next;
1690 continue;
1691 }
1692 mprev = mpos;
1693 mpos = mpos->next;
1694 }
1695
1696 /* ! s->is_client */
1697#if DO_GET
1698 gpos = s->cs.server.gets;
1699 while (gpos != NULL)
1700 {
1701 gnext = gpos->next;
1702 gpos->next = NULL;
1703 if ((gpos->last_get_activity + HTTP_TIMEOUT < now) ||
1704 (gpos != s->cs.server.gets))
1705 {
1706 if (gpos == s->cs.server.gets)
1707 s->cs.server.gets = NULL;
1708 r = gpos->get;
1709 gpos->get = NULL;
1710 MHD_destroy_response (r);
1711 }
1712 gpos = gnext;
1713 }
1714#endif
1715 if (
1716#if DO_GET
1717 (s->cs.server.gets == NULL) &&
1718#endif
1719 (s->is_mhd_active == 0) && (s->users == 0))
1720 {
1721#if DO_GET
1722#if DEBUG_HTTP
1723 GNUNET_GE_LOG (coreAPI->ectx,
1724 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
1725 GNUNET_GE_USER,
1726 "HTTP transport destroys unused server session\n");
1727#endif
1728#endif
1729 destroy_tsession (tsessions[i]);
1730 i--;
1731 continue;
1732 }
1733 }
1734 }
1735 GNUNET_mutex_unlock (lock);
1736}
1737
1738/**
1739 * Thread that runs the CURL and MHD requests.
1740 */
1741static void *
1742curl_runner (void *unused)
1743{
1744 CURLMcode mret;
1745 fd_set rs;
1746 fd_set ws;
1747 fd_set es;
1748 int max;
1749 struct timeval tv;
1750 int running;
1751 unsigned long long timeout;
1752 long ms;
1753 int have_tv;
1754 char buf[128]; /* for reading from pipe */
1755 int ret;
1756
1757#if DEBUG_HTTP
1758 GNUNET_GE_LOG (coreAPI->ectx,
1759 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1760 "HTTP transport select thread started\n");
1761#endif
1762 while (GNUNET_YES == http_running)
1763 {
1764 max = 0;
1765 FD_ZERO (&rs);
1766 FD_ZERO (&ws);
1767 FD_ZERO (&es);
1768 GNUNET_mutex_lock (lock);
1769 mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
1770 GNUNET_mutex_unlock (lock);
1771 if (mret != CURLM_OK)
1772 {
1773 GNUNET_GE_LOG (coreAPI->ectx,
1774 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
1775 GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"),
1776 "curl_multi_fdset", __FILE__, __LINE__,
1777 curl_multi_strerror (mret));
1778 break;
1779 }
1780 if (mhd_daemon != NULL)
1781 MHD_get_fdset (mhd_daemon, &rs, &ws, &es, &max);
1782 timeout = 0;
1783 have_tv = MHD_NO;
1784 if (mhd_daemon != NULL)
1785 have_tv = MHD_get_timeout (mhd_daemon, &timeout);
1786 GNUNET_mutex_lock (lock);
1787 if ((CURLM_OK == curl_multi_timeout (curl_multi, &ms)) &&
1788 (ms != -1) && ((ms < timeout) || (have_tv == MHD_NO)))
1789 {
1790 timeout = ms;
1791 have_tv = MHD_YES;
1792 }
1793 GNUNET_mutex_unlock (lock);
1794 FD_SET (signal_pipe[0], &rs);
1795 if (max < signal_pipe[0])
1796 max = signal_pipe[0];
1797 tv.tv_sec = timeout / 1000;
1798 tv.tv_usec = (timeout % 1000) * 1000;
1799 if (stats != NULL)
1800 stats->change (stat_select_calls, 1);
1801 ret =
1802 SELECT (max + 1, &rs, &ws, &es, (have_tv == MHD_YES) ? &tv : NULL);
1803 if (ret == -1)
1804 {
1805 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
1806 GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
1807 GNUNET_GE_DEVELOPER, "select");
1808 }
1809 if (GNUNET_YES != http_running)
1810 break;
1811 running = 0;
1812 do
1813 {
1814 GNUNET_mutex_lock (lock);
1815 mret = curl_multi_perform (curl_multi, &running);
1816 GNUNET_mutex_unlock (lock);
1817 }
1818 while ((mret == CURLM_CALL_MULTI_PERFORM)
1819 && (http_running == GNUNET_YES));
1820 if (FD_ISSET (signal_pipe[0], &rs))
1821 read (signal_pipe[0], buf, sizeof (buf));
1822 if ((mret != CURLM_OK) && (mret != CURLM_CALL_MULTI_PERFORM))
1823 GNUNET_GE_LOG (coreAPI->ectx,
1824 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
1825 GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"),
1826 "curl_multi_perform", __FILE__, __LINE__,
1827 curl_multi_strerror (mret));
1828 if (mhd_daemon != NULL)
1829 MHD_run (mhd_daemon);
1830 cleanup_connections ();
1831 }
1832#if DEBUG_HTTP
1833 GNUNET_GE_LOG (coreAPI->ectx,
1834 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1835 "HTTP transport select thread exits.\n");
1836#endif
1837 return NULL;
1838}
1839
1840
1841/**
1842 * Start the server process to receive inbound traffic.
1843 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
1844 */
1845static int
1846startTransportServer ()
1847{
1848 unsigned short port;
1849
1850 if ((curl_multi != NULL) || (http_running == GNUNET_YES))
1851 return GNUNET_SYSERR;
1852 curl_multi = curl_multi_init ();
1853 if (curl_multi == NULL)
1854 return GNUNET_SYSERR;
1855 port = get_port ();
1856 if ((mhd_daemon == NULL) && (port != 0))
1857 {
1858 if (GNUNET_YES !=
1859 GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD",
1860 "DISABLE-IPV6",
1861 GNUNET_YES))
1862 {
1863 mhd_daemon = MHD_start_daemon (MHD_USE_IPv6,
1864 port,
1865 &acceptPolicyCallback,
1866 NULL, &accessHandlerCallback, NULL,
1867 MHD_OPTION_CONNECTION_TIMEOUT,
1868 (unsigned int) HTTP_TIMEOUT,
1869 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1870 (unsigned int) 1024 * 128,
1871 MHD_OPTION_CONNECTION_LIMIT,
1872 (unsigned int) 128,
1873 MHD_OPTION_PER_IP_CONNECTION_LIMIT,
1874 (unsigned int) 8,
1875 MHD_OPTION_NOTIFY_COMPLETED,
1876 &requestCompletedCallback, NULL,
1877 MHD_OPTION_END);
1878 }
1879 if (mhd_daemon == NULL)
1880 {
1881 /* try without IPv6 */
1882 mhd_daemon = MHD_start_daemon (MHD_NO_FLAG,
1883 port,
1884 &acceptPolicyCallback,
1885 NULL, &accessHandlerCallback, NULL,
1886 MHD_OPTION_CONNECTION_TIMEOUT,
1887 (unsigned int) HTTP_TIMEOUT,
1888 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1889 (unsigned int) 1024 * 128,
1890 MHD_OPTION_CONNECTION_LIMIT,
1891 (unsigned int) 128,
1892 MHD_OPTION_PER_IP_CONNECTION_LIMIT,
1893 (unsigned int) 8,
1894 MHD_OPTION_NOTIFY_COMPLETED,
1895 &requestCompletedCallback, NULL,
1896 MHD_OPTION_END);
1897 }
1898 else
1899 {
1900 available_protocols |= VERSION_AVAILABLE_IPV6;
1901 }
1902 if (mhd_daemon != NULL)
1903 available_protocols |= VERSION_AVAILABLE_IPV4;
1904 }
1905 if (port == 0)
1906 {
1907 /* NAT */
1908 available_protocols |= VERSION_AVAILABLE_IPV4;
1909 if (GNUNET_YES !=
1910 GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD",
1911 "DISABLE-IPV6",
1912 GNUNET_YES))
1913 available_protocols |= VERSION_AVAILABLE_IPV6;
1914 }
1915 if (0 != PIPE (signal_pipe))
1916 {
1917 MHD_stop_daemon (mhd_daemon);
1918 curl_multi_cleanup (curl_multi);
1919 curl_multi = NULL;
1920 mhd_daemon = NULL;
1921 return GNUNET_SYSERR;
1922 }
1923 GNUNET_pipe_make_nonblocking (coreAPI->ectx, signal_pipe[0]);
1924 GNUNET_pipe_make_nonblocking (coreAPI->ectx, signal_pipe[1]);
1925 http_running = GNUNET_YES;
1926 curl_thread = GNUNET_thread_create (&curl_runner, NULL, 32 * 1024);
1927 if (curl_thread == NULL)
1928 GNUNET_GE_DIE_STRERROR (coreAPI->ectx,
1929 GNUNET_GE_FATAL | GNUNET_GE_ADMIN |
1930 GNUNET_GE_IMMEDIATE, "pthread_create");
1931 return GNUNET_OK;
1932}
1933
1934/**
1935 * Shutdown the server process (stop receiving inbound
1936 * traffic). May be restarted later!
1937 */
1938static int
1939stopTransportServer ()
1940{
1941 void *unused;
1942 int i;
1943 HTTPSession *s;
1944
1945 if ((http_running == GNUNET_NO) || (curl_multi == NULL))
1946 return GNUNET_SYSERR;
1947 http_running = GNUNET_NO;
1948 signal_select ();
1949 GNUNET_thread_stop_sleep (curl_thread);
1950 GNUNET_thread_join (curl_thread, &unused);
1951 CLOSE (signal_pipe[0]);
1952 CLOSE (signal_pipe[1]);
1953 if (mhd_daemon != NULL)
1954 {
1955 MHD_stop_daemon (mhd_daemon);
1956 mhd_daemon = NULL;
1957 }
1958 cleanup_connections ();
1959 for (i = 0; i < tsessionCount; i++)
1960 {
1961 s = tsessions[i]->internal;
1962 if (s->users == 0)
1963 {
1964 destroy_tsession (tsessions[i]);
1965 i--;
1966 }
1967 }
1968 curl_multi_cleanup (curl_multi);
1969 curl_multi = NULL;
1970 return GNUNET_OK;
1971}
1972
1973/* ******************** public API ******************** */
1974
1975/**
1976 * The exported method. Makes the core api available
1977 * via a global and returns the udp transport API.
1978 */
1979GNUNET_TransportAPI *
1980inittransport_http (GNUNET_CoreAPIForTransport * core)
1981{
1982 GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (HostAddress) == 24);
1983 coreAPI = core;
1984 cfg = coreAPI->cfg;
1985 lock = GNUNET_mutex_create (GNUNET_YES);
1986 if (0 != GNUNET_GC_attach_change_listener (coreAPI->cfg,
1987 &reload_configuration, NULL))
1988 {
1989 GNUNET_mutex_destroy (lock);
1990 lock = NULL;
1991 return NULL;
1992 }
1993 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1994 {
1995 GNUNET_GE_BREAK (NULL, 0);
1996 GNUNET_GC_detach_change_listener (coreAPI->cfg, &reload_configuration,
1997 NULL);
1998 GNUNET_mutex_destroy (lock);
1999 lock = NULL;
2000 return NULL;
2001 }
2002 tsessionCount = 0;
2003 tsessionArrayLength = 0;
2004 GNUNET_array_grow (tsessions, tsessionArrayLength, 32);
2005 if (GNUNET_GC_get_configuration_value_yesno (coreAPI->cfg,
2006 "HTTP", "UPNP",
2007 GNUNET_YES) == GNUNET_YES)
2008 {
2009 upnp = coreAPI->service_request ("upnp");
2010
2011 if (upnp == NULL)
2012 {
2013 GNUNET_GE_LOG (coreAPI->ectx,
2014 GNUNET_GE_ERROR | GNUNET_GE_USER |
2015 GNUNET_GE_IMMEDIATE,
2016 _
2017 ("The UPnP service could not be loaded. To disable UPnP, set the "
2018 "configuration option \"UPNP\" in section \"%s\" to \"NO\"\n"),
2019 "HTTP");
2020 }
2021 }
2022 stats = coreAPI->service_request ("stats");
2023 if (stats != NULL)
2024 {
2025 stat_bytesReceived
2026 = stats->create (gettext_noop ("# bytes received via HTTP"));
2027 stat_bytesSent = stats->create (gettext_noop ("# bytes sent via HTTP"));
2028 stat_bytesDropped
2029 = stats->create (gettext_noop ("# bytes dropped by HTTP (outgoing)"));
2030 stat_get_issued = stats->create (gettext_noop ("# HTTP GET issued"));
2031 stat_get_received
2032 = stats->create (gettext_noop ("# HTTP GET received"));
2033 stat_put_issued = stats->create (gettext_noop ("# HTTP PUT issued"));
2034 stat_put_received
2035 = stats->create (gettext_noop ("# HTTP PUT received"));
2036 stat_select_calls
2037 = stats->create (gettext_noop ("# HTTP select calls"));
2038
2039 stat_send_calls = stats->create (gettext_noop ("# HTTP send calls"));
2040
2041 stat_curl_send_callbacks
2042 = stats->create (gettext_noop ("# HTTP curl send callbacks"));
2043 stat_curl_receive_callbacks
2044 = stats->create (gettext_noop ("# HTTP curl receive callbacks"));
2045 stat_mhd_access_callbacks
2046 = stats->create (gettext_noop ("# HTTP mhd access callbacks"));
2047 stat_mhd_read_callbacks
2048 = stats->create (gettext_noop ("# HTTP mhd read callbacks"));
2049 stat_mhd_close_callbacks
2050 = stats->create (gettext_noop ("# HTTP mhd close callbacks"));
2051 stat_connect_calls
2052 = stats->create (gettext_noop ("# HTTP connect calls"));
2053 }
2054 GNUNET_GC_get_configuration_value_string (coreAPI->cfg,
2055 "GNUNETD", "HTTP-PROXY", "",
2056 &proxy);
2057
2058 myAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_HTTP;
2059 myAPI.mtu = 0;
2060 myAPI.cost = 20000; /* about equal to udp */
2061 myAPI.hello_verify = &verify_hello;
2062 myAPI.hello_create = &create_hello;
2063 myAPI.connect = &httpConnect;
2064 myAPI.associate = &httpAssociate;
2065 myAPI.send = &httpSend;
2066 myAPI.disconnect = &httpDisconnect;
2067 myAPI.server_start = &startTransportServer;
2068 myAPI.server_stop = &stopTransportServer;
2069 myAPI.hello_to_address = &hello_to_address;
2070 myAPI.send_now_test = &httpTestWouldTry;
2071
2072 return &myAPI;
2073}
2074
2075void
2076donetransport_http ()
2077{
2078 curl_global_cleanup ();
2079 GNUNET_free_non_null (proxy);
2080 proxy = NULL;
2081 GNUNET_array_grow (tsessions, tsessionArrayLength, 0);
2082 do_shutdown ();
2083}
2084
2085/* end of http.c */
diff --git a/src/transport/plugin_transport_smtp.c b/src/transport/plugin_transport_smtp.c
new file mode 100644
index 000000000..f7cc530e4
--- /dev/null
+++ b/src/transport/plugin_transport_smtp.c
@@ -0,0 +1,906 @@
1/*
2 This file is part of GNUnet
3 (C) 2003, 2004, 2005, 2006, 2007 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file transports/smtp.c
23 * @brief Implementation of the SMTP transport service
24 * @author Christian Grothoff
25 * @author Renaldo Ferreira
26 */
27
28#include "platform.h"
29#include "gnunet_util.h"
30#include "gnunet_directories.h"
31#include "gnunet_protocols.h"
32#include "gnunet_transport.h"
33#include "gnunet_stats_service.h"
34#include <libesmtp.h>
35#include <signal.h>
36
37
38/**
39 * The default maximum size of each outbound SMTP message.
40 */
41#define SMTP_MESSAGE_SIZE 65528
42
43#define DEBUG_SMTP GNUNET_NO
44
45#define FILTER_STRING_SIZE 64
46
47/* how long can a line in base64 encoded
48 mime text be? (in characters, excluding "\n") */
49#define MAX_CHAR_PER_LINE 76
50
51#define EBUF_LEN 128
52
53/**
54 * Host-Address in a SMTP network.
55 */
56typedef struct
57{
58
59 /**
60 * Filter line that every sender must include in the E-mails such
61 * that the receiver can effectively filter out the GNUnet traffic
62 * from the E-mail.
63 */
64 char filter[FILTER_STRING_SIZE];
65
66 /**
67 * Claimed E-mail address of the sender.
68 * Format is "foo@bar.com" with null termination, padded to be
69 * of a multiple of 8 bytes long.
70 */
71 char senderAddress[0];
72
73} EmailAddress;
74
75/**
76 * Encapsulation of a GNUnet message in the SMTP mail body (before
77 * base64 encoding).
78 */
79typedef struct
80{
81 GNUNET_MessageHeader header;
82
83 /**
84 * What is the identity of the sender (GNUNET_hash of public key)
85 */
86 GNUNET_PeerIdentity sender;
87
88} SMTPMessage;
89
90/* *********** globals ************* */
91
92/**
93 * apis (our advertised API and the core api )
94 */
95static GNUNET_CoreAPIForTransport *coreAPI;
96
97static struct GNUNET_GE_Context *ectx;
98
99/**
100 * Thread that listens for inbound messages
101 */
102static struct GNUNET_ThreadHandle *dispatchThread;
103
104/**
105 * Flag to indicate that server has been shut down.
106 */
107static int smtp_shutdown = GNUNET_YES;
108
109/**
110 * Set to the SMTP server hostname (and port) for outgoing messages.
111 */
112static char *smtp_server_name;
113
114static char *pipename;
115
116/**
117 * Lock for uses of libesmtp (not thread-safe).
118 */
119static struct GNUNET_Mutex *lock;
120
121/**
122 * Old handler for SIGPIPE (kept to be able to restore).
123 */
124static struct sigaction old_handler;
125
126static char *email;
127
128static GNUNET_TransportAPI smtpAPI;
129
130static GNUNET_Stats_ServiceAPI *stats;
131
132static int stat_bytesReceived;
133
134static int stat_bytesSent;
135
136static int stat_bytesDropped;
137
138/**
139 * How many e-mails are we allowed to send per hour?
140 */
141static unsigned long long rate_limit;
142
143static GNUNET_CronTime last_transmission;
144
145/** ******************** Base64 encoding ***********/
146
147#define FILLCHAR '='
148static char *cvt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
149 "abcdefghijklmnopqrstuvwxyz" "0123456789+/";
150
151/**
152 * Encode into Base64.
153 *
154 * @param data the data to encode
155 * @param len the length of the input
156 * @param output where to write the output (*output should be NULL,
157 * is allocated)
158 * @return the size of the output
159 */
160static unsigned int
161base64_encode (const char *data, unsigned int len, char **output)
162{
163 unsigned int i;
164 char c;
165 unsigned int ret;
166 char *opt;
167
168/* (*output)[ret++] = '\r'; \*/
169#define CHECKLINE \
170 if ( (ret % MAX_CHAR_PER_LINE) == 0) { \
171 (*output)[ret++] = '\n'; \
172 }
173 ret = 0;
174 opt = GNUNET_malloc (2 + (((len * 4 / 3) + 8) * (MAX_CHAR_PER_LINE + 2)) /
175 MAX_CHAR_PER_LINE);
176 /* message must start with \r\n for libesmtp */
177 *output = opt;
178 opt[0] = '\r';
179 opt[1] = '\n';
180 ret += 2;
181 for (i = 0; i < len; ++i)
182 {
183 c = (data[i] >> 2) & 0x3f;
184 opt[ret++] = cvt[(int) c];
185 CHECKLINE;
186 c = (data[i] << 4) & 0x3f;
187 if (++i < len)
188 c |= (data[i] >> 4) & 0x0f;
189 opt[ret++] = cvt[(int) c];
190 CHECKLINE;
191 if (i < len)
192 {
193 c = (data[i] << 2) & 0x3f;
194 if (++i < len)
195 c |= (data[i] >> 6) & 0x03;
196 opt[ret++] = cvt[(int) c];
197 CHECKLINE;
198 }
199 else
200 {
201 ++i;
202 opt[ret++] = FILLCHAR;
203 CHECKLINE;
204 }
205 if (i < len)
206 {
207 c = data[i] & 0x3f;
208 opt[ret++] = cvt[(int) c];
209 CHECKLINE;
210 }
211 else
212 {
213 opt[ret++] = FILLCHAR;
214 CHECKLINE;
215 }
216 }
217 opt[ret++] = FILLCHAR;
218 return ret;
219}
220
221#define cvtfind(a)( (((a) >= 'A')&&((a) <= 'Z'))? (a)-'A'\
222 :(((a)>='a')&&((a)<='z')) ? (a)-'a'+26\
223 :(((a)>='0')&&((a)<='9')) ? (a)-'0'+52\
224 :((a) == '+') ? 62\
225 :((a) == '/') ? 63 : -1)
226/**
227 * Decode from Base64.
228 *
229 * @param data the data to encode
230 * @param len the length of the input
231 * @param output where to write the output (*output should be NULL,
232 * is allocated)
233 * @return the size of the output
234 */
235static unsigned int
236base64_decode (const char *data, unsigned int len, char **output)
237{
238 unsigned int i;
239 char c;
240 char c1;
241 unsigned int ret = 0;
242
243#define CHECK_CRLF while (data[i] == '\r' || data[i] == '\n') {\
244 GNUNET_GE_LOG(ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, "ignoring CR/LF\n"); \
245 i++; \
246 if (i >= len) goto END; \
247 }
248
249 *output = GNUNET_malloc ((len * 3 / 4) + 8);
250#if DEBUG_SMTP
251 GNUNET_GE_LOG (ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
252 "base64_decode decoding len=%d\n", len);
253#endif
254 for (i = 0; i < len; ++i)
255 {
256 CHECK_CRLF;
257 if (data[i] == FILLCHAR)
258 break;
259 c = (char) cvtfind (data[i]);
260 ++i;
261 CHECK_CRLF;
262 c1 = (char) cvtfind (data[i]);
263 c = (c << 2) | ((c1 >> 4) & 0x3);
264 (*output)[ret++] = c;
265 if (++i < len)
266 {
267 CHECK_CRLF;
268 c = data[i];
269 if (FILLCHAR == c)
270 break;
271 c = (char) cvtfind (c);
272 c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
273 (*output)[ret++] = c1;
274 }
275 if (++i < len)
276 {
277 CHECK_CRLF;
278 c1 = data[i];
279 if (FILLCHAR == c1)
280 break;
281
282 c1 = (char) cvtfind (c1);
283 c = ((c << 6) & 0xc0) | c1;
284 (*output)[ret++] = c;
285 }
286 }
287END:
288 return ret;
289}
290
291/* ********************* the real stuff ******************* */
292
293#define strAUTOncmp(a,b) strncmp(a,b,strlen(b))
294
295/**
296 * Listen to the pipe, decode messages and send to core.
297 */
298static void *
299listenAndDistribute (void *unused)
300{
301 char *line;
302 unsigned int linesize;
303 SMTPMessage *mp;
304 FILE *fdes;
305 char *retl;
306 char *out;
307 unsigned int size;
308 GNUNET_TransportPacket *coreMP;
309 int fd;
310 unsigned int pos;
311
312 linesize = ((GNUNET_MAX_BUFFER_SIZE * 4 / 3) + 8) * (MAX_CHAR_PER_LINE + 2) / MAX_CHAR_PER_LINE; /* maximum size of a line supported */
313 line = GNUNET_malloc (linesize + 2); /* 2 bytes for off-by-one errors, just to be safe... */
314
315#define READLINE(l,limit) \
316 do { retl = fgets(l, (limit), fdes); \
317 if ( (retl == NULL) || (smtp_shutdown == GNUNET_YES)) {\
318 goto END; \
319 }\
320 if (coreAPI->load_monitor != NULL) \
321 GNUNET_network_monitor_notify_transmission(coreAPI->load_monitor, GNUNET_ND_DOWNLOAD, strlen(retl)); \
322 } while (0)
323
324
325 while (smtp_shutdown == GNUNET_NO)
326 {
327 fd = OPEN (pipename, O_RDONLY | O_ASYNC);
328 if (fd == -1)
329 {
330 if (smtp_shutdown == GNUNET_NO)
331 GNUNET_thread_sleep (5 * GNUNET_CRON_SECONDS);
332 continue;
333 }
334 fdes = fdopen (fd, "r");
335 while (smtp_shutdown == GNUNET_NO)
336 {
337 /* skip until end of header */
338 do
339 {
340 READLINE (line, linesize);
341 }
342 while ((line[0] != '\r') && (line[0] != '\n')); /* expect newline */
343 READLINE (line, linesize); /* read base64 encoded message; decode, process */
344 pos = 0;
345 while (1)
346 {
347 pos = strlen (line) - 1; /* ignore new line */
348 READLINE (&line[pos], linesize - pos); /* read base64 encoded message; decode, process */
349 if ((line[pos] == '\r') || (line[pos] == '\n'))
350 break; /* empty line => end of message! */
351 }
352 size = base64_decode (line, pos, &out);
353 if (size < sizeof (SMTPMessage))
354 {
355 GNUNET_GE_BREAK (ectx, 0);
356 GNUNET_free (out);
357 goto END;
358 }
359
360 mp = (SMTPMessage *) & out[size - sizeof (SMTPMessage)];
361 if (ntohs (mp->header.size) != size)
362 {
363 GNUNET_GE_LOG (ectx,
364 GNUNET_GE_WARNING | GNUNET_GE_BULK |
365 GNUNET_GE_USER,
366 _
367 ("Received malformed message via %s. Ignored.\n"),
368 "SMTP");
369#if DEBUG_SMTP
370 GNUNET_GE_LOG (ectx,
371 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
372 GNUNET_GE_USER,
373 "Size returned by base64=%d, in the msg=%d.\n",
374 size, ntohl (mp->size));
375#endif
376 GNUNET_free (out);
377 goto END;
378 }
379 if (stats != NULL)
380 stats->change (stat_bytesReceived, size);
381 coreMP = GNUNET_malloc (sizeof (GNUNET_TransportPacket));
382 coreMP->msg = out;
383 coreMP->size = size - sizeof (SMTPMessage);
384 coreMP->tsession = NULL;
385 coreMP->sender = mp->sender;
386#if DEBUG_SMTP
387 GNUNET_GE_LOG (ectx,
388 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
389 "SMTP message passed to the core.\n");
390#endif
391
392 coreAPI->receive (coreMP);
393 }
394 END:
395#if DEBUG_SMTP
396 GNUNET_GE_LOG (ectx,
397 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
398 "SMTP message processed.\n");
399#endif
400 if (fdes != NULL)
401 fclose (fdes);
402 }
403 GNUNET_free (line);
404 return NULL;
405}
406
407/* *************** API implementation *************** */
408
409/**
410 * Verify that a hello-Message is correct (a node is reachable at that
411 * address). Since the reply will be asynchronous, a method must be
412 * called on success.
413 *
414 * @param hello the hello message to verify
415 * (the signature/crc have been verified before)
416 * @return GNUNET_OK on success, GNUNET_SYSERR on error
417 */
418static int
419api_verify_hello (const GNUNET_MessageHello * hello)
420{
421 const EmailAddress *maddr;
422
423 maddr = (const EmailAddress *) &hello[1];
424 if ((ntohs (hello->header.size) !=
425 sizeof (GNUNET_MessageHello) + ntohs (hello->senderAddressSize)) ||
426 (maddr->senderAddress[ntohs (hello->senderAddressSize) - 1 -
427 FILTER_STRING_SIZE] != '\0'))
428 {
429 GNUNET_GE_BREAK (ectx, 0);
430 return GNUNET_SYSERR; /* obviously invalid */
431 }
432 if (NULL == strstr (maddr->filter, ": "))
433 return GNUNET_SYSERR;
434 return GNUNET_OK;
435}
436
437/**
438 * Create a hello-Message for the current node. The hello is created
439 * without signature and without a timestamp. The GNUnet core will
440 * GNUNET_RSA_sign the message and add an expiration time.
441 *
442 * @return hello on success, NULL on error
443 */
444static GNUNET_MessageHello *
445api_create_hello ()
446{
447 GNUNET_MessageHello *msg;
448 char *filter;
449 EmailAddress *haddr;
450 int i;
451
452 GNUNET_GC_get_configuration_value_string (coreAPI->cfg,
453 "SMTP", "FILTER",
454 "X-mailer: GNUnet", &filter);
455 if (NULL == strstr (filter, ": "))
456 {
457 GNUNET_GE_LOG (ectx,
458 GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_USER,
459 _("SMTP filter string to invalid, lacks ': '\n"));
460 GNUNET_free (filter);
461 return NULL;
462 }
463
464 if (strlen (filter) > FILTER_STRING_SIZE)
465 {
466 filter[FILTER_STRING_SIZE] = '\0';
467 GNUNET_GE_LOG (ectx,
468 GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_USER,
469 _("SMTP filter string to long, capped to `%s'\n"),
470 filter);
471 }
472 i = (strlen (email) + 8) & (~7); /* make multiple of 8 */
473 msg =
474 GNUNET_malloc (sizeof (GNUNET_MessageHello) + sizeof (EmailAddress) + i);
475 memset (msg, 0, sizeof (GNUNET_MessageHello) + sizeof (EmailAddress) + i);
476 haddr = (EmailAddress *) & msg[1];
477 memset (&haddr->filter[0], 0, FILTER_STRING_SIZE);
478 strcpy (&haddr->filter[0], filter);
479 memcpy (&haddr->senderAddress[0], email, strlen (email) + 1);
480 msg->senderAddressSize = htons (strlen (email) + 1 + sizeof (EmailAddress));
481 msg->protocol = htons (GNUNET_TRANSPORT_PROTOCOL_NUMBER_SMTP);
482 msg->MTU = htonl (smtpAPI.mtu);
483 msg->header.size = htons (GNUNET_sizeof_hello (msg));
484 if (api_verify_hello (msg) == GNUNET_SYSERR)
485 GNUNET_GE_ASSERT (ectx, 0);
486 GNUNET_free (filter);
487 return msg;
488}
489
490struct GetMessageClosure
491{
492 unsigned int esize;
493 unsigned int pos;
494 char *ebody;
495};
496
497static const char *
498get_message (void **buf, int *len, void *cls)
499{
500 struct GetMessageClosure *gmc = cls;
501
502 *buf = NULL;
503 if (len == NULL)
504 {
505 gmc->pos = 0;
506 return NULL;
507 }
508 if (gmc->pos == gmc->esize)
509 return NULL; /* done */
510 *len = gmc->esize;
511 gmc->pos = gmc->esize;
512 return gmc->ebody;
513}
514
515/**
516 * Send a message to the specified remote node.
517 *
518 * @param tsession the GNUNET_MessageHello identifying the remote node
519 * @param message what to send
520 * @param size the size of the message
521 * @return GNUNET_SYSERR on error, GNUNET_OK on success
522 */
523static int
524api_send (GNUNET_TSession * tsession,
525 const void *msg, const unsigned int size, int important)
526{
527 const GNUNET_MessageHello *hello;
528 const EmailAddress *haddr;
529 char *m;
530 char *filter;
531 char *fvalue;
532 SMTPMessage *mp;
533 struct GetMessageClosure gm_cls;
534 smtp_session_t session;
535 smtp_message_t message;
536 smtp_recipient_t recipient;
537#define EBUF_LEN 128
538 char ebuf[EBUF_LEN];
539 GNUNET_CronTime now;
540
541 if (smtp_shutdown == GNUNET_YES)
542 return GNUNET_SYSERR;
543 if ((size == 0) || (size > smtpAPI.mtu))
544 {
545 GNUNET_GE_BREAK (ectx, 0);
546 return GNUNET_SYSERR;
547 }
548 now = GNUNET_get_time ();
549 if ((important != GNUNET_YES) &&
550 ((now - last_transmission) * rate_limit) < GNUNET_CRON_HOURS)
551 return GNUNET_NO; /* rate too high */
552 last_transmission = now;
553
554 hello = (const GNUNET_MessageHello *) tsession->internal;
555 if (hello == NULL)
556 return GNUNET_SYSERR;
557 GNUNET_mutex_lock (lock);
558 session = smtp_create_session ();
559 if (session == NULL)
560 {
561 GNUNET_GE_LOG (ectx,
562 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
563 GNUNET_GE_IMMEDIATE,
564 _("SMTP: `%s' failed: %s.\n"),
565 "smtp_create_session",
566 smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
567 GNUNET_mutex_unlock (lock);
568 return GNUNET_SYSERR;
569 }
570 if (0 == smtp_set_server (session, smtp_server_name))
571 {
572 GNUNET_GE_LOG (ectx,
573 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
574 GNUNET_GE_IMMEDIATE,
575 _("SMTP: `%s' failed: %s.\n"),
576 "smtp_set_server",
577 smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
578 smtp_destroy_session (session);
579 GNUNET_mutex_unlock (lock);
580 return GNUNET_SYSERR;
581 }
582 haddr = (const EmailAddress *) &hello[1];
583 message = smtp_add_message (session);
584 if (message == NULL)
585 {
586 GNUNET_GE_LOG (ectx,
587 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
588 GNUNET_GE_BULK,
589 _("SMTP: `%s' failed: %s.\n"),
590 "smtp_add_message",
591 smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
592 smtp_destroy_session (session);
593 GNUNET_mutex_unlock (lock);
594 return GNUNET_SYSERR;
595 }
596 smtp_set_header (message, "To", NULL, haddr->senderAddress);
597 smtp_set_header (message, "From", NULL, email);
598
599 filter = GNUNET_strdup (haddr->filter);
600 fvalue = strstr (filter, ": ");
601 GNUNET_GE_ASSERT (NULL, NULL != fvalue);
602 fvalue[0] = '\0';
603 fvalue += 2;
604 if (0 == smtp_set_header (message, filter, fvalue))
605 {
606 GNUNET_GE_LOG (ectx,
607 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
608 GNUNET_GE_BULK,
609 _("SMTP: `%s' failed: %s.\n"),
610 "smtp_set_header",
611 smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
612 smtp_destroy_session (session);
613 GNUNET_mutex_unlock (lock);
614 GNUNET_free (filter);
615 return GNUNET_SYSERR;
616 }
617 GNUNET_free (filter);
618 m = GNUNET_malloc (size + sizeof (SMTPMessage));
619 memcpy (m, msg, size);
620 mp = (SMTPMessage *) & m[size];
621 mp->header.size = htons (size + sizeof (SMTPMessage));
622 mp->header.type = htons (0);
623 mp->sender = *coreAPI->my_identity;
624 gm_cls.ebody = NULL;
625 gm_cls.pos = 0;
626 gm_cls.esize =
627 base64_encode (m, size + sizeof (SMTPMessage), &gm_cls.ebody);
628 GNUNET_free (m);
629 if (0 == smtp_size_set_estimate (message, gm_cls.esize))
630 {
631 GNUNET_GE_LOG (ectx,
632 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
633 GNUNET_GE_BULK,
634 _("SMTP: `%s' failed: %s.\n"),
635 "smtp_size_set_estimate",
636 smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
637 }
638 if (0 == smtp_set_messagecb (message, &get_message, &gm_cls))
639 {
640 GNUNET_GE_LOG (ectx,
641 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
642 GNUNET_GE_BULK,
643 _("SMTP: `%s' failed: %s.\n"),
644 "smtp_set_messagecb",
645 smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
646 smtp_destroy_session (session);
647 GNUNET_mutex_unlock (lock);
648 GNUNET_free (gm_cls.ebody);
649 return GNUNET_SYSERR;
650 }
651 recipient = smtp_add_recipient (message, haddr->senderAddress);
652 if (recipient == NULL)
653 {
654 GNUNET_GE_LOG (ectx,
655 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
656 GNUNET_GE_BULK,
657 _("SMTP: `%s' failed: %s.\n"),
658 "smtp_add_recipient",
659 smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
660 smtp_destroy_session (session);
661 GNUNET_mutex_unlock (lock);
662 return GNUNET_SYSERR;
663 }
664 if (0 == smtp_start_session (session))
665 {
666 GNUNET_GE_LOG (ectx,
667 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
668 GNUNET_GE_BULK,
669 _("SMTP: `%s' failed: %s.\n"),
670 "smtp_start_session",
671 smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
672 smtp_destroy_session (session);
673 GNUNET_mutex_unlock (lock);
674 GNUNET_free (gm_cls.ebody);
675 return GNUNET_SYSERR;
676 }
677 if (stats != NULL)
678 stats->change (stat_bytesSent, size);
679 if (coreAPI->load_monitor != NULL)
680 GNUNET_network_monitor_notify_transmission (coreAPI->load_monitor,
681 GNUNET_ND_UPLOAD,
682 gm_cls.esize);
683 smtp_message_reset_status (message); /* this is needed to plug a 28-byte/message memory leak in libesmtp */
684 smtp_destroy_session (session);
685 GNUNET_mutex_unlock (lock);
686 GNUNET_free (gm_cls.ebody);
687 return GNUNET_OK;
688}
689
690/**
691 * Establish a connection to a remote node.
692 * @param helo the hello-Message for the target node
693 * @param tsessionPtr the session handle that is to be set
694 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
695 */
696static int
697api_connect (const GNUNET_MessageHello * hello,
698 GNUNET_TSession ** tsessionPtr, int may_reuse)
699{
700 GNUNET_TSession *tsession;
701
702 tsession = GNUNET_malloc (sizeof (GNUNET_TSession));
703 tsession->internal = GNUNET_malloc (GNUNET_sizeof_hello (hello));
704 tsession->peer = hello->senderIdentity;
705 memcpy (tsession->internal, hello, GNUNET_sizeof_hello (hello));
706 tsession->ttype = smtpAPI.protocol_number;
707 (*tsessionPtr) = tsession;
708 return GNUNET_OK;
709}
710
711/**
712 * Disconnect from a remote node.
713 *
714 * @param tsession the session that is closed
715 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
716 */
717static int
718api_disconnect (GNUNET_TSession * tsession)
719{
720 if (tsession != NULL)
721 {
722 if (tsession->internal != NULL)
723 GNUNET_free (tsession->internal);
724 GNUNET_free (tsession);
725 }
726 return GNUNET_OK;
727}
728
729/**
730 * Start the server process to receive inbound traffic.
731 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
732 */
733static int
734api_start_transport_server ()
735{
736 smtp_shutdown = GNUNET_NO;
737 /* initialize SMTP network */
738 dispatchThread =
739 GNUNET_thread_create (&listenAndDistribute, NULL, 1024 * 4);
740 if (dispatchThread == NULL)
741 {
742 GNUNET_GE_DIE_STRERROR (ectx,
743 GNUNET_GE_ADMIN | GNUNET_GE_BULK |
744 GNUNET_GE_FATAL, "pthread_create");
745 return GNUNET_SYSERR;
746 }
747 return GNUNET_OK;
748}
749
750/**
751 * Shutdown the server process (stop receiving inbound traffic). Maybe
752 * restarted later!
753 */
754static int
755api_stop_transport_server ()
756{
757 void *unused;
758
759 smtp_shutdown = GNUNET_YES;
760 GNUNET_thread_stop_sleep (dispatchThread);
761 GNUNET_thread_join (dispatchThread, &unused);
762 return GNUNET_OK;
763}
764
765/**
766 * Convert SMTP hello to an IP address (always fails).
767 */
768static int
769api_hello_to_address (const GNUNET_MessageHello * hello,
770 void **sa, unsigned int *sa_len)
771{
772 return GNUNET_SYSERR;
773}
774
775/**
776 * Always fails.
777 */
778static int
779api_associate (GNUNET_TSession * tsession)
780{
781 return GNUNET_SYSERR; /* SMTP connections can never be associated */
782}
783
784/**
785 * Always succeeds (for now; we should look at adding
786 * frequency limits to SMTP in the future!).
787 */
788static int
789api_test_would_try (GNUNET_TSession * tsession, const unsigned int size,
790 int important)
791{
792 return GNUNET_OK; /* we always try... */
793}
794
795/**
796 * The exported method. Makes the core api available via a global and
797 * returns the smtp transport API.
798 */
799GNUNET_TransportAPI *
800inittransport_smtp (GNUNET_CoreAPIForTransport * core)
801{
802
803
804 unsigned long long mtu;
805 struct sigaction sa;
806
807 coreAPI = core;
808 ectx = core->ectx;
809 if (!GNUNET_GC_have_configuration_value (coreAPI->cfg, "SMTP", "EMAIL"))
810 {
811 GNUNET_GE_LOG (ectx,
812 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
813 _
814 ("No email-address specified, can not start SMTP transport.\n"));
815 return NULL;
816 }
817 GNUNET_GC_get_configuration_value_number (coreAPI->cfg,
818 "SMTP",
819 "MTU",
820 1200,
821 SMTP_MESSAGE_SIZE,
822 SMTP_MESSAGE_SIZE, &mtu);
823 GNUNET_GC_get_configuration_value_number (coreAPI->cfg,
824 "SMTP",
825 "RATELIMIT",
826 0, 0, 1024 * 1024, &rate_limit);
827 stats = coreAPI->service_request ("stats");
828 if (stats != NULL)
829 {
830 stat_bytesReceived
831 = stats->create (gettext_noop ("# bytes received via SMTP"));
832 stat_bytesSent = stats->create (gettext_noop ("# bytes sent via SMTP"));
833 stat_bytesDropped
834 = stats->create (gettext_noop ("# bytes dropped by SMTP (outgoing)"));
835 }
836 GNUNET_GC_get_configuration_value_filename (coreAPI->cfg,
837 "SMTP",
838 "PIPE",
839 GNUNET_DEFAULT_DAEMON_VAR_DIRECTORY
840 "/smtp-pipe", &pipename);
841 UNLINK (pipename);
842 if (0 != mkfifo (pipename, S_IWUSR | S_IRUSR | S_IWGRP | S_IWOTH))
843 {
844 GNUNET_GE_LOG_STRERROR (ectx,
845 GNUNET_GE_ADMIN | GNUNET_GE_BULK |
846 GNUNET_GE_FATAL, "mkfifo");
847 GNUNET_free (pipename);
848 coreAPI->service_release (stats);
849 stats = NULL;
850 return NULL;
851 }
852 /* we need to allow the mailer program to send us messages;
853 easiest done by giving it write permissions (see Mantis #1142) */
854 if (0 != chmod (pipename, S_IWUSR | S_IRUSR | S_IWGRP | S_IWOTH))
855 GNUNET_GE_LOG_STRERROR (ectx,
856 GNUNET_GE_ADMIN | GNUNET_GE_BULK |
857 GNUNET_GE_WARNING, "chmod");
858 GNUNET_GC_get_configuration_value_string (coreAPI->cfg,
859 "SMTP", "EMAIL", NULL, &email);
860 lock = GNUNET_mutex_create (GNUNET_NO);
861 GNUNET_GC_get_configuration_value_string (coreAPI->cfg,
862 "SMTP",
863 "SERVER",
864 "localhost:25",
865 &smtp_server_name);
866 sa.sa_handler = SIG_IGN;
867 sigemptyset (&sa.sa_mask);
868 sa.sa_flags = 0;
869 sigaction (SIGPIPE, &sa, &old_handler);
870
871 smtpAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_SMTP;
872 smtpAPI.mtu = mtu - sizeof (SMTPMessage);
873 smtpAPI.cost = 50;
874 smtpAPI.hello_verify = &api_verify_hello;
875 smtpAPI.hello_create = &api_create_hello;
876 smtpAPI.connect = &api_connect;
877 smtpAPI.send = &api_send;
878 smtpAPI.associate = &api_associate;
879 smtpAPI.disconnect = &api_disconnect;
880 smtpAPI.server_start = &api_start_transport_server;
881 smtpAPI.server_stop = &api_stop_transport_server;
882 smtpAPI.hello_to_address = &api_hello_to_address;
883 smtpAPI.send_now_test = &api_test_would_try;
884 return &smtpAPI;
885}
886
887void
888donetransport_smtp ()
889{
890 sigaction (SIGPIPE, &old_handler, NULL);
891 GNUNET_free (smtp_server_name);
892 if (stats != NULL)
893 {
894 coreAPI->service_release (stats);
895 stats = NULL;
896 }
897 GNUNET_mutex_destroy (lock);
898 lock = NULL;
899 UNLINK (pipename);
900 GNUNET_free (pipename);
901 pipename = NULL;
902 GNUNET_free (email);
903 email = NULL;
904}
905
906/* end of smtp.c */
diff --git a/src/transport/plugin_transport_tcp.c b/src/transport/plugin_transport_tcp.c
new file mode 100644
index 000000000..c87056e71
--- /dev/null
+++ b/src/transport/plugin_transport_tcp.c
@@ -0,0 +1,1782 @@
1/*
2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file transport/plugin_transport_tcp.c
23 * @brief Implementation of the TCP transport service
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_hello_lib.h"
29#include "gnunet_network_lib.h"
30#include "gnunet_os_lib.h"
31#include "gnunet_peerinfo_service.h"
32#include "gnunet_protocols.h"
33#include "gnunet_resolver_service.h"
34#include "gnunet_server_lib.h"
35#include "gnunet_service_lib.h"
36#include "gnunet_statistics_service.h"
37#include "gnunet_transport_service.h"
38#include "plugin_transport.h"
39#include "transport.h"
40
41#define DEBUG_TCP GNUNET_NO
42
43/**
44 * After how long do we expire an address that we
45 * learned from another peer if it is not reconfirmed
46 * by anyone?
47 */
48#define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
49
50/**
51 * How long until we give up on transmitting the welcome message?
52 */
53#define WELCOME_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
54
55/**
56 * How long until we give up on transmitting the welcome message?
57 */
58#define HOSTNAME_RESOLVE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
59
60/**
61 * For how many messages back to we keep transmission times?
62 */
63#define ACK_LOG_SIZE 32
64
65/**
66 * Initial handshake message for a session. This header
67 * is followed by the address that the other peer used to
68 * connect to us (so that we may learn it) or the address
69 * that the other peer got from the accept call.
70 */
71struct WelcomeMessage
72{
73 struct GNUNET_MessageHeader header;
74
75 /**
76 * Identity of the node connecting (TCP client)
77 */
78 struct GNUNET_PeerIdentity clientIdentity;
79
80};
81
82
83/**
84 * Encapsulation for normal TCP traffic.
85 */
86struct DataMessage
87{
88 struct GNUNET_MessageHeader header;
89
90 /**
91 * For alignment.
92 */
93 uint32_t reserved GNUNET_PACKED;
94
95 /**
96 * Number of the last message that was received from the other peer.
97 */
98 uint64_t ack_in GNUNET_PACKED;
99
100 /**
101 * Number of this outgoing message.
102 */
103 uint64_t ack_out GNUNET_PACKED;
104
105 /**
106 * How long was sending this ack delayed by the other peer
107 * (estimate). The receiver of this message can use the delay
108 * between sending his message number 'ack' and receiving this ack
109 * minus the delay as an estimate of the round-trip time.
110 */
111 struct GNUNET_TIME_RelativeNBO delay;
112
113};
114
115
116/**
117 * Encapsulation of all of the state of the plugin.
118 */
119struct Plugin;
120
121
122/**
123 * Information kept for each message that is yet to
124 * be transmitted.
125 */
126struct PendingMessage
127{
128
129 /**
130 * This is a linked list.
131 */
132 struct PendingMessage *next;
133
134 /**
135 * The pending message, pointer to the end
136 * of this struct, do not free!
137 */
138 struct GNUNET_MessageHeader *msg;
139
140
141 /**
142 * Continuation function to call once the message
143 * has been sent. Can be NULL if there is no
144 * continuation to call.
145 */
146 GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
147
148 /**
149 * Closure for transmit_cont.
150 */
151 void *transmit_cont_cls;
152
153 /**
154 * Timeout value for the pending message.
155 */
156 struct GNUNET_TIME_Absolute timeout;
157
158 /**
159 * GNUNET_YES if this is a welcome message;
160 * otherwise this should be a DATA message.
161 */
162 int is_welcome;
163
164};
165
166
167/**
168 * Session handle for TCP connections.
169 */
170struct Session
171{
172
173 /**
174 * Stored in a linked list.
175 */
176 struct Session *next;
177
178 /**
179 * Pointer to the global plugin struct.
180 */
181 struct Plugin *plugin;
182
183 /**
184 * The client (used to identify this connection)
185 */
186 struct GNUNET_SERVER_Client *client;
187
188 /**
189 * gnunet-service-transport context for this connection.
190 */
191 struct ReadyList *service_context;
192
193 /**
194 * Messages currently pending for transmission
195 * to this peer, if any.
196 */
197 struct PendingMessage *pending_messages;
198
199 /**
200 * Handle for pending transmission request.
201 */
202 struct GNUNET_NETWORK_TransmitHandle *transmit_handle;
203
204 /**
205 * To whom are we talking to (set to our identity
206 * if we are still waiting for the welcome message)
207 */
208 struct GNUNET_PeerIdentity target;
209
210 /**
211 * At what time did we reset last_received last?
212 */
213 struct GNUNET_TIME_Absolute last_quota_update;
214
215 /**
216 * Address of the other peer if WE initiated the connection
217 * (and hence can be sure what it is), otherwise NULL.
218 */
219 void *connect_addr;
220
221 /**
222 * How many bytes have we received since the "last_quota_update"
223 * timestamp?
224 */
225 uint64_t last_received;
226
227 /**
228 * Our current latency estimate (in ms).
229 */
230 double latency_estimate;
231
232 /**
233 * Time when we generated the last ACK_LOG_SIZE acks.
234 * (the "last" refers to the "out_msg_counter" here)
235 */
236 struct GNUNET_TIME_Absolute gen_time[ACK_LOG_SIZE];
237
238 /**
239 * Our current sequence number.
240 */
241 uint64_t out_msg_counter;
242
243 /**
244 * Highest received incoming sequence number.
245 */
246 uint64_t max_in_msg_counter;
247
248 /**
249 * Number of bytes per ms that this peer is allowed
250 * to send to us.
251 */
252 uint32_t quota_in;
253
254 /**
255 * Length of connect_addr, can be 0.
256 */
257 size_t connect_alen;
258
259 /**
260 * Are we still expecting the welcome message? (GNUNET_YES/GNUNET_NO)
261 */
262 int expecting_welcome;
263
264 /**
265 * Are we still trying to connect?
266 */
267 int still_connecting;
268
269};
270
271
272/**
273 * Encapsulation of all of the state of the plugin.
274 */
275struct Plugin
276{
277 /**
278 * Our environment.
279 */
280 struct GNUNET_TRANSPORT_PluginEnvironment *env;
281
282 /**
283 * The listen socket.
284 */
285 struct GNUNET_NETWORK_SocketHandle *lsock;
286
287 /**
288 * List of open TCP sessions.
289 */
290 struct Session *sessions;
291
292 /**
293 * Handle for the statistics service.
294 */
295 struct GNUNET_STATISTICS_Handle *statistics;
296
297 /**
298 * Handle to the network service.
299 */
300 struct GNUNET_SERVICE_Context *service;
301
302 /**
303 * Handle to the server for this service.
304 */
305 struct GNUNET_SERVER_Handle *server;
306
307 /**
308 * Copy of the handler array where the closures are
309 * set to this struct's instance.
310 */
311 struct GNUNET_SERVER_MessageHandler *handlers;
312
313 /**
314 * ID of task used to update our addresses when one expires.
315 */
316 GNUNET_SCHEDULER_TaskIdentifier address_update_task;
317
318 /**
319 * Port that we are actually listening on.
320 */
321 uint16_t open_port;
322
323 /**
324 * Port that the user said we would have visible to the
325 * rest of the world.
326 */
327 uint16_t adv_port;
328
329};
330
331
332/**
333 * Find the session handle for the given peer.
334 */
335static struct Session *
336find_session_by_target (struct Plugin *plugin,
337 const struct GNUNET_PeerIdentity *target)
338{
339 struct Session *ret;
340
341 ret = plugin->sessions;
342 while ((ret != NULL) &&
343 (0 != memcmp (target,
344 &ret->target, sizeof (struct GNUNET_PeerIdentity))))
345 ret = ret->next;
346 return ret;
347}
348
349
350/**
351 * Find the session handle for the given peer.
352 */
353static struct Session *
354find_session_by_client (struct Plugin *plugin,
355 const struct GNUNET_SERVER_Client *client)
356{
357 struct Session *ret;
358
359 ret = plugin->sessions;
360 while ((ret != NULL) && (client != ret->client))
361 ret = ret->next;
362 return ret;
363}
364
365
366/**
367 * Create a welcome message.
368 */
369static struct PendingMessage *
370create_welcome (size_t addrlen, const void *addr, struct Plugin *plugin)
371{
372 struct PendingMessage *pm;
373 struct WelcomeMessage *welcome;
374
375 pm = GNUNET_malloc (sizeof (struct PendingMessage) +
376 sizeof (struct WelcomeMessage) + addrlen);
377 pm->msg = (struct GNUNET_MessageHeader *) &pm[1];
378 welcome = (struct WelcomeMessage *) &pm[1];
379 welcome->header.size = htons (sizeof (struct WelcomeMessage) + addrlen);
380 welcome->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME);
381 GNUNET_CRYPTO_hash (plugin->env->my_public_key,
382 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
383 &welcome->clientIdentity.hashPubKey);
384 memcpy (&welcome[1], addr, addrlen);
385 pm->timeout = GNUNET_TIME_relative_to_absolute (WELCOME_TIMEOUT);
386 pm->is_welcome = GNUNET_YES;
387 return pm;
388}
389
390
391/**
392 * Create a new session using the specified address
393 * for the welcome message.
394 *
395 * @param plugin us
396 * @param target peer to connect to
397 * @param client client to use
398 * @param addrlen IPv4 or IPv6
399 * @param addr either struct sockaddr_in or struct sockaddr_in6
400 * @return NULL connection failed / invalid address
401 */
402static struct Session *
403create_session (struct Plugin *plugin,
404 const struct GNUNET_PeerIdentity *target,
405 struct GNUNET_SERVER_Client *client,
406 const void *addr, size_t addrlen)
407{
408 struct Session *ret;
409
410 ret = GNUNET_malloc (sizeof (struct Session));
411 ret->plugin = plugin;
412 ret->next = plugin->sessions;
413 plugin->sessions = ret;
414 ret->client = client;
415 ret->target = *target;
416 ret->last_quota_update = GNUNET_TIME_absolute_get ();
417 ret->quota_in = plugin->env->default_quota_in;
418 ret->expecting_welcome = GNUNET_YES;
419 ret->pending_messages = create_welcome (addrlen, addr, plugin);
420 return ret;
421}
422
423
424/**
425 * Create a new session connecting to the specified
426 * target at the specified address.
427 *
428 * @param plugin us
429 * @param target peer to connect to
430 * @param addrlen IPv4 or IPv6
431 * @param addr either struct sockaddr_in or struct sockaddr_in6
432 * @return NULL connection failed / invalid address
433 */
434static struct Session *
435connect_and_create_session (struct Plugin *plugin,
436 const struct GNUNET_PeerIdentity *target,
437 const void *addr, size_t addrlen)
438{
439 struct GNUNET_SERVER_Client *client;
440 struct GNUNET_NETWORK_SocketHandle *conn;
441 struct Session *session;
442 int af;
443 char buf[INET6_ADDRSTRLEN];
444 uint16_t port;
445
446 session = plugin->sessions;
447 while (session != NULL)
448 {
449 if ((0 == memcmp (target,
450 &session->target,
451 sizeof (struct GNUNET_PeerIdentity))) &&
452 (session->connect_alen == addrlen) &&
453 (0 == memcmp (session->connect_addr, addr, addrlen)))
454 return session; /* already exists! */
455 session = session->next;
456 }
457
458 if (addrlen == sizeof (struct sockaddr_in))
459 {
460 af = AF_INET;
461 inet_ntop (af,
462 &((struct sockaddr_in *) addr)->sin_addr, buf, sizeof (buf));
463 port = ntohs (((struct sockaddr_in *) addr)->sin_port);
464 }
465 else if (addrlen == sizeof (struct sockaddr_in6))
466 {
467 af = AF_INET6;
468 inet_ntop (af,
469 &((struct sockaddr_in6 *) addr)->sin6_addr,
470 buf, sizeof (buf));
471 port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port);
472 }
473 else
474 {
475 GNUNET_break_op (0);
476 return NULL; /* invalid address */
477 }
478 conn = GNUNET_NETWORK_socket_create_from_sockaddr (plugin->env->sched,
479 af,
480 addr,
481 addrlen,
482 GNUNET_SERVER_MAX_MESSAGE_SIZE);
483 if (conn == NULL)
484 {
485#if DEBUG_TCP
486 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
487 "tcp",
488 "Failed to create connection to peer at `%s:%u'.\n",
489 buf, port);
490#endif
491 return NULL;
492 }
493 client = GNUNET_SERVER_connect_socket (plugin->server, conn);
494 GNUNET_assert (client != NULL);
495 session = create_session (plugin, target, client, addr, addrlen);
496 session->connect_alen = addrlen;
497 session->connect_addr = GNUNET_malloc (addrlen);
498 memcpy (session->connect_addr, addr, addrlen);
499#if DEBUG_TCP
500 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
501 "tcp",
502 "Creating new session %p with `%s:%u' based on `%s' request.\n",
503 session, buf, port, "send_to");
504#endif
505 return session;
506}
507
508
509/**
510 * If we have pending messages, ask the server to
511 * transmit them (schedule the respective tasks, etc.)
512 *
513 * @param session for which session should we do this
514 */
515static void process_pending_messages (struct Session *session);
516
517
518/**
519 * Function called to notify a client about the socket
520 * begin ready to queue more data. "buf" will be
521 * NULL and "size" zero if the socket was closed for
522 * writing in the meantime.
523 *
524 * @param cls closure
525 * @param size number of bytes available in buf
526 * @param buf where the callee should write the message
527 * @return number of bytes written to buf
528 */
529static size_t
530do_transmit (void *cls, size_t size, void *buf)
531{
532 struct Session *session = cls;
533 struct PendingMessage *pm;
534 char *cbuf;
535 uint16_t msize;
536 size_t ret;
537 struct DataMessage *dm;
538
539 session->transmit_handle = NULL;
540 if (buf == NULL)
541 {
542#if DEBUG_TCP
543 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
544 "tcp", "Timeout trying to transmit\n");
545#endif
546 /* timeout */
547 while (NULL != (pm = session->pending_messages))
548 {
549 session->pending_messages = pm->next;
550 if (pm->transmit_cont != NULL)
551 pm->transmit_cont (pm->transmit_cont_cls,
552 session->service_context,
553 &session->target, GNUNET_SYSERR);
554 GNUNET_free (pm);
555 }
556 return 0;
557 }
558 ret = 0;
559 cbuf = buf;
560 while (NULL != (pm = session->pending_messages))
561 {
562 if (pm->is_welcome)
563 {
564 if (size < (msize = htons (pm->msg->size)))
565 break;
566 memcpy (cbuf, pm->msg, msize);
567 cbuf += msize;
568 ret += msize;
569 size -= msize;
570 }
571 else
572 {
573 if (size <
574 sizeof (struct DataMessage) + (msize = htons (pm->msg->size)))
575 break;
576 dm = (struct DataMessage *) cbuf;
577 dm->header.size = htons (sizeof (struct DataMessage) + msize);
578 dm->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_DATA);
579 dm->ack_out = GNUNET_htonll (++session->out_msg_counter);
580 dm->ack_in = GNUNET_htonll (session->max_in_msg_counter);
581 cbuf += sizeof (struct DataMessage);
582 ret += sizeof (struct DataMessage);
583 size -= sizeof (struct DataMessage);
584 memcpy (cbuf, pm->msg, msize);
585 cbuf += msize;
586 ret += msize;
587 size -= msize;
588 }
589 session->pending_messages = pm->next;
590 if (pm->transmit_cont != NULL)
591 pm->transmit_cont (pm->transmit_cont_cls,
592 session->service_context,
593 &session->target, GNUNET_OK);
594 GNUNET_free (pm);
595 session->gen_time[session->out_msg_counter % ACK_LOG_SIZE]
596 = GNUNET_TIME_absolute_get ();
597 }
598 process_pending_messages (session);
599#if DEBUG_TCP || 1
600 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
601 "tcp", "Transmitting %u bytes\n", ret);
602#endif
603 return ret;
604}
605
606
607/**
608 * If we have pending messages, ask the server to
609 * transmit them (schedule the respective tasks, etc.)
610 *
611 * @param session for which session should we do this
612 */
613static void
614process_pending_messages (struct Session *session)
615{
616 GNUNET_assert (session->client != NULL);
617 if (session->pending_messages == NULL)
618 return;
619 if (session->transmit_handle != NULL)
620 return;
621 session->transmit_handle
622 = GNUNET_SERVER_notify_transmit_ready (session->client,
623 htons (session->pending_messages->
624 msg->size) +
625 (session->pending_messages->
626 is_welcome ? 0 : sizeof (struct
627 DataMessage)),
628 GNUNET_TIME_absolute_get_remaining
629 (session->pending_messages[0].
630 timeout), &do_transmit, session);
631}
632
633
634/**
635 * Function that can be used by the transport service to transmit
636 * a message using the plugin using a fresh connection (even if
637 * we already have a connection to this peer, this function is
638 * required to establish a new one).
639 *
640 * @param cls closure
641 * @param target who should receive this message
642 * @param msg1 first message to transmit
643 * @param msg2 second message to transmit (can be NULL)
644 * @param timeout how long should we try to transmit these?
645 * @param addrlen length of the address
646 * @param addr the address
647 * @return session if the transmission has been scheduled
648 * NULL if the address format is invalid
649 */
650static void *
651tcp_plugin_send_to (void *cls,
652 const struct GNUNET_PeerIdentity *target,
653 const struct GNUNET_MessageHeader *msg1,
654 const struct GNUNET_MessageHeader *msg2,
655 struct GNUNET_TIME_Relative timeout,
656 const void *addr, size_t addrlen)
657{
658 struct Plugin *plugin = cls;
659 struct Session *session;
660 struct PendingMessage *pl;
661 struct PendingMessage *pm;
662
663 session = connect_and_create_session (plugin, target, addr, addrlen);
664 if (session == NULL)
665 {
666#if DEBUG_TCP
667 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
668 "tcp", "Failed to create fresh session.\n");
669#endif
670 return NULL;
671 }
672 pl = NULL;
673 if (msg2 != NULL)
674 {
675 pm = GNUNET_malloc (sizeof (struct PendingMessage) +
676 ntohs (msg2->size));
677 pm->msg = (struct GNUNET_MessageHeader *) &pm[1];
678 memcpy (pm->msg, msg2, ntohs (msg2->size));
679 pm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
680 pm->is_welcome = GNUNET_NO;
681 pl = pm;
682 }
683 if (msg1 != NULL)
684 {
685 pm = GNUNET_malloc (sizeof (struct PendingMessage) +
686 ntohs (msg1->size));
687 pm->msg = (struct GNUNET_MessageHeader *) &pm[1];
688 memcpy (pm->msg, msg1, ntohs (msg1->size));
689 pm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
690 pm->is_welcome = GNUNET_NO;
691 pm->next = pl;
692 pl = pm;
693 }
694 /* append */
695 if (session->pending_messages != NULL)
696 {
697 pm = session->pending_messages;
698 while (pm->next != NULL)
699 pm = pm->next;
700 pm->next = pl;
701 }
702 else
703 {
704 session->pending_messages = pl;
705 }
706 process_pending_messages (session);
707 return session;
708}
709
710
711/**
712 * Functions with this signature are called whenever we need
713 * to close a session due to a disconnect or failure to
714 * establish a connection.
715 *
716 * @param session session to close down
717 */
718static void
719disconnect_session (struct Session *session)
720{
721 struct Session *prev;
722 struct Session *pos;
723 struct PendingMessage *pm;
724
725#if DEBUG_TCP
726 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
727 "tcp",
728 "Disconnecting from other peer (session %p).\n", session);
729#endif
730 /* remove from session list */
731 prev = NULL;
732 pos = session->plugin->sessions;
733 while (pos != session)
734 {
735 prev = pos;
736 pos = pos->next;
737 }
738 if (prev == NULL)
739 session->plugin->sessions = session->next;
740 else
741 prev->next = session->next;
742 /* clean up state */
743 if (session->client != NULL)
744 {
745#if DEBUG_TCP
746 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
747 "Disconnecting from client address %p\n", session->client);
748#endif
749 GNUNET_SERVER_client_drop (session->client);
750 session->client = NULL;
751 }
752 if (session->transmit_handle != NULL)
753 {
754 GNUNET_NETWORK_notify_transmit_ready_cancel (session->transmit_handle);
755 session->transmit_handle = NULL;
756 }
757 while (NULL != (pm = session->pending_messages))
758 {
759 session->pending_messages = pm->next;
760 if (NULL != pm->transmit_cont)
761 pm->transmit_cont (pm->transmit_cont_cls,
762 session->service_context,
763 &session->target, GNUNET_SYSERR);
764 GNUNET_free (pm);
765 }
766 /* notify transport service about disconnect */
767 session->plugin->env->receive (session->plugin->env->cls,
768 session,
769 session->service_context,
770 GNUNET_TIME_UNIT_ZERO,
771 &session->target, NULL);
772 GNUNET_free_non_null (session->connect_addr);
773 GNUNET_free (session);
774}
775
776
777/**
778 * Iterator callback to go over all addresses. If we get
779 * a TCP address, increment the counter
780 *
781 * @param cls closure, points to the counter
782 * @param tname name of the transport
783 * @param expiration expiration time
784 * @param addr the address
785 * @param addrlen length of the address
786 * @return GNUNET_OK to keep the address,
787 * GNUNET_NO to delete it from the HELLO
788 * GNUNET_SYSERR to stop iterating (but keep current address)
789 */
790static int
791count_tcp_addresses (void *cls,
792 const char *tname,
793 struct GNUNET_TIME_Absolute expiration,
794 const void *addr, size_t addrlen)
795{
796 unsigned int *counter = cls;
797
798 if (0 != strcmp (tname, "tcp"))
799 return GNUNET_OK; /* not one of ours */
800 (*counter)++;
801 return GNUNET_OK; /* failed to connect */
802}
803
804
805struct ConnectContext
806{
807 struct Plugin *plugin;
808
809 struct GNUNET_NETWORK_SocketHandle *sa;
810
811 struct PendingMessage *welcome;
812
813 unsigned int pos;
814};
815
816
817/**
818 * Iterator callback to go over all addresses. If we get
819 * the "pos" TCP address, try to connect to it.
820 *
821 * @param cls closure
822 * @param tname name of the transport
823 * @param expiration expiration time
824 * @param addrlen length of the address
825 * @param addr the address
826 * @return GNUNET_OK to keep the address,
827 * GNUNET_NO to delete it from the HELLO
828 * GNUNET_SYSERR to stop iterating (but keep current address)
829 */
830static int
831try_connect_to_address (void *cls,
832 const char *tname,
833 struct GNUNET_TIME_Absolute expiration,
834 const void *addr, size_t addrlen)
835{
836 struct ConnectContext *cc = cls;
837 int af;
838
839 if (0 != strcmp (tname, "tcp"))
840 return GNUNET_OK; /* not one of ours */
841 if (sizeof (struct sockaddr_in) == addrlen)
842 af = AF_INET;
843 else if (sizeof (struct sockaddr_in6) == addrlen)
844 af = AF_INET6;
845 else
846 {
847 /* not a valid address */
848 GNUNET_break (0);
849 return GNUNET_NO;
850 }
851 if (0 == cc->pos--)
852 {
853 cc->welcome = create_welcome (addrlen, addr, cc->plugin);
854 cc->sa =
855 GNUNET_NETWORK_socket_create_from_sockaddr (cc->plugin->env->sched,
856 af, addr, addrlen,
857 GNUNET_SERVER_MAX_MESSAGE_SIZE);
858#if DEBUG_TCP
859 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
860 "tcp", "Connected to other peer.\n");
861#endif
862 return GNUNET_SYSERR;
863 }
864 return GNUNET_OK; /* failed to connect */
865}
866
867
868/**
869 * Type of an iterator over the hosts. Note that each
870 * host will be called with each available protocol.
871 *
872 * @param cls closure
873 * @param peer id of the peer, NULL for last call
874 * @param hello hello message for the peer (can be NULL)
875 * @param trust amount of trust we have in the peer
876 */
877static void
878session_try_connect (void *cls,
879 const struct GNUNET_PeerIdentity *peer,
880 const struct GNUNET_HELLO_Message *hello, uint32_t trust)
881{
882 struct Session *session = cls;
883 unsigned int count;
884 struct ConnectContext cctx;
885 struct PendingMessage *pm;
886
887 if (peer == NULL)
888 {
889 /* last call, destroy session if we are still not
890 connected */
891 if (session->still_connecting == GNUNET_NO)
892 {
893#if DEBUG_TCP
894 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
895 "tcp",
896 "Connected to other peer, now processing messages.\n");
897#endif
898 process_pending_messages (session);
899 }
900 else
901 {
902#if DEBUG_TCP
903 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
904 "tcp",
905 "Failed to connect to other peer, now closing session.\n");
906#endif
907 disconnect_session (session);
908 }
909 return;
910 }
911 if ((hello == NULL) || (session->client != NULL))
912 {
913 GNUNET_break (0); /* should this ever happen!? */
914 return;
915 }
916 count = 0;
917 GNUNET_HELLO_iterate_addresses (hello,
918 GNUNET_NO, &count_tcp_addresses, &count);
919 if (count == 0)
920 {
921#if DEBUG_TCP
922 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
923 "tcp",
924 "Asked to connect, but have no addresses to try.\n");
925#endif
926 return;
927 }
928 cctx.plugin = session->plugin;
929 cctx.sa = NULL;
930 cctx.welcome = NULL;
931 cctx.pos = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, count);
932 GNUNET_HELLO_iterate_addresses (hello,
933 GNUNET_NO, &try_connect_to_address, &cctx);
934 if (cctx.sa == NULL)
935 {
936#if DEBUG_TCP
937 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
938 "tcp",
939 "Asked to connect, but all addresses failed.\n");
940#endif
941 GNUNET_free_non_null (cctx.welcome);
942 return;
943 }
944 session->client = GNUNET_SERVER_connect_socket (session->plugin->server,
945 cctx.sa);
946#if DEBUG_TCP
947 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
948 "Connected getting client address %p\n", session->client);
949#endif
950 if (session->client == NULL)
951 {
952 GNUNET_break (0); /* how could this happen? */
953 GNUNET_free_non_null (cctx.welcome);
954 return;
955 }
956 pm = cctx.welcome;
957 /* prepend (!) */
958 pm->next = session->pending_messages;
959 session->pending_messages = pm;
960 session->still_connecting = GNUNET_NO;
961#if DEBUG_TCP
962 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
963 "tcp",
964 "Connected to other peer, now sending `%s' message.\n",
965 "WELCOME");
966#endif
967}
968
969
970/**
971 * Function that can be used by the transport service to transmit
972 * a message using the plugin.
973 *
974 * @param cls closure
975 * @param plugin_context value we were asked to pass to this plugin
976 * to respond to the given peer (use is optional,
977 * but may speed up processing), can be NULL
978 * @param service_context value passed to the transport-service
979 * to identify the neighbour
980 * @param target who should receive this message
981 * @param msg the message to transmit
982 * @param cont continuation to call once the message has
983 * been transmitted (or if the transport is ready
984 * for the next transmission call; or if the
985 * peer disconnected...)
986 * @param cont_cls closure for cont
987 * @return plugin_context that should be used next time for
988 * sending messages to the specified peer
989 */
990static void *
991tcp_plugin_send (void *cls,
992 void *plugin_context,
993 struct ReadyList *service_context,
994 const struct GNUNET_PeerIdentity *target,
995 const struct GNUNET_MessageHeader *msg,
996 struct GNUNET_TIME_Relative timeout,
997 GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
998{
999 struct Plugin *plugin = cls;
1000 struct Session *session = plugin_context;
1001 struct PendingMessage *pm;
1002 struct PendingMessage *pme;
1003
1004 if (session == NULL)
1005 session = find_session_by_target (plugin, target);
1006 pm = GNUNET_malloc (sizeof (struct PendingMessage) + ntohs (msg->size));
1007 pm->msg = (struct GNUNET_MessageHeader *) &pm[1];
1008 memcpy (pm->msg, msg, ntohs (msg->size));
1009 pm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1010 pm->transmit_cont = cont;
1011 pm->transmit_cont_cls = cont_cls;
1012 if (session == NULL)
1013 {
1014 session = GNUNET_malloc (sizeof (struct Session));
1015#if DEBUG_TCP
1016 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1017 "tcp",
1018 "Asked to transmit, creating fresh session %p.\n",
1019 session);
1020#endif
1021 session->next = plugin->sessions;
1022 plugin->sessions = session;
1023 session->plugin = plugin;
1024 session->target = *target;
1025 session->last_quota_update = GNUNET_TIME_absolute_get ();
1026 session->quota_in = plugin->env->default_quota_in;
1027 session->expecting_welcome = GNUNET_YES;
1028 session->still_connecting = GNUNET_YES;
1029 session->pending_messages = pm;
1030 GNUNET_PEERINFO_for_all (plugin->env->cfg,
1031 plugin->env->sched,
1032 target,
1033 0, timeout, &session_try_connect, session);
1034 return session;
1035 }
1036 GNUNET_assert (session != NULL);
1037 GNUNET_assert (session->still_connecting == GNUNET_NO);
1038 /* append pm to pending_messages list */
1039 pme = session->pending_messages;
1040 if (pme == NULL)
1041 {
1042 session->pending_messages = pm;
1043 }
1044 else
1045 {
1046 while (NULL != pme->next)
1047 pme = pme->next;
1048 pme->next = pm;
1049 }
1050#if DEBUG_TCP
1051 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1052 "tcp", "Asked to transmit, added message to list.\n");
1053#endif
1054 process_pending_messages (session);
1055 return session;
1056}
1057
1058
1059
1060/**
1061 * Function that can be called to force a disconnect from the
1062 * specified neighbour. This should also cancel all previously
1063 * scheduled transmissions. Obviously the transmission may have been
1064 * partially completed already, which is OK. The plugin is supposed
1065 * to close the connection (if applicable) and no longer call the
1066 * transmit continuation(s).
1067 *
1068 * Finally, plugin MUST NOT call the services's receive function to
1069 * notify the service that the connection to the specified target was
1070 * closed after a getting this call.
1071 *
1072 * @param cls closure
1073 * @param plugin_context value we were asked to pass to this plugin
1074 * to respond to the given peer (use is optional,
1075 * but may speed up processing), can be NULL (if
1076 * NULL was returned from the transmit function)
1077 * @param service_context must correspond to the service context
1078 * of the corresponding Transmit call; the plugin should
1079 * not cancel a send call made with a different service
1080 * context pointer! Never NULL.
1081 * @param target peer for which the last transmission is
1082 * to be cancelled
1083 */
1084static void
1085tcp_plugin_cancel (void *cls,
1086 void *plugin_context,
1087 struct ReadyList *service_context,
1088 const struct GNUNET_PeerIdentity *target)
1089{
1090 struct Plugin *plugin = cls;
1091 struct PendingMessage *pm;
1092 struct Session *session;
1093 struct Session *next;
1094
1095 session = plugin->sessions;
1096 while (session != NULL)
1097 {
1098 next = session->next;
1099 if (0 == memcmp (target,
1100 &session->target, sizeof (struct GNUNET_PeerIdentity)))
1101 {
1102 pm = session->pending_messages;
1103 while (pm != NULL)
1104 {
1105 pm->transmit_cont = NULL;
1106 pm->transmit_cont_cls = NULL;
1107 pm = pm->next;
1108 }
1109 session->service_context = NULL;
1110 GNUNET_SERVER_client_disconnect (session->client);
1111 /* rest of the clean-up of the session will be done as part of
1112 disconnect_notify which should be triggered any time now */
1113 }
1114 session = next;
1115 }
1116}
1117
1118
1119struct PrettyPrinterContext
1120{
1121 GNUNET_TRANSPORT_AddressStringCallback asc;
1122 void *asc_cls;
1123 uint16_t port;
1124};
1125
1126
1127/**
1128 * Append our port and forward the result.
1129 */
1130static void
1131append_port (void *cls, const char *hostname)
1132{
1133 struct PrettyPrinterContext *ppc = cls;
1134 char *ret;
1135
1136 if (hostname == NULL)
1137 {
1138 ppc->asc (ppc->asc_cls, NULL);
1139 GNUNET_free (ppc);
1140 return;
1141 }
1142 GNUNET_asprintf (&ret, "%s:%d", hostname, ppc->port);
1143 ppc->asc (ppc->asc_cls, ret);
1144 GNUNET_free (ret);
1145}
1146
1147
1148/**
1149 * Convert the transports address to a nice, human-readable
1150 * format.
1151 *
1152 * @param cls closure
1153 * @param name name of the transport that generated the address
1154 * @param addr one of the addresses of the host, NULL for the last address
1155 * the specific address format depends on the transport
1156 * @param addrlen length of the address
1157 * @param numeric should (IP) addresses be displayed in numeric form?
1158 * @param timeout after how long should we give up?
1159 * @param asc function to call on each string
1160 * @param asc_cls closure for asc
1161 */
1162static void
1163tcp_plugin_address_pretty_printer (void *cls,
1164 const char *type,
1165 const void *addr,
1166 size_t addrlen,
1167 int numeric,
1168 struct GNUNET_TIME_Relative timeout,
1169 GNUNET_TRANSPORT_AddressStringCallback asc,
1170 void *asc_cls)
1171{
1172 struct Plugin *plugin = cls;
1173 const struct sockaddr_in *v4;
1174 const struct sockaddr_in6 *v6;
1175 struct PrettyPrinterContext *ppc;
1176
1177 if ((addrlen != sizeof (struct sockaddr_in)) &&
1178 (addrlen != sizeof (struct sockaddr_in6)))
1179 {
1180 /* invalid address */
1181 GNUNET_break_op (0);
1182 asc (asc_cls, NULL);
1183 return;
1184 }
1185 ppc = GNUNET_malloc (sizeof (struct PrettyPrinterContext));
1186 ppc->asc = asc;
1187 ppc->asc_cls = asc_cls;
1188 if (addrlen == sizeof (struct sockaddr_in))
1189 {
1190 v4 = (const struct sockaddr_in *) addr;
1191 ppc->port = ntohs (v4->sin_port);
1192 }
1193 else
1194 {
1195 v6 = (const struct sockaddr_in6 *) addr;
1196 ppc->port = ntohs (v6->sin6_port);
1197
1198 }
1199 GNUNET_RESOLVER_hostname_get (plugin->env->sched,
1200 plugin->env->cfg,
1201 addr,
1202 addrlen,
1203 !numeric, timeout, &append_port, ppc);
1204}
1205
1206
1207/**
1208 * Update the last-received and bandwidth quota values
1209 * for this session.
1210 *
1211 * @param session session to update
1212 * @param force set to GNUNET_YES if we should update even
1213 * though the minimum refresh time has not yet expired
1214 */
1215static void
1216update_quota (struct Session *session, int force)
1217{
1218 struct GNUNET_TIME_Absolute now;
1219 unsigned long long delta;
1220 unsigned long long total_allowed;
1221 unsigned long long total_remaining;
1222
1223 now = GNUNET_TIME_absolute_get ();
1224 delta = now.value - session->last_quota_update.value;
1225 if ((delta < MIN_QUOTA_REFRESH_TIME) && (!force))
1226 return; /* too early, not enough data */
1227
1228 total_allowed = session->quota_in * delta;
1229 if (total_allowed > session->last_received)
1230 {
1231 /* got less than acceptable */
1232 total_remaining = total_allowed - session->last_received;
1233 session->last_received = 0;
1234 delta = total_remaining / session->quota_in; /* bonus seconds */
1235 if (delta > MAX_BANDWIDTH_CARRY)
1236 delta = MAX_BANDWIDTH_CARRY; /* limit amount of carry-over */
1237 }
1238 else
1239 {
1240 /* got more than acceptable */
1241 total_remaining = 0;
1242 session->last_received -= total_allowed;
1243 delta = 0;
1244 }
1245 session->last_quota_update.value = now.value - delta;
1246}
1247
1248
1249/**
1250 * Set a quota for receiving data from the given peer; this is a
1251 * per-transport limit. The transport should limit its read/select
1252 * calls to stay below the quota (in terms of incoming data).
1253 *
1254 * @param cls closure
1255 * @param peer the peer for whom the quota is given
1256 * @param quota_in quota for receiving/sending data in bytes per ms
1257 */
1258static void
1259tcp_plugin_set_receive_quota (void *cls,
1260 const struct GNUNET_PeerIdentity *target,
1261 uint32_t quota_in)
1262{
1263 struct Plugin *plugin = cls;
1264 struct Session *session;
1265
1266 session = find_session_by_target (plugin, target);
1267 if (session->quota_in != quota_in)
1268 {
1269 update_quota (session, GNUNET_YES);
1270 if (session->quota_in > quota_in)
1271 session->last_quota_update = GNUNET_TIME_absolute_get ();
1272 session->quota_in = quota_in;
1273 }
1274}
1275
1276
1277/**
1278 * Check if the given port is plausible (must be either
1279 * our listen port or our advertised port). If it is
1280 * neither, we return one of these two ports at random.
1281 *
1282 * @return either in_port or a more plausible port
1283 */
1284static uint16_t
1285check_port (struct Plugin *plugin, uint16_t in_port)
1286{
1287 if ((in_port == plugin->adv_port) || (in_port == plugin->open_port))
1288 return in_port;
1289 return (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1290 2) == 0)
1291 ? plugin->open_port : plugin->adv_port;
1292}
1293
1294
1295/**
1296 * Another peer has suggested an address for this
1297 * peer and transport plugin. Check that this could be a valid
1298 * address. If so, consider adding it to the list
1299 * of addresses.
1300 *
1301 * @param cls closure
1302 * @param addr pointer to the address
1303 * @param addrlen length of addr
1304 * @return GNUNET_OK if this is a plausible address for this peer
1305 * and transport
1306 */
1307static int
1308tcp_plugin_address_suggested (void *cls, const void *addr, size_t addrlen)
1309{
1310 struct Plugin *plugin = cls;
1311 char buf[sizeof (struct sockaddr_in6)];
1312 struct sockaddr_in *v4;
1313 struct sockaddr_in6 *v6;
1314 char dst[INET6_ADDRSTRLEN];
1315 uint16_t port;
1316
1317 if ((addrlen != sizeof (struct sockaddr_in)) &&
1318 (addrlen != sizeof (struct sockaddr_in6)))
1319 {
1320 GNUNET_break_op (0);
1321 return GNUNET_SYSERR;
1322 }
1323 memcpy (buf, addr, sizeof (struct sockaddr_in6));
1324 if (addrlen == sizeof (struct sockaddr_in))
1325 {
1326 v4 = (struct sockaddr_in *) buf;
1327 v4->sin_port = htons (check_port (plugin, ntohs (v4->sin_port)));
1328 inet_ntop (AF_INET, &v4->sin_addr, dst, sizeof (dst));
1329 port = ntohs (v4->sin_port);
1330 }
1331 else
1332 {
1333 v6 = (struct sockaddr_in6 *) buf;
1334 v6->sin6_port = htons (check_port (plugin, ntohs (v6->sin6_port)));
1335 inet_ntop (AF_INET6, &v6->sin6_addr, dst, sizeof (dst));
1336 port = ntohs (v6->sin6_port);
1337 }
1338#if DEBUG_TCP
1339 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1340 "tcp",
1341 "Informing transport service about my address `%s:%u'.\n",
1342 dst, port);
1343#endif
1344 plugin->env->notify_address (plugin->env->cls,
1345 "tcp",
1346 buf, addrlen, LEARNED_ADDRESS_EXPIRATION);
1347 return GNUNET_OK;
1348}
1349
1350
1351/**
1352 * We've received a welcome from this peer via TCP.
1353 * Possibly create a fresh client record and send back
1354 * our welcome.
1355 *
1356 * @param cls closure
1357 * @param server the server handling the message
1358 * @param client identification of the client
1359 * @param message the actual message
1360 */
1361static void
1362handle_tcp_welcome (void *cls,
1363 struct GNUNET_SERVER_Handle *server,
1364 struct GNUNET_SERVER_Client *client,
1365 const struct GNUNET_MessageHeader *message)
1366{
1367 struct Plugin *plugin = cls;
1368 struct Session *session_c;
1369 const struct WelcomeMessage *wm;
1370 uint16_t msize;
1371 uint32_t addrlen;
1372 size_t alen;
1373 void *vaddr;
1374 const struct sockaddr *addr;
1375
1376#if DEBUG_TCP
1377 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1378 "tcp",
1379 "Received `%s' message from %p.\n", "WELCOME", client);
1380#endif
1381 msize = ntohs (message->size);
1382 if (msize < sizeof (struct WelcomeMessage))
1383 {
1384 GNUNET_break_op (0);
1385 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1386 return;
1387 }
1388 wm = (const struct WelcomeMessage *) message;
1389 session_c = find_session_by_client (plugin, client);
1390 if (session_c == NULL)
1391 {
1392 vaddr = NULL;
1393 GNUNET_SERVER_client_get_address (client, &vaddr, &alen);
1394 GNUNET_SERVER_client_keep (client);
1395 session_c = create_session (plugin,
1396 &wm->clientIdentity, client, vaddr, alen);
1397#if DEBUG_TCP
1398 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1399 "tcp",
1400 "Creating new session %p for incoming `%s' message.\n",
1401 session_c, "WELCOME");
1402#endif
1403 GNUNET_free_non_null (vaddr);
1404 process_pending_messages (session_c);
1405 }
1406 session_c->expecting_welcome = GNUNET_NO;
1407 if (0 < (addrlen = msize - sizeof (struct WelcomeMessage)))
1408 {
1409 addr = (const struct sockaddr *) &wm[1];
1410 tcp_plugin_address_suggested (plugin, addr, addrlen);
1411 }
1412 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1413}
1414
1415
1416/**
1417 * Calculate how long we should delay reading from the TCP socket to
1418 * ensure that we stay within our bandwidth limits (push back).
1419 *
1420 * @param session for which client should this be calculated
1421 */
1422static struct GNUNET_TIME_Relative
1423calculate_throttle_delay (struct Session *session)
1424{
1425 struct GNUNET_TIME_Relative ret;
1426 struct GNUNET_TIME_Absolute now;
1427 uint64_t del;
1428 uint64_t avail;
1429 uint64_t excess;
1430
1431 now = GNUNET_TIME_absolute_get ();
1432 del = now.value - session->last_quota_update.value;
1433 if (del > MAX_BANDWIDTH_CARRY)
1434 {
1435 update_quota (session, GNUNET_YES);
1436 del = now.value - session->last_quota_update.value;
1437 GNUNET_assert (del <= MAX_BANDWIDTH_CARRY);
1438 }
1439 if (session->quota_in == 0)
1440 session->quota_in = 1; /* avoid divison by zero */
1441 avail = del * session->quota_in;
1442 if (avail > session->last_received)
1443 return GNUNET_TIME_UNIT_ZERO; /* can receive right now */
1444 excess = session->last_received - avail;
1445 ret.value = excess / session->quota_in;
1446 return ret;
1447}
1448
1449
1450/**
1451 * Task to signal the server that we can continue
1452 * receiving from the TCP client now.
1453 */
1454static void
1455delayed_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1456{
1457 struct Session *session = cls;
1458 GNUNET_SERVER_receive_done (session->client, GNUNET_OK);
1459}
1460
1461
1462/**
1463 * We've received data for this peer via TCP. Unbox,
1464 * compute latency and forward.
1465 *
1466 * @param cls closure
1467 * @param server the server handling the message
1468 * @param client identification of the client
1469 * @param message the actual message
1470 */
1471static void
1472handle_tcp_data (void *cls,
1473 struct GNUNET_SERVER_Handle *server,
1474 struct GNUNET_SERVER_Client *client,
1475 const struct GNUNET_MessageHeader *message)
1476{
1477 struct Plugin *plugin = cls;
1478 struct Session *session;
1479 const struct DataMessage *dm;
1480 uint16_t msize;
1481 const struct GNUNET_MessageHeader *msg;
1482 struct GNUNET_TIME_Relative latency;
1483 struct GNUNET_TIME_Absolute ttime;
1484 struct GNUNET_TIME_Absolute now;
1485 struct GNUNET_TIME_Relative delay;
1486 uint64_t ack_in;
1487
1488#if DEBUG_TCP
1489 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1490 "tcp", "Receiving data from other peer.\n");
1491#endif
1492 msize = ntohs (message->size);
1493 if ((msize <
1494 sizeof (struct DataMessage) + sizeof (struct GNUNET_MessageHeader)))
1495 {
1496 GNUNET_break_op (0);
1497 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1498 return;
1499 }
1500 session = find_session_by_client (plugin, client);
1501 if ((NULL == session) || (GNUNET_YES == session->expecting_welcome))
1502 {
1503 GNUNET_break_op (0);
1504 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1505 return;
1506 }
1507 dm = (const struct DataMessage *) message;
1508 session->max_in_msg_counter = GNUNET_MAX (session->max_in_msg_counter,
1509 GNUNET_ntohll (dm->ack_out));
1510 msg = (const struct GNUNET_MessageHeader *) &dm[1];
1511 if (msize != sizeof (struct DataMessage) + ntohs (msg->size))
1512 {
1513 GNUNET_break_op (0);
1514 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1515 return;
1516 }
1517 /* estimate latency */
1518 ack_in = GNUNET_ntohll (dm->ack_in);
1519 if ((ack_in <= session->out_msg_counter) &&
1520 (session->out_msg_counter - ack_in < ACK_LOG_SIZE))
1521 {
1522 delay = GNUNET_TIME_relative_ntoh (dm->delay);
1523 ttime = session->gen_time[ack_in % ACK_LOG_SIZE];
1524 now = GNUNET_TIME_absolute_get ();
1525 if (delay.value > now.value - ttime.value)
1526 delay.value = 0; /* not plausible */
1527 /* update (round-trip) latency using ageing; we
1528 use 7:1 so that we can reasonably quickly react
1529 to changes, but not so fast that latency is largely
1530 jitter... */
1531 session->latency_estimate
1532 = ((7 * session->latency_estimate) +
1533 (now.value - ttime.value - delay.value)) / 8;
1534 }
1535 latency.value = (uint64_t) session->latency_estimate;
1536 /* deliver on */
1537#if DEBUG_TCP
1538 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1539 "tcp",
1540 "Forwarding data of type %u to transport service.\n",
1541 ntohs (msg->type));
1542#endif
1543 session->service_context
1544 = plugin->env->receive (plugin->env->cls,
1545 session,
1546 session->service_context,
1547 latency, &session->target, msg);
1548 /* update bandwidth used */
1549 session->last_received += msize;
1550 update_quota (session, GNUNET_NO);
1551
1552 delay = calculate_throttle_delay (session);
1553 if (delay.value == 0)
1554 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1555 else
1556 GNUNET_SCHEDULER_add_delayed (session->plugin->env->sched,
1557 GNUNET_NO,
1558 GNUNET_SCHEDULER_PRIORITY_HIGH,
1559 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1560 delay, &delayed_done, session);
1561}
1562
1563
1564/**
1565 * Handlers for the various TCP messages.
1566 */
1567static struct GNUNET_SERVER_MessageHandler my_handlers[] = {
1568 {&handle_tcp_welcome, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME, 0},
1569 {&handle_tcp_data, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_DATA, 0},
1570 {NULL, NULL, 0, 0}
1571};
1572
1573
1574static void
1575create_tcp_handlers (struct Plugin *plugin)
1576{
1577 unsigned int i;
1578 plugin->handlers = GNUNET_malloc (sizeof (my_handlers));
1579 memcpy (plugin->handlers, my_handlers, sizeof (my_handlers));
1580 for (i = 0;
1581 i <
1582 sizeof (my_handlers) / sizeof (struct GNUNET_SERVER_MessageHandler);
1583 i++)
1584 plugin->handlers[i].callback_cls = plugin;
1585 GNUNET_SERVER_add_handlers (plugin->server, plugin->handlers);
1586}
1587
1588
1589/**
1590 * Functions with this signature are called whenever a peer
1591 * is disconnected on the network level.
1592 *
1593 * @param cls closure
1594 * @param client identification of the client
1595 */
1596static void
1597disconnect_notify (void *cls, struct GNUNET_SERVER_Client *client)
1598{
1599 struct Plugin *plugin = cls;
1600 struct Session *session;
1601
1602#if DEBUG_TCP
1603 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1604 "tcp",
1605 "Notified about network-level disconnect of client %p.\n",
1606 client);
1607#endif
1608 session = find_session_by_client (plugin, client);
1609 if (session == NULL)
1610 return; /* unknown, nothing to do */
1611#if DEBUG_TCP
1612 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1613 "tcp", "Will now destroy session %p.\n", session);
1614#endif
1615 disconnect_session (session);
1616}
1617
1618
1619/**
1620 * Add the IP of our network interface to the list of
1621 * our external IP addresses.
1622 */
1623static int
1624process_interfaces (void *cls,
1625 const char *name,
1626 int isDefault,
1627 const struct sockaddr *addr, socklen_t addrlen)
1628{
1629 struct Plugin *plugin = cls;
1630 char dst[INET6_ADDRSTRLEN];
1631 int af;
1632 struct sockaddr_in *v4;
1633 struct sockaddr_in6 *v6;
1634
1635 af = addr->sa_family;
1636 if (af == AF_INET)
1637 {
1638 v4 = (struct sockaddr_in *) addr;
1639 inet_ntop (AF_INET, &v4->sin_addr, dst, sizeof (dst));
1640 v4->sin_port = htons (plugin->adv_port);
1641 }
1642 else
1643 {
1644 GNUNET_assert (af == AF_INET6);
1645 v6 = (struct sockaddr_in6 *) addr;
1646 inet_ntop (AF_INET6, &v6->sin6_addr, dst, sizeof (dst));
1647 v6->sin6_port = htons (plugin->adv_port);
1648 }
1649 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO |
1650 GNUNET_ERROR_TYPE_BULK,
1651 "tcp", _("Found address `%s' (%s)\n"), dst, name);
1652 plugin->env->notify_address (plugin->env->cls,
1653 "tcp",
1654 addr, addrlen, GNUNET_TIME_UNIT_FOREVER_REL);
1655 return GNUNET_OK;
1656}
1657
1658
1659/**
1660 * Function called by the resolver for each address obtained from DNS
1661 * for our own hostname. Add the addresses to the list of our
1662 * external IP addresses.
1663 *
1664 * @param cls closure
1665 * @param addr one of the addresses of the host, NULL for the last address
1666 * @param addrlen length of the address
1667 */
1668static void
1669process_hostname_ips (void *cls,
1670 const struct sockaddr *addr, socklen_t addrlen)
1671{
1672 struct Plugin *plugin = cls;
1673
1674 if (addr == NULL)
1675 return;
1676 plugin->env->notify_address (plugin->env->cls,
1677 "tcp",
1678 addr, addrlen, GNUNET_TIME_UNIT_FOREVER_REL);
1679}
1680
1681
1682/**
1683 * Entry point for the plugin.
1684 */
1685void *
1686libgnunet_plugin_transport_tcp_init (void *cls)
1687{
1688 struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1689 struct GNUNET_TRANSPORT_PluginFunctions *api;
1690 struct Plugin *plugin;
1691 struct GNUNET_SERVICE_Context *service;
1692 unsigned long long aport;
1693 unsigned long long bport;
1694
1695 service = GNUNET_SERVICE_start ("tcp", env->sched, env->cfg);
1696 if (service == NULL)
1697 {
1698 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
1699 "tcp",
1700 _
1701 ("Failed to start service for `%s' transport plugin.\n"),
1702 "tcp");
1703 return NULL;
1704 }
1705 aport = 0;
1706 if ((GNUNET_OK !=
1707 GNUNET_CONFIGURATION_get_value_number (env->cfg,
1708 "tcp",
1709 "PORT",
1710 &bport)) ||
1711 (bport > 65535) ||
1712 ((GNUNET_OK ==
1713 GNUNET_CONFIGURATION_get_value_number (env->cfg,
1714 "tcp",
1715 "ADVERTISED-PORT",
1716 &aport)) && (aport > 65535)))
1717 {
1718 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1719 "tcp",
1720 _
1721 ("Require valid port number for service `%s' in configuration!\n"),
1722 "tcp");
1723 GNUNET_SERVICE_stop (service);
1724 return NULL;
1725 }
1726 if (aport == 0)
1727 aport = bport;
1728 plugin = GNUNET_malloc (sizeof (struct Plugin));
1729 plugin->open_port = bport;
1730 plugin->adv_port = aport;
1731 plugin->env = env;
1732 plugin->lsock = NULL;
1733 plugin->statistics = NULL;
1734 api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1735 api->cls = plugin;
1736 api->send_to = &tcp_plugin_send_to;
1737 api->send = &tcp_plugin_send;
1738 api->cancel = &tcp_plugin_cancel;
1739 api->address_pretty_printer = &tcp_plugin_address_pretty_printer;
1740 api->set_receive_quota = &tcp_plugin_set_receive_quota;
1741 api->address_suggested = &tcp_plugin_address_suggested;
1742 api->cost_estimate = 42; /* TODO: ATS */
1743 plugin->service = service;
1744 plugin->server = GNUNET_SERVICE_get_server (service);
1745 create_tcp_handlers (plugin);
1746 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1747 "tcp", _("TCP transport listening on port %u\n"), bport);
1748 if (aport != bport)
1749 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1750 "tcp",
1751 _
1752 ("TCP transport advertises itself as being on port %u\n"),
1753 aport);
1754 GNUNET_SERVER_disconnect_notify (plugin->server, &disconnect_notify,
1755 plugin);
1756 GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
1757 GNUNET_RESOLVER_hostname_resolve (env->sched,
1758 env->cfg,
1759 AF_UNSPEC,
1760 HOSTNAME_RESOLVE_TIMEOUT,
1761 &process_hostname_ips, plugin);
1762 return api;
1763}
1764
1765
1766/**
1767 * Exit point from the plugin.
1768 */
1769void *
1770libgnunet_plugin_transport_tcp_done (void *cls)
1771{
1772 struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1773 struct Plugin *plugin = api->cls;
1774
1775 GNUNET_SERVICE_stop (plugin->service);
1776 GNUNET_free (plugin->handlers);
1777 GNUNET_free (plugin);
1778 GNUNET_free (api);
1779 return NULL;
1780}
1781
1782/* end of plugin_transport_tcp.c */
diff --git a/src/transport/plugin_transport_template.c b/src/transport/plugin_transport_template.c
new file mode 100644
index 000000000..1c8b06c61
--- /dev/null
+++ b/src/transport/plugin_transport_template.c
@@ -0,0 +1,335 @@
1/*
2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file transport/plugin_transport_template.c
23 * @brief template for a new transport service
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_protocols.h"
29#include "gnunet_network_lib.h"
30#include "gnunet_server_lib.h"
31#include "gnunet_service_lib.h"
32#include "gnunet_statistics_service.h"
33#include "gnunet_transport_service.h"
34#include "plugin_transport.h"
35
36#define DEBUG_TEMPLATE GNUNET_NO
37
38/**
39 * After how long do we expire an address that we
40 * learned from another peer if it is not reconfirmed
41 * by anyone?
42 */
43#define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
44
45
46/**
47 * Encapsulation of all of the state of the plugin.
48 */
49struct Plugin;
50
51
52/**
53 * Session handle for connections.
54 */
55struct Session
56{
57
58 /**
59 * Stored in a linked list.
60 */
61 struct Session *next;
62
63 /**
64 * Pointer to the global plugin struct.
65 */
66 struct Plugin *plugin;
67
68 /**
69 * The client (used to identify this connection)
70 */
71 /* void *client; */
72
73 /**
74 * Continuation function to call once the transmission buffer
75 * has again space available. NULL if there is no
76 * continuation to call.
77 */
78 GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
79
80 /**
81 * Closure for transmit_cont.
82 */
83 void *transmit_cont_cls;
84
85 /**
86 * To whom are we talking to (set to our identity
87 * if we are still waiting for the welcome message)
88 */
89 struct GNUNET_PeerIdentity sender;
90
91 /**
92 * At what time did we reset last_received last?
93 */
94 struct GNUNET_TIME_Absolute last_quota_update;
95
96 /**
97 * How many bytes have we received since the "last_quota_update"
98 * timestamp?
99 */
100 uint64_t last_received;
101
102 /**
103 * Number of bytes per ms that this peer is allowed
104 * to send to us.
105 */
106 uint32_t quota;
107
108};
109
110/**
111 * Encapsulation of all of the state of the plugin.
112 */
113struct Plugin
114{
115 /**
116 * Our environment.
117 */
118 struct GNUNET_TRANSPORT_PluginEnvironment *env;
119
120 /**
121 * List of open sessions.
122 */
123 struct Session *sessions;
124
125 /**
126 * Handle for the statistics service.
127 */
128 struct GNUNET_STATISTICS_Handle *statistics;
129
130};
131
132
133
134/**
135 * Function that can be used by the transport service to transmit
136 * a message using the plugin using a fresh connection (even if
137 * we already have a connection to this peer, this function is
138 * required to establish a new one).
139 *
140 * @param cls closure
141 * @param target who should receive this message
142 * @param msg1 first message to transmit
143 * @param msg2 second message to transmit (can be NULL)
144 * @param timeout how long until we give up?
145 * @param addr the address
146 * @param addrlen length of the address
147 * @return non-null session if the transmission has been scheduled
148 * NULL if the address format is invalid
149 */
150static void *
151template_plugin_send_to (void *cls,
152 const struct GNUNET_PeerIdentity *target,
153 const struct GNUNET_MessageHeader *msg1,
154 const struct GNUNET_MessageHeader *msg2,
155 struct GNUNET_TIME_Relative timeout,
156 const void *addr, size_t addrlen)
157{
158 // FIXME
159 return NULL;
160}
161
162
163/**
164 * Function that can be used by the transport service to transmit
165 * a message using the plugin.
166 *
167 * @param cls closure
168 * @param plugin_context value we were asked to pass to this plugin
169 * to respond to the given peer (use is optional,
170 * but may speed up processing), can be NULL
171 * @param service_context value passed to the transport-service
172 * to identify the neighbour
173 * @param target who should receive this message
174 * @param msg the message to transmit
175 * @param cont continuation to call once the message has
176 * been transmitted (or if the transport is ready
177 * for the next transmission call; or if the
178 * peer disconnected...)
179 * @param cont_cls closure for cont
180 * @return plugin_context that should be used next time for
181 * sending messages to the specified peer
182 */
183static void *
184template_plugin_send (void *cls,
185 void *plugin_context,
186 struct ReadyList *service_context,
187 const struct GNUNET_PeerIdentity *target,
188 const struct GNUNET_MessageHeader *msg,
189 struct GNUNET_TIME_Relative timeout,
190 GNUNET_TRANSPORT_TransmitContinuation cont,
191 void *cont_cls)
192{
193 // struct Plugin *plugin = cls;
194 return NULL;
195}
196
197
198
199/**
200 *
201 * @param cls closure
202 * @param plugin_context value we were asked to pass to this plugin
203 * to respond to the given peer (use is optional,
204 * but may speed up processing), can be NULL (if
205 * NULL was returned from the transmit function)
206 * @param service_context must correspond to the service context
207 * of the corresponding Transmit call; the plugin should
208 * not cancel a send call made with a different service
209 * context pointer! Never NULL.
210 * @param target peer for which the last transmission is
211 * to be cancelled
212 */
213static void
214template_plugin_cancel (void *cls,
215 void *plugin_context,
216 struct ReadyList *service_context,
217 const struct GNUNET_PeerIdentity *target)
218{
219 // struct Plugin *plugin = cls;
220 // FIXME
221}
222
223
224/**
225 * Convert the transports address to a nice, human-readable
226 * format.
227 *
228 * @param cls closure
229 * @param name name of the transport that generated the address
230 * @param addr one of the addresses of the host, NULL for the last address
231 * the specific address format depends on the transport
232 * @param addrlen length of the address
233 * @param numeric should (IP) addresses be displayed in numeric form?
234 * @param timeout after how long should we give up?
235 * @param asc function to call on each string
236 * @param asc_cls closure for asc
237 */
238static void
239template_plugin_address_pretty_printer (void *cls,
240 const char *type,
241 const void *addr,
242 size_t addrlen,
243 int numeric,
244 struct GNUNET_TIME_Relative timeout,
245 GNUNET_TRANSPORT_AddressStringCallback
246 asc, void *asc_cls)
247{
248 asc (asc_cls, NULL);
249}
250
251/**
252 * Set a quota for receiving data from the given peer; this is a
253 * per-transport limit. The transport should limit its read/select
254 * calls to stay below the quota (in terms of incoming data).
255 *
256 * @param cls closure
257 * @param peer the peer for whom the quota is given
258 * @param quota_in quota for receiving/sending data in bytes per ms
259 */
260static void
261template_plugin_set_receive_quota (void *cls,
262 const struct GNUNET_PeerIdentity *target,
263 uint32_t quota_in)
264{
265 // struct Plugin *plugin = cls;
266 // FIXME!
267}
268
269
270/**
271 * Another peer has suggested an address for this
272 * peer and transport plugin. Check that this could be a valid
273 * address. If so, consider adding it to the list
274 * of addresses.
275 *
276 * @param cls closure
277 * @param addr pointer to the address
278 * @param addrlen length of addr
279 * @return GNUNET_OK if this is a plausible address for this peer
280 * and transport
281 */
282static int
283template_plugin_address_suggested (void *cls,
284 const void *addr, size_t addrlen)
285{
286 // struct Plugin *plugin = cls;
287
288 /* check if the address is plausible; if so,
289 add it to our list! */
290 // FIXME!
291 return GNUNET_OK;
292}
293
294
295/**
296 * Entry point for the plugin.
297 */
298void *
299gnunet_plugin_transport_template_init (void *cls)
300{
301 struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
302 struct GNUNET_TRANSPORT_PluginFunctions *api;
303 struct Plugin *plugin;
304
305 plugin = GNUNET_malloc (sizeof (struct Plugin));
306 plugin->env = env;
307 plugin->statistics = NULL;
308 api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
309 api->cls = plugin;
310 api->send_to = &template_plugin_send_to;
311 api->send = &template_plugin_send;
312 api->cancel = &template_plugin_cancel;
313 api->address_pretty_printer = &template_plugin_address_pretty_printer;
314 api->set_receive_quota = &template_plugin_set_receive_quota;
315 api->address_suggested = &template_plugin_address_suggested;
316 api->cost_estimate = 42; // FIXME
317 return api;
318}
319
320
321/**
322 * Exit point from the plugin.
323 */
324void *
325gnunet_plugin_transport_template_done (void *cls)
326{
327 struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
328 struct Plugin *plugin = api->cls;
329
330 GNUNET_free (plugin);
331 GNUNET_free (api);
332 return NULL;
333}
334
335/* end of plugin_transport_template.c */
diff --git a/src/transport/plugin_transport_udp.c b/src/transport/plugin_transport_udp.c
new file mode 100644
index 000000000..ccaf9fbd1
--- /dev/null
+++ b/src/transport/plugin_transport_udp.c
@@ -0,0 +1,592 @@
1/*
2 This file is part of GNUnet
3 (C) 2001, 2002, 2003, 2004, 2005, 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file transports/udp.c
23 * @brief Implementation of the UDP transport service
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util.h"
29#include "gnunet_protocols.h"
30#include "gnunet_transport.h"
31#include "gnunet_stats_service.h"
32#include "gnunet_upnp_service.h"
33#include "ip.h"
34
35#define DEBUG_UDP GNUNET_YES
36
37/**
38 * The default maximum size of each outbound UDP message,
39 * optimal value for Ethernet (10 or 100 MBit).
40 */
41#define MESSAGE_SIZE 1472
42
43/**
44 * Message-Packet header.
45 */
46typedef struct
47{
48 /**
49 * size of the message, in bytes, including this header.
50 */
51 GNUNET_MessageHeader header;
52
53 /**
54 * What is the identity of the sender (GNUNET_hash of public key)
55 */
56 GNUNET_PeerIdentity sender;
57
58} UDPMessage;
59
60#define MY_TRANSPORT_NAME "UDP"
61#include "common.c"
62
63/* *********** globals ************* */
64
65static int stat_bytesReceived;
66
67static int stat_bytesSent;
68
69static int stat_bytesDropped;
70
71static int stat_udpConnected;
72
73/**
74 * thread that listens for inbound messages
75 */
76static struct GNUNET_SelectHandle *selector;
77
78/**
79 * the socket that we transmit all data with
80 */
81static struct GNUNET_SocketHandle *udp_sock;
82
83static struct GNUNET_LoadMonitor *load_monitor;
84
85
86/**
87 * The socket of session has data waiting, process!
88 *
89 * This function may only be called if the tcplock is
90 * already held by the caller.
91 */
92static int
93select_message_handler (void *mh_cls,
94 struct GNUNET_SelectHandle *sh,
95 struct GNUNET_SocketHandle *sock,
96 void *sock_ctx, const GNUNET_MessageHeader * msg)
97{
98 unsigned int len;
99 GNUNET_TransportPacket *mp;
100 const UDPMessage *um;
101
102 len = ntohs (msg->size);
103 if (len <= sizeof (UDPMessage))
104 {
105 GNUNET_GE_LOG (coreAPI->ectx,
106 GNUNET_GE_WARNING | GNUNET_GE_USER | GNUNET_GE_BULK,
107 _("Received malformed message via %s. Ignored.\n"),
108 "UDP");
109 return GNUNET_SYSERR;
110 }
111 um = (const UDPMessage *) msg;
112 mp = GNUNET_malloc (sizeof (GNUNET_TransportPacket));
113 mp->msg = GNUNET_malloc (len - sizeof (UDPMessage));
114 memcpy (mp->msg, &um[1], len - sizeof (UDPMessage));
115 mp->sender = um->sender;
116 mp->size = len - sizeof (UDPMessage);
117 mp->tsession = NULL;
118 coreAPI->receive (mp);
119 if (stats != NULL)
120 stats->change (stat_bytesReceived, len);
121 return GNUNET_OK;
122}
123
124static void *
125select_accept_handler (void *ah_cls,
126 struct GNUNET_SelectHandle *sh,
127 struct GNUNET_SocketHandle *sock,
128 const void *addr, unsigned int addr_len)
129{
130 static int nonnullpointer;
131
132 if (GNUNET_NO != is_rejected_tester (addr, addr_len))
133 return NULL;
134 return &nonnullpointer;
135}
136
137/**
138 * Select has been forced to close a connection.
139 * Free the associated context.
140 */
141static void
142select_close_handler (void *ch_cls,
143 struct GNUNET_SelectHandle *sh,
144 struct GNUNET_SocketHandle *sock, void *sock_ctx)
145{
146 /* do nothing */
147}
148
149/**
150 * Establish a connection to a remote node.
151 *
152 * @param hello the hello-Message for the target node
153 * @param tsessionPtr the session handle that is to be set
154 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
155 */
156static int
157udp_connect (const GNUNET_MessageHello * hello,
158 GNUNET_TSession ** tsessionPtr, int may_reuse)
159{
160 GNUNET_TSession *tsession;
161
162 tsession = GNUNET_malloc (sizeof (GNUNET_TSession));
163 memset (tsession, 0, sizeof (GNUNET_TSession));
164 tsession->internal = GNUNET_malloc (GNUNET_sizeof_hello (hello));
165 memcpy (tsession->internal, hello, GNUNET_sizeof_hello (hello));
166 tsession->ttype = myAPI.protocol_number;
167 tsession->peer = hello->senderIdentity;
168 *tsessionPtr = tsession;
169 if (stats != NULL)
170 stats->change (stat_udpConnected, 1);
171 return GNUNET_OK;
172}
173
174/**
175 * A (core) Session is to be associated with a transport session. The
176 * transport service may want to know in order to call back on the
177 * core if the connection is being closed.
178 *
179 * @param tsession the session handle passed along
180 * from the call to receive that was made by the transport
181 * layer
182 * @return GNUNET_OK if the session could be associated,
183 * GNUNET_SYSERR if not.
184 */
185int
186udp_associate (GNUNET_TSession * tsession)
187{
188 return GNUNET_SYSERR; /* UDP connections can never be associated */
189}
190
191/**
192 * Disconnect from a remote node.
193 *
194 * @param tsession the session that is closed
195 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
196 */
197static int
198udp_disconnect (GNUNET_TSession * tsession)
199{
200 if (tsession != NULL)
201 {
202 if (tsession->internal != NULL)
203 GNUNET_free (tsession->internal);
204 GNUNET_free (tsession);
205 if (stats != NULL)
206 stats->change (stat_udpConnected, -1);
207 }
208 return GNUNET_OK;
209}
210
211/**
212 * Shutdown the server process (stop receiving inbound traffic). Maybe
213 * restarted later!
214 */
215static int
216udp_transport_server_stop ()
217{
218 GNUNET_GE_ASSERT (coreAPI->ectx, udp_sock != NULL);
219 if (selector != NULL)
220 {
221 GNUNET_select_destroy (selector);
222 selector = NULL;
223 }
224 GNUNET_socket_destroy (udp_sock);
225 udp_sock = NULL;
226 return GNUNET_OK;
227}
228
229/**
230 * Test if the transport would even try to send
231 * a message of the given size and importance
232 * for the given session.<br>
233 * This function is used to check if the core should
234 * even bother to construct (and encrypt) this kind
235 * of message.
236 *
237 * @return GNUNET_YES if the transport would try (i.e. queue
238 * the message or call the OS to send),
239 * GNUNET_NO if the transport would just drop the message,
240 * GNUNET_SYSERR if the size/session is invalid
241 */
242static int
243udp_test_would_try (GNUNET_TSession * tsession, unsigned int size,
244 int important)
245{
246 const GNUNET_MessageHello *hello;
247
248 if (udp_sock == NULL)
249 return GNUNET_SYSERR;
250 if (size == 0)
251 {
252 GNUNET_GE_BREAK (coreAPI->ectx, 0);
253 return GNUNET_SYSERR;
254 }
255 if (size > myAPI.mtu)
256 {
257 GNUNET_GE_BREAK (coreAPI->ectx, 0);
258 return GNUNET_SYSERR;
259 }
260 hello = (const GNUNET_MessageHello *) tsession->internal;
261 if (hello == NULL)
262 return GNUNET_SYSERR;
263 return GNUNET_YES;
264}
265
266/**
267 * Create a UDP socket. If possible, use IPv6, otherwise
268 * try IPv4. Update available_protocols accordingly.
269 */
270static int
271udp_create_socket ()
272{
273 int s;
274
275 available_protocols = VERSION_AVAILABLE_NONE;
276 s = -1;
277 if (GNUNET_YES !=
278 GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD", "DISABLE-IPV6",
279 GNUNET_YES))
280 {
281#ifndef MINGW
282 s = SOCKET (PF_INET6, SOCK_DGRAM, 17);
283#else
284 s = win_ols_socket (PF_INET6, SOCK_DGRAM, 17);
285#endif
286 }
287 if (s < 0)
288 {
289#ifndef MINGW
290 s = SOCKET (PF_INET, SOCK_DGRAM, 17);
291#else
292 s = win_ols_socket (PF_INET, SOCK_DGRAM, 17);
293#endif
294 if (s < 0)
295 {
296 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
297 GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
298 GNUNET_GE_BULK, "socket");
299 return GNUNET_SYSERR;
300 }
301 available_protocols = VERSION_AVAILABLE_IPV4;
302 }
303 else
304 {
305 available_protocols = VERSION_AVAILABLE_IPV6 | VERSION_AVAILABLE_IPV4;
306 }
307 return s;
308}
309
310/**
311 * Send a message to the specified remote node.
312 *
313 * @param tsession the GNUNET_MessageHello identifying the remote node
314 * @param message what to send
315 * @param size the size of the message
316 * @return GNUNET_SYSERR on error, GNUNET_OK on success
317 */
318static int
319udp_send (GNUNET_TSession * tsession,
320 const void *message, const unsigned int size, int important)
321{
322 const GNUNET_MessageHello *hello;
323 const HostAddress *haddr;
324 UDPMessage *mp;
325 struct sockaddr_in serverAddrv4;
326 struct sockaddr_in6 serverAddrv6;
327 struct sockaddr *serverAddr;
328 socklen_t addrlen;
329 unsigned short available;
330 int ok;
331 int ssize;
332 size_t sent;
333
334 GNUNET_GE_ASSERT (NULL, tsession != NULL);
335 if (udp_sock == NULL)
336 return GNUNET_SYSERR;
337 if (size == 0)
338 {
339 GNUNET_GE_BREAK (coreAPI->ectx, 0);
340 return GNUNET_SYSERR;
341 }
342 if (size > myAPI.mtu)
343 {
344 GNUNET_GE_BREAK (coreAPI->ectx, 0);
345 return GNUNET_SYSERR;
346 }
347 hello = (const GNUNET_MessageHello *) tsession->internal;
348 if (hello == NULL)
349 return GNUNET_SYSERR;
350
351 haddr = (const HostAddress *) &hello[1];
352 available = ntohs (haddr->availability) & available_protocols;
353 if (available == VERSION_AVAILABLE_NONE)
354 return GNUNET_SYSERR;
355 if (available == (VERSION_AVAILABLE_IPV4 | VERSION_AVAILABLE_IPV6))
356 {
357 if (GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 2) == 0)
358 available = VERSION_AVAILABLE_IPV4;
359 else
360 available = VERSION_AVAILABLE_IPV6;
361 }
362 ssize = size + sizeof (UDPMessage);
363 mp = GNUNET_malloc (ssize);
364 mp->header.size = htons (ssize);
365 mp->header.type = 0;
366 mp->sender = *(coreAPI->my_identity);
367 memcpy (&mp[1], message, size);
368 ok = GNUNET_SYSERR;
369
370 if ((available & VERSION_AVAILABLE_IPV4) > 0)
371 {
372 memset (&serverAddrv4, 0, sizeof (serverAddrv4));
373 serverAddrv4.sin_family = AF_INET;
374 serverAddrv4.sin_port = haddr->port;
375 memcpy (&serverAddrv4.sin_addr, &haddr->ipv4, sizeof (struct in_addr));
376 addrlen = sizeof (serverAddrv4);
377 serverAddr = (struct sockaddr *) &serverAddrv4;
378 }
379 else
380 {
381 memset (&serverAddrv6, 0, sizeof (serverAddrv6));
382 serverAddrv6.sin6_family = AF_INET;
383 serverAddrv6.sin6_port = haddr->port;
384 memcpy (&serverAddrv6.sin6_addr, &haddr->ipv6,
385 sizeof (struct in6_addr));
386 addrlen = sizeof (serverAddrv6);
387 serverAddr = (struct sockaddr *) &serverAddrv6;
388 }
389#ifndef MINGW
390 if (GNUNET_YES == GNUNET_socket_send_to (udp_sock,
391 GNUNET_NC_NONBLOCKING,
392 mp,
393 ssize, &sent,
394 (const char *) serverAddr,
395 addrlen))
396#else
397 sent =
398 win_ols_sendto (udp_sock, mp, ssize, (const char *) serverAddr, addrlen);
399 if (sent != SOCKET_ERROR)
400#endif
401 {
402 ok = GNUNET_OK;
403 if (stats != NULL)
404 stats->change (stat_bytesSent, sent);
405 }
406 else
407 {
408 if (stats != NULL)
409 stats->change (stat_bytesDropped, ssize);
410 }
411 GNUNET_free (mp);
412 return ok;
413}
414
415/**
416 * Start the server process to receive inbound traffic.
417 *
418 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
419 */
420static int
421udp_transport_server_start ()
422{
423 struct sockaddr_in serverAddrv4;
424 struct sockaddr_in6 serverAddrv6;
425 struct sockaddr *serverAddr;
426 socklen_t addrlen;
427 int sock;
428 const int on = 1;
429 unsigned short port;
430
431 GNUNET_GE_ASSERT (coreAPI->ectx, selector == NULL);
432 /* initialize UDP network */
433 port = get_port ();
434 if (port != 0)
435 {
436 sock = udp_create_socket ();
437 if (sock < 0)
438 return GNUNET_SYSERR;
439 if (SETSOCKOPT (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
440 {
441 GNUNET_GE_DIE_STRERROR (coreAPI->ectx,
442 GNUNET_GE_FATAL | GNUNET_GE_ADMIN |
443 GNUNET_GE_IMMEDIATE, "setsockopt");
444 return GNUNET_SYSERR;
445 }
446 if (available_protocols == VERSION_AVAILABLE_IPV4)
447 {
448 memset (&serverAddrv4, 0, sizeof (serverAddrv4));
449 serverAddrv4.sin_family = AF_INET;
450 serverAddrv4.sin_addr.s_addr = INADDR_ANY;
451 serverAddrv4.sin_port = htons (port);
452 addrlen = sizeof (serverAddrv4);
453 serverAddr = (struct sockaddr *) &serverAddrv4;
454 }
455 else
456 {
457 memset (&serverAddrv6, 0, sizeof (serverAddrv6));
458 serverAddrv6.sin6_family = AF_INET6;
459 serverAddrv6.sin6_addr = in6addr_any;
460 serverAddrv6.sin6_port = htons (port);
461 addrlen = sizeof (serverAddrv6);
462 serverAddr = (struct sockaddr *) &serverAddrv6;
463 }
464 if (BIND (sock, serverAddr, addrlen) < 0)
465 {
466 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
467 GNUNET_GE_FATAL | GNUNET_GE_ADMIN |
468 GNUNET_GE_IMMEDIATE, "bind");
469 GNUNET_GE_LOG (coreAPI->ectx,
470 GNUNET_GE_FATAL | GNUNET_GE_ADMIN |
471 GNUNET_GE_IMMEDIATE,
472 _("Failed to bind to %s port %d.\n"),
473 MY_TRANSPORT_NAME, port);
474 if (0 != CLOSE (sock))
475 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
476 GNUNET_GE_ERROR | GNUNET_GE_USER |
477 GNUNET_GE_ADMIN | GNUNET_GE_BULK,
478 "close");
479 return GNUNET_SYSERR;
480 }
481 selector = GNUNET_select_create ("udp", GNUNET_YES, coreAPI->ectx, load_monitor, sock, addrlen, 0, /* timeout */
482 &select_message_handler,
483 NULL,
484 &select_accept_handler,
485 NULL,
486 &select_close_handler,
487 NULL, 64 * 1024,
488 16 /* max sockets */ );
489 if (selector == NULL)
490 return GNUNET_SYSERR;
491 }
492 sock = udp_create_socket ();
493 if (sock == -1)
494 {
495 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
496 GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
497 GNUNET_GE_BULK, "socket");
498 GNUNET_select_destroy (selector);
499 selector = NULL;
500 return GNUNET_SYSERR;
501 }
502 udp_sock = GNUNET_socket_create (coreAPI->ectx, load_monitor, sock);
503 GNUNET_GE_ASSERT (coreAPI->ectx, udp_sock != NULL);
504 return GNUNET_OK;
505}
506
507/**
508 * The exported method. Makes the core api available via a global and
509 * returns the udp transport API.
510 */
511GNUNET_TransportAPI *
512inittransport_udp (GNUNET_CoreAPIForTransport * core)
513{
514 unsigned long long mtu;
515
516 cfg = core->cfg;
517 load_monitor = core->load_monitor;
518 GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (UDPMessage) == 68);
519 GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (HostAddress) == 24);
520 coreAPI = core;
521 if (-1 == GNUNET_GC_get_configuration_value_number (cfg,
522 "UDP",
523 "MTU",
524 sizeof (UDPMessage)
525 +
526 GNUNET_P2P_MESSAGE_OVERHEAD
527 +
528 sizeof
529 (GNUNET_MessageHeader) +
530 32, 65500,
531 MESSAGE_SIZE, &mtu))
532 {
533 return NULL;
534 }
535 if (mtu < 1200)
536 GNUNET_GE_LOG (coreAPI->ectx,
537 GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE,
538 _("MTU %llu for `%s' is probably too low!\n"), mtu, "UDP");
539 lock = GNUNET_mutex_create (GNUNET_NO);
540 if (0 !=
541 GNUNET_GC_attach_change_listener (cfg, &reload_configuration, NULL))
542 {
543 GNUNET_mutex_destroy (lock);
544 lock = NULL;
545 return NULL;
546 }
547 if (GNUNET_GC_get_configuration_value_yesno (cfg, "UDP", "UPNP", GNUNET_YES)
548 == GNUNET_YES)
549 {
550 upnp = coreAPI->service_request ("upnp");
551 if (upnp == NULL)
552 GNUNET_GE_LOG (coreAPI->ectx,
553 GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE,
554 "The UPnP service could not be loaded. To disable UPnP, set the "
555 "configuration option \"UPNP\" in section \"%s\" to \"NO\"\n",
556 "UDP");
557 }
558 stats = coreAPI->service_request ("stats");
559 if (stats != NULL)
560 {
561 stat_bytesReceived
562 = stats->create (gettext_noop ("# bytes received via UDP"));
563 stat_bytesSent = stats->create (gettext_noop ("# bytes sent via UDP"));
564 stat_bytesDropped
565 = stats->create (gettext_noop ("# bytes dropped by UDP (outgoing)"));
566 stat_udpConnected
567 = stats->create (gettext_noop ("# UDP connections (right now)"));
568 }
569 myAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_UDP;
570 myAPI.mtu = mtu - sizeof (UDPMessage);
571 myAPI.cost = 20000;
572 myAPI.hello_verify = &verify_hello;
573 myAPI.hello_create = &create_hello;
574 myAPI.connect = &udp_connect;
575 myAPI.send = &udp_send;
576 myAPI.associate = &udp_associate;
577 myAPI.disconnect = &udp_disconnect;
578 myAPI.server_start = &udp_transport_server_start;
579 myAPI.server_stop = &udp_transport_server_stop;
580 myAPI.hello_to_address = &hello_to_address;
581 myAPI.send_now_test = &udp_test_would_try;
582
583 return &myAPI;
584}
585
586void
587donetransport_udp ()
588{
589 do_shutdown ();
590}
591
592/* end of udp.c */
diff --git a/src/transport/test_transport_api.c b/src/transport/test_transport_api.c
new file mode 100644
index 000000000..02c28b09c
--- /dev/null
+++ b/src/transport/test_transport_api.c
@@ -0,0 +1,305 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file transport/test_transport_api.c
22 * @brief testcase for transport_api.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_hello_lib.h"
27#include "gnunet_getopt_lib.h"
28#include "gnunet_os_lib.h"
29#include "gnunet_program_lib.h"
30#include "gnunet_scheduler_lib.h"
31#include "gnunet_transport_service.h"
32#include "transport.h"
33
34#define VERBOSE GNUNET_NO
35
36#define START_ARM GNUNET_YES
37
38/**
39 * How long until we give up on transmitting the message?
40 */
41#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
42
43#define MTYPE 12345
44
45struct PeerContext
46{
47 struct GNUNET_CONFIGURATION_Handle *cfg;
48 struct GNUNET_TRANSPORT_Handle *th;
49 struct GNUNET_PeerIdentity id;
50#if START_ARM
51 pid_t arm_pid;
52#endif
53};
54
55static struct PeerContext p1;
56
57static struct PeerContext p2;
58
59static struct GNUNET_SCHEDULER_Handle *sched;
60
61static int ok;
62
63#if VERBOSE
64#define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
65#else
66#define OKPP do { ok++; } while (0)
67#endif
68
69
70static void
71end ()
72{
73 /* do work here */
74 GNUNET_assert (ok == 8);
75 GNUNET_TRANSPORT_disconnect (p1.th);
76 GNUNET_TRANSPORT_disconnect (p2.th);
77 ok = 0;
78}
79
80
81/**
82 * Function called by the transport for each received message.
83 *
84 * @param cls closure
85 * @param latency estimated latency for communicating with the
86 * given peer
87 * @param peer (claimed) identity of the other peer
88 * @param message the message
89 */
90static void
91notify_receive (void *cls,
92 struct GNUNET_TIME_Relative latency,
93 const struct GNUNET_PeerIdentity *peer,
94 const struct GNUNET_MessageHeader *message)
95{
96 GNUNET_assert (ok == 7);
97 OKPP;
98 GNUNET_assert (MTYPE == ntohs (message->type));
99 GNUNET_assert (sizeof (struct GNUNET_MessageHeader) ==
100 ntohs (message->size));
101 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received message from peer (%p)!\n",
102 cls);
103 end ();
104}
105
106
107/**
108 * Function called to notify transport users that another
109 * peer connected to us.
110 *
111 * @param cls closure
112 * @param transport the transport service handle
113 * @param peer the peer that disconnected
114 * @param latency current latency of the connection
115 */
116static void
117notify_connect (void *cls,
118 const struct GNUNET_PeerIdentity *peer,
119 struct GNUNET_TIME_Relative latency)
120{
121 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer connected to us (%p)!\n", cls);
122 GNUNET_assert ((ok >= 1) && (ok <= 6));
123 OKPP;
124}
125
126
127/**
128 * Function called to notify transport users that another
129 * peer disconnected from us.
130 *
131 * @param cls closure
132 * @param transport the transport service handle
133 * @param peer the peer that disconnected
134 */
135static void
136notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
137{
138 GNUNET_assert (0);
139}
140
141
142static void
143setup_peer (struct PeerContext *p, const char *cfgname)
144{
145 p->cfg = GNUNET_CONFIGURATION_create ();
146#if START_ARM
147 p->arm_pid = GNUNET_OS_start_process ("gnunet-service-arm",
148 "gnunet-service-arm",
149#if VERBOSE
150 "-L", "DEBUG",
151#endif
152 "-c", cfgname, NULL);
153 sleep (1); /* allow ARM to start */
154#endif
155 GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
156 p->th = GNUNET_TRANSPORT_connect (sched, p->cfg,
157 p,
158 &notify_receive,
159 &notify_connect, &notify_disconnect);
160 GNUNET_assert (p->th != NULL);
161}
162
163
164static size_t
165notify_ready (void *cls, size_t size, void *buf)
166{
167 struct GNUNET_MessageHeader *hdr;
168
169 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
170 "Transmitting message to peer (%p) - %u!\n", cls, size);
171 GNUNET_assert (size >= 256);
172 GNUNET_assert ((ok >= 5) && (ok <= 6));
173 OKPP;
174 hdr = buf;
175 hdr->size = htons (sizeof (struct GNUNET_MessageHeader));
176 hdr->type = htons (MTYPE);
177 return sizeof (struct GNUNET_MessageHeader);
178}
179
180
181static void
182exchange_hello_last (void *cls,
183 struct GNUNET_TIME_Relative latency,
184 const struct GNUNET_PeerIdentity *peer,
185 const struct GNUNET_MessageHeader *message)
186{
187 struct PeerContext *me = cls;
188 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk;
189
190 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
191 "Exchanging HELLO with peer (%p)!\n", cls);
192 GNUNET_assert (ok >= 3);
193 OKPP;
194 GNUNET_assert (message != NULL);
195 GNUNET_assert (GNUNET_OK ==
196 GNUNET_HELLO_get_key ((const struct GNUNET_HELLO_Message *)
197 message, &pk));
198 GNUNET_CRYPTO_hash (&pk,
199 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
200 &me->id.hashPubKey);
201 GNUNET_TRANSPORT_offer_hello (p1.th, message);
202 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
203 "Finished exchanging HELLOs, now waiting for transmission!\n");
204 /* both HELLOs exchanged, get ready to test transmission! */
205 GNUNET_TRANSPORT_notify_transmit_ready (p1.th,
206 &p2.id,
207 256, TIMEOUT, &notify_ready, &p1);
208}
209
210
211static void
212exchange_hello (void *cls,
213 struct GNUNET_TIME_Relative latency,
214 const struct GNUNET_PeerIdentity *peer,
215 const struct GNUNET_MessageHeader *message)
216{
217 struct PeerContext *me = cls;
218 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk;
219
220 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
221 "Exchanging HELLO with peer (%p)!\n", cls);
222 GNUNET_assert (ok >= 2);
223 OKPP;
224 GNUNET_assert (message != NULL);
225 GNUNET_assert (GNUNET_OK ==
226 GNUNET_HELLO_get_key ((const struct GNUNET_HELLO_Message *)
227 message, &pk));
228 GNUNET_CRYPTO_hash (&pk,
229 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
230 &me->id.hashPubKey);
231 GNUNET_TRANSPORT_get_hello (p2.th, GNUNET_TIME_UNIT_MINUTES,
232 &exchange_hello_last, &p2);
233}
234
235
236static void
237run (void *cls,
238 struct GNUNET_SCHEDULER_Handle *s,
239 char *const *args,
240 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
241{
242 GNUNET_assert (ok == 1);
243 OKPP;
244 sched = s;
245 setup_peer (&p1, "test_transport_api_peer1.conf");
246 setup_peer (&p2, "test_transport_api_peer2.conf");
247 GNUNET_TRANSPORT_get_hello (p1.th,
248 GNUNET_TIME_UNIT_MINUTES, &exchange_hello, &p1);
249}
250
251
252static void
253stop_arm (struct PeerContext *p)
254{
255#if START_ARM
256 if (0 != PLIBC_KILL (p->arm_pid, SIGTERM))
257 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
258 waitpid (p->arm_pid, NULL, 0);
259#endif
260 GNUNET_CONFIGURATION_destroy (p->cfg);
261}
262
263
264static int
265check ()
266{
267 char *const argv[] = { "test-transport-api",
268 "-c",
269 "test_transport_api_data.conf",
270#if VERBOSE
271 "-L", "DEBUG",
272#endif
273 NULL
274 };
275 struct GNUNET_GETOPT_CommandLineOption options[] = {
276 GNUNET_GETOPT_OPTION_END
277 };
278
279 ok = 1;
280 GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
281 argv, "test-transport-api", "nohelp",
282 options, &run, &ok);
283 stop_arm (&p1);
284 stop_arm (&p2);
285 return ok;
286}
287
288int
289main (int argc, char *argv[])
290{
291 int ret;
292
293 GNUNET_log_setup ("test-transport-api",
294#if VERBOSE
295 "DEBUG",
296#else
297 "WARNING",
298#endif
299 NULL);
300 ret = check ();
301
302 return ret;
303}
304
305/* end of test_transport_api.c */
diff --git a/src/transport/test_transport_api_data.conf b/src/transport/test_transport_api_data.conf
new file mode 100644
index 000000000..0fa611350
--- /dev/null
+++ b/src/transport/test_transport_api_data.conf
@@ -0,0 +1,24 @@
1[PATHS]
2SERVICEHOME = /tmp/test-gnunetd-transport-master/
3
4[resolver]
5PORT = 2364
6
7[transport]
8PORT = 2365
9PLUGINS = tcp
10
11[arm]
12PORT = 2366
13
14[statistics]
15PORT = 2367
16
17[tcp]
18PORT = 2368
19
20[peerinfo]
21PORT = 2369
22
23[testing]
24WEAKRANDOM = YES
diff --git a/src/transport/test_transport_api_peer1.conf b/src/transport/test_transport_api_peer1.conf
new file mode 100644
index 000000000..dcc0ab4cf
--- /dev/null
+++ b/src/transport/test_transport_api_peer1.conf
@@ -0,0 +1,25 @@
1[PATHS]
2SERVICEHOME = /tmp/test-gnunetd-transport-peer-1/
3DEFAULTCONFIG = test_transport_api_peer1.conf
4
5[resolver]
6PORT = 12364
7
8[transport]
9PORT = 12365
10PLUGINS = tcp
11
12[arm]
13PORT = 12366
14
15[statistics]
16PORT = 12367
17
18[tcp]
19PORT = 12368
20
21[peerinfo]
22PORT = 12369
23
24[testing]
25WEAKRANDOM = YES
diff --git a/src/transport/test_transport_api_peer2.conf b/src/transport/test_transport_api_peer2.conf
new file mode 100644
index 000000000..8567c6ac8
--- /dev/null
+++ b/src/transport/test_transport_api_peer2.conf
@@ -0,0 +1,25 @@
1[PATHS]
2SERVICEHOME = /tmp/test-gnunetd-transport-peer-2/
3DEFAULTCONFIG = test_transport_api_peer2.conf
4
5[resolver]
6PORT = 22364
7
8[transport]
9PORT = 22365
10PLUGINS = tcp
11
12[arm]
13PORT = 22366
14
15[statistics]
16PORT = 22367
17
18[tcp]
19PORT = 22368
20
21[peerinfo]
22PORT = 22369
23
24[testing]
25WEAKRANDOM = YES
diff --git a/src/transport/transport.h b/src/transport/transport.h
new file mode 100644
index 000000000..8e1291005
--- /dev/null
+++ b/src/transport/transport.h
@@ -0,0 +1,238 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file transport/transport.h
23 * @brief common internal definitions for transport service
24 * @author Christian Grothoff
25 */
26#include "gnunet_crypto_lib.h"
27#include "gnunet_time_lib.h"
28#include "gnunet_transport_service.h"
29
30#define DEBUG_TRANSPORT GNUNET_NO
31
32/**
33 * For how long do we allow unused bandwidth
34 * from the past to carry over into the future? (in ms)
35 */
36#define MAX_BANDWIDTH_CARRY 5000
37
38/**
39 * How often do we (at most) do a full quota
40 * recalculation? (in ms)
41 */
42#define MIN_QUOTA_REFRESH_TIME 2000
43
44/**
45 * Message from the transport service to the library
46 * informing about neighbors.
47 */
48struct ConnectInfoMessage
49{
50
51 /**
52 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT
53 */
54 struct GNUNET_MessageHeader header;
55
56 /**
57 * Current quota for outbound traffic in bytes/ms.
58 * (should be equal to system default)
59 */
60 uint32_t quota_out GNUNET_PACKED;
61
62 /**
63 * Latency estimate.
64 */
65 struct GNUNET_TIME_RelativeNBO latency;
66
67 /**
68 * Identity of the new neighbour.
69 */
70 struct GNUNET_PeerIdentity id;
71
72};
73
74
75/**
76 * Message from the transport service to the library
77 * informing about disconnects.
78 */
79struct DisconnectInfoMessage
80{
81
82 /**
83 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT
84 */
85 struct GNUNET_MessageHeader header;
86
87 /**
88 * Reserved, always zero.
89 */
90 uint32_t reserved GNUNET_PACKED;
91
92 /**
93 * Who got disconnected?
94 */
95 struct GNUNET_PeerIdentity peer;
96
97};
98
99
100/**
101 * Message used to set a particular bandwidth quota. Send
102 * TO the service to set an incoming quota, send FROM the
103 * service to update an outgoing quota.
104 */
105struct QuotaSetMessage
106{
107
108 /**
109 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_NEIGHBOUR_INFO
110 */
111 struct GNUNET_MessageHeader header;
112
113 /**
114 * Quota in bytes per ms, 0 to drop everything;
115 * in network byte order.
116 */
117 uint32_t quota_in GNUNET_PACKED;
118
119 /**
120 * About which peer are we talking here?
121 */
122 struct GNUNET_PeerIdentity peer;
123
124};
125
126
127/**
128 * Message used to ask the transport service to connect
129 * to a particular peer.
130 */
131struct TryConnectMessage
132{
133
134 /**
135 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_TRY_CONNECT.
136 */
137 struct GNUNET_MessageHeader header;
138
139 /**
140 * Always zero.
141 */
142 uint32_t reserved GNUNET_PACKED;
143
144 /**
145 * About which peer are we talking here?
146 */
147 struct GNUNET_PeerIdentity peer;
148
149};
150
151/**
152 * Message used to notify the transport API about a message
153 * received from the network. The actual message follows.
154 */
155struct InboundMessage
156{
157
158 /**
159 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_RECV
160 */
161 struct GNUNET_MessageHeader header;
162
163 /**
164 * Always zero.
165 */
166 uint32_t reserved GNUNET_PACKED;
167
168 /**
169 * Latency estimate.
170 */
171 struct GNUNET_TIME_RelativeNBO latency;
172
173 /**
174 * Which peer sent the message?
175 */
176 struct GNUNET_PeerIdentity peer;
177
178};
179
180
181/**
182 * Message used to notify the transport API that it can
183 * send another message to the transport service.
184 */
185struct SendOkMessage
186{
187
188 /**
189 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK
190 */
191 struct GNUNET_MessageHeader header;
192
193 /**
194 * GNUNET_OK if the transmission succeeded,
195 * GNUNET_SYSERR if it failed (i.e. network disconnect);
196 * in either case, it is now OK for this client to
197 * send us another message for the given peer.
198 */
199 uint32_t success GNUNET_PACKED;
200
201 /**
202 * Which peer can send more now?
203 */
204 struct GNUNET_PeerIdentity peer;
205
206};
207
208
209/**
210 * Message used to notify the transport service about a message
211 * to be transmitted to another peer. The actual message follows.
212 */
213struct OutboundMessage
214{
215
216 /**
217 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_SEND
218 */
219 struct GNUNET_MessageHeader header;
220
221 /**
222 * Always zero.
223 */
224 uint32_t reserved GNUNET_PACKED;
225
226 /**
227 * Which peer should receive the message?
228 */
229 struct GNUNET_PeerIdentity peer;
230
231};
232
233
234
235
236
237
238/* end of transport.h */
diff --git a/src/transport/transport_api.c b/src/transport/transport_api.c
new file mode 100644
index 000000000..d6d4e2a96
--- /dev/null
+++ b/src/transport/transport_api.c
@@ -0,0 +1,1863 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file transport/transport_api.c
23 * @brief library to access the low-level P2P IO service
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - set_quota with low bandwidth should cause peer
28 * disconnects (currently never does that) (MINOR)
29 */
30#include "platform.h"
31#include "gnunet_client_lib.h"
32#include "gnunet_arm_service.h"
33#include "gnunet_hello_lib.h"
34#include "gnunet_protocols.h"
35#include "gnunet_server_lib.h"
36#include "gnunet_time_lib.h"
37#include "gnunet_transport_service.h"
38#include "transport.h"
39
40/**
41 * After how long do we give up on transmitting a HELLO
42 * to the service?
43 */
44#define OFFER_HELLO_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
45
46/**
47 * How long should ARM wait when starting up the
48 * transport service before reporting back?
49 */
50#define START_SERVICE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
51
52/**
53 * How long should ARM wait when stopping the
54 * transport service before reporting back?
55 */
56#define STOP_SERVICE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
57
58/**
59 * Entry in linked list of all of our current neighbours.
60 */
61struct NeighbourList
62{
63
64 /**
65 * This is a linked list.
66 */
67 struct NeighbourList *next;
68
69 /**
70 * Active transmit handle, can be NULL. Used to move
71 * from ready to wait list on disconnect and to block
72 * two transmissions to the same peer from being scheduled
73 * at the same time.
74 */
75 struct GNUNET_TRANSPORT_TransmitHandle *transmit_handle;
76
77
78 /**
79 * Identity of this neighbour.
80 */
81 struct GNUNET_PeerIdentity id;
82
83 /**
84 * At what time did we reset last_sent last?
85 */
86 struct GNUNET_TIME_Absolute last_quota_update;
87
88 /**
89 * How many bytes have we sent since the "last_quota_update"
90 * timestamp?
91 */
92 uint64_t last_sent;
93
94 /**
95 * Global quota for outbound traffic to the neighbour in bytes/ms.
96 */
97 uint32_t quota_out;
98
99 /**
100 * Set to GNUNET_YES if we are currently allowed to
101 * transmit a message to the transport service for this
102 * peer, GNUNET_NO otherwise.
103 */
104 int transmit_ok;
105
106 /**
107 * Set to GNUNET_YES if we have received an ACK for the
108 * given peer. Peers that receive our HELLO always respond
109 * with an ACK to let us know that we are successfully
110 * communicating. Note that a PING can not be used for this
111 * since PINGs are only send if a HELLO address requires
112 * confirmation (and also, PINGs are not passed to the
113 * transport API itself).
114 */
115 int received_ack;
116
117};
118
119
120/**
121 * Linked list of requests from clients for our HELLO
122 * that were deferred.
123 */
124struct HelloWaitList
125{
126
127 /**
128 * This is a linked list.
129 */
130 struct HelloWaitList *next;
131
132 /**
133 * Reference back to our transport handle.
134 */
135 struct GNUNET_TRANSPORT_Handle *handle;
136
137 /**
138 * Callback to call once we got our HELLO.
139 */
140 GNUNET_TRANSPORT_ReceiveCallback rec;
141
142 /**
143 * Closure for rec.
144 */
145 void *rec_cls;
146
147 /**
148 * When to time out (call rec with NULL).
149 */
150 struct GNUNET_TIME_Absolute timeout;
151
152 /**
153 * Timeout task (used to trigger timeout,
154 * cancel if we get the HELLO in time).
155 */
156 GNUNET_SCHEDULER_TaskIdentifier task;
157
158
159};
160
161
162/**
163 * Opaque handle for a transmission-ready request.
164 */
165struct GNUNET_TRANSPORT_TransmitHandle
166{
167
168 /**
169 * We keep the transmit handles that are waiting for
170 * a transport-level connection in a doubly linked list.
171 */
172 struct GNUNET_TRANSPORT_TransmitHandle *next;
173
174 /**
175 * We keep the transmit handles that are waiting for
176 * a transport-level connection in a doubly linked list.
177 */
178 struct GNUNET_TRANSPORT_TransmitHandle *prev;
179
180 /**
181 * Handle of the main transport data structure.
182 */
183 struct GNUNET_TRANSPORT_Handle *handle;
184
185 /**
186 * Neighbour for this handle, can be NULL if the service
187 * is not yet connected to the target.
188 */
189 struct NeighbourList *neighbour;
190
191 /**
192 * Which peer is this transmission going to be for? All
193 * zeros if it is control-traffic to the service.
194 */
195 struct GNUNET_PeerIdentity target;
196
197 /**
198 * Function to call when notify_size bytes are available
199 * for transmission.
200 */
201 GNUNET_NETWORK_TransmitReadyNotify notify;
202
203 /**
204 * Closure for notify.
205 */
206 void *notify_cls;
207
208 /**
209 * transmit_ready task Id. The task is used to introduce
210 * the artificial delay that may be required to maintain
211 * the bandwidth limits.
212 */
213 GNUNET_SCHEDULER_TaskIdentifier notify_delay_task;
214
215 /**
216 * Timeout for this request.
217 */
218 struct GNUNET_TIME_Absolute timeout;
219
220 /**
221 * How many bytes is our notify callback waiting for?
222 */
223 size_t notify_size;
224
225};
226
227
228/**
229 * Handle for the transport service (includes all of the
230 * state for the transport service).
231 */
232struct GNUNET_TRANSPORT_Handle
233{
234
235 /**
236 * Closure for the callbacks.
237 */
238 void *cls;
239
240 /**
241 * Function to call for received data.
242 */
243 GNUNET_TRANSPORT_ReceiveCallback rec;
244
245 /**
246 * function to call on connect events
247 */
248 GNUNET_TRANSPORT_NotifyConnect nc_cb;
249
250 /**
251 * function to call on disconnect events
252 */
253 GNUNET_TRANSPORT_NotifyDisconnect nd_cb;
254
255 /**
256 * The current HELLO message for this peer. Updated
257 * whenever transports change their addresses.
258 */
259 struct GNUNET_HELLO_Message *my_hello;
260
261 /**
262 * My client connection to the transport service.
263 */
264 struct GNUNET_CLIENT_Connection *client;
265
266 /**
267 * Handle to our registration with the client for notification.
268 */
269 struct GNUNET_NETWORK_TransmitHandle *network_handle;
270
271 /**
272 * Linked list of transmit handles that are waiting for the
273 * transport to connect to the respective peer. When we
274 * receive notification that the transport connected to a
275 * peer, we go over this list and check if someone has already
276 * requested a transmission to the new peer; if so, we trigger
277 * the next step.
278 */
279 struct GNUNET_TRANSPORT_TransmitHandle *connect_wait_head;
280
281 /**
282 * Linked list of transmit handles that are waiting for the
283 * transport to be ready for transmission to the respective
284 * peer. When we
285 * receive notification that the transport disconnected from
286 * a peer, we go over this list and move the entry back to
287 * the connect_wait list.
288 */
289 struct GNUNET_TRANSPORT_TransmitHandle *connect_ready_head;
290
291 /**
292 * Linked list of pending requests for our HELLO.
293 */
294 struct HelloWaitList *hwl_head;
295
296 /**
297 * My scheduler.
298 */
299 struct GNUNET_SCHEDULER_Handle *sched;
300
301 /**
302 * My configuration.
303 */
304 struct GNUNET_CONFIGURATION_Handle *cfg;
305
306 /**
307 * Linked list of the current neighbours of this peer.
308 */
309 struct NeighbourList *neighbours;
310
311 /**
312 * ID of the task trying to reconnect to the
313 * service.
314 */
315 GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
316
317 /**
318 * Delay until we try to reconnect.
319 */
320 struct GNUNET_TIME_Relative reconnect_delay;
321
322 /**
323 * Do we currently have a transmission pending?
324 * (schedule transmission was called but has not
325 * yet succeeded)?
326 */
327 int transmission_scheduled;
328};
329
330
331static struct NeighbourList *
332find_neighbour (struct GNUNET_TRANSPORT_Handle *h,
333 const struct GNUNET_PeerIdentity *peer)
334{
335 struct NeighbourList *pos;
336
337 pos = h->neighbours;
338 while ((pos != NULL) &&
339 (0 != memcmp (peer, &pos->id, sizeof (struct GNUNET_PeerIdentity))))
340 pos = pos->next;
341 return pos;
342}
343
344
345/**
346 * Schedule the task to send one message from the
347 * connect_ready list to the service.
348 */
349static void schedule_transmission (struct GNUNET_TRANSPORT_Handle *h);
350
351
352/**
353 * Transmit message to client...
354 */
355static size_t
356transport_notify_ready (void *cls, size_t size, void *buf)
357{
358 struct GNUNET_TRANSPORT_Handle *h = cls;
359 struct GNUNET_TRANSPORT_TransmitHandle *th;
360 struct NeighbourList *n;
361 size_t ret;
362 char *cbuf;
363
364#if DEBUG_TRANSPORT
365 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
366 "Ready to transmit %u bytes to transport service\n", size);
367#endif
368 h->network_handle = NULL;
369 h->transmission_scheduled = GNUNET_NO;
370 if (buf == NULL)
371 {
372 th = h->connect_ready_head;
373 if (th->next != NULL)
374 th->next->prev = NULL;
375 h->connect_ready_head = th->next;
376 if (NULL != (n = th->neighbour))
377 {
378 GNUNET_assert (n->transmit_handle == th);
379 n->transmit_handle = NULL;
380 }
381 GNUNET_assert (0 == th->notify (th->notify_cls, 0, NULL));
382 GNUNET_free (th);
383 return 0;
384 }
385 cbuf = buf;
386 ret = 0;
387 h->network_handle = NULL;
388 h->transmission_scheduled = GNUNET_NO;
389 do
390 {
391 th = h->connect_ready_head;
392 GNUNET_assert (th->notify_size <= size);
393 if (th->next != NULL)
394 th->next->prev = NULL;
395 h->connect_ready_head = th->next;
396 if (NULL != (n = th->neighbour))
397 {
398 GNUNET_assert (n->transmit_handle == th);
399 n->transmit_handle = NULL;
400 }
401 ret += th->notify (th->notify_cls, size, &cbuf[ret]);
402 GNUNET_free (th);
403 if (n != NULL)
404 n->last_sent += ret;
405 size -= ret;
406 }
407 while ((h->connect_ready_head != NULL) &&
408 (h->connect_ready_head->notify_size <= size));
409 if (h->connect_ready_head != NULL)
410 schedule_transmission (h);
411#if DEBUG_TRANSPORT
412 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
413 "Transmitting %u bytes to transport service\n", ret);
414#endif
415 return ret;
416}
417
418
419/**
420 * Schedule the task to send one message from the
421 * connect_ready list to the service.
422 */
423static void
424schedule_transmission (struct GNUNET_TRANSPORT_Handle *h)
425{
426 struct GNUNET_TRANSPORT_TransmitHandle *th;
427
428 GNUNET_assert (NULL == h->network_handle);
429 if (h->client == NULL)
430 {
431#if DEBUG_TRANSPORT
432 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
433 "Not yet connected to transport service, need to wait.\n");
434#endif
435 return;
436 }
437 th = h->connect_ready_head;
438 if (th == NULL)
439 {
440#if DEBUG_TRANSPORT
441 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
442 "Schedule transmission called, but no request is pending.\n");
443#endif
444 return;
445 }
446 h->transmission_scheduled = GNUNET_YES;
447#if DEBUG_TRANSPORT
448 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
449 "Asking client API for transmission of %u bytes\n",
450 th->notify_size);
451#endif
452 h->network_handle = GNUNET_CLIENT_notify_transmit_ready (h->client,
453 th->notify_size,
454 GNUNET_TIME_absolute_get_remaining
455 (th->timeout),
456 &transport_notify_ready,
457 h);
458 GNUNET_assert (NULL != h->network_handle);
459}
460
461
462/**
463 * Insert the given transmit handle in the given sorted
464 * doubly linked list based on timeout.
465 *
466 * @param head pointer to the head of the linked list
467 * @param th element to insert into the list
468 */
469static void
470insert_transmit_handle (struct GNUNET_TRANSPORT_TransmitHandle **head,
471 struct GNUNET_TRANSPORT_TransmitHandle *th)
472{
473 struct GNUNET_TRANSPORT_TransmitHandle *pos;
474 struct GNUNET_TRANSPORT_TransmitHandle *prev;
475
476 pos = *head;
477 prev = NULL;
478 while ((pos != NULL) && (pos->timeout.value < th->timeout.value))
479 {
480 prev = pos;
481 pos = pos->next;
482 }
483 if (prev == NULL)
484 {
485 th->next = *head;
486 if (th->next != NULL)
487 th->next->prev = th;
488 *head = th;
489 }
490 else
491 {
492 th->next = pos;
493 th->prev = prev;
494 prev->next = th;
495 if (pos != NULL)
496 pos->prev = th;
497 }
498}
499
500
501/**
502 * Queue control request for transmission to the transport
503 * service.
504 *
505 * @param size number of bytes to be transmitted
506 * @param at_head request must be added to the head of the queue
507 * (otherwise request will be appended)
508 * @param timeout how long this transmission can wait (at most)
509 * @param notify function to call to get the content
510 * @param notify_cls closure for notify
511 */
512static void
513schedule_control_transmit (struct GNUNET_TRANSPORT_Handle *h,
514 size_t size,
515 int at_head,
516 struct GNUNET_TIME_Relative timeout,
517 GNUNET_NETWORK_TransmitReadyNotify notify,
518 void *notify_cls)
519{
520 struct GNUNET_TRANSPORT_TransmitHandle *th;
521#if DEBUG_TRANSPORT
522 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
523 "Queueing %u bytes control transmission request.\n", size);
524#endif
525 th = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_TransmitHandle));
526 th->handle = h;
527 th->notify = notify;
528 th->notify_cls = notify_cls;
529 th->timeout = GNUNET_TIME_relative_to_absolute (timeout);
530 th->notify_size = size;
531 if (at_head)
532 {
533 th->next = h->connect_ready_head;
534 h->connect_ready_head = th;
535 if (th->next != NULL)
536 th->next->prev = th;
537 }
538 else
539 {
540 insert_transmit_handle (&h->connect_ready_head, th);
541 }
542 if (GNUNET_NO == h->transmission_scheduled)
543 schedule_transmission (h);
544}
545
546
547/**
548 * Update the quota values for the given neighbour now.
549 */
550static void
551update_quota (struct NeighbourList *n)
552{
553 struct GNUNET_TIME_Relative delta;
554 uint64_t allowed;
555 uint64_t remaining;
556
557 delta = GNUNET_TIME_absolute_get_duration (n->last_quota_update);
558 allowed = delta.value * n->quota_out;
559 if (n->last_sent < allowed)
560 {
561 remaining = allowed - n->last_sent;
562 if (n->quota_out > 0)
563 remaining /= n->quota_out;
564 else
565 remaining = 0;
566 if (remaining > MAX_BANDWIDTH_CARRY)
567 remaining = MAX_BANDWIDTH_CARRY;
568 n->last_sent = 0;
569 n->last_quota_update = GNUNET_TIME_absolute_get ();
570 n->last_quota_update.value -= remaining;
571 }
572 else
573 {
574 n->last_sent -= allowed;
575 n->last_quota_update = GNUNET_TIME_absolute_get ();
576 }
577}
578
579
580struct SetQuotaContext
581{
582 struct GNUNET_TRANSPORT_Handle *handle;
583
584 struct GNUNET_PeerIdentity target;
585
586 GNUNET_SCHEDULER_Task cont;
587
588 void *cont_cls;
589
590 struct GNUNET_TIME_Absolute timeout;
591
592 uint32_t quota_in;
593};
594
595
596static size_t
597send_set_quota (void *cls, size_t size, void *buf)
598{
599 struct SetQuotaContext *sqc = cls;
600 struct QuotaSetMessage *msg;
601
602 if (buf == NULL)
603 {
604 GNUNET_SCHEDULER_add_continuation (sqc->handle->sched,
605 GNUNET_NO,
606 sqc->cont,
607 sqc->cont_cls,
608 GNUNET_SCHEDULER_REASON_TIMEOUT);
609 GNUNET_free (sqc);
610 return 0;
611 }
612#if DEBUG_TRANSPORT
613 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
614 "Transmitting `%s' request with respect to `%4s'.\n",
615 "SET_QUOTA", GNUNET_i2s (&sqc->target));
616#endif
617 GNUNET_assert (size >= sizeof (struct QuotaSetMessage));
618 msg = buf;
619 msg->header.size = htons (sizeof (struct QuotaSetMessage));
620 msg->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA);
621 msg->quota_in = htonl (sqc->quota_in);
622 memcpy (&msg->peer, &sqc->target, sizeof (struct GNUNET_PeerIdentity));
623 if (sqc->cont != NULL)
624 GNUNET_SCHEDULER_add_continuation (sqc->handle->sched,
625 GNUNET_NO,
626 sqc->cont,
627 sqc->cont_cls,
628 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
629 GNUNET_free (sqc);
630 return sizeof (struct QuotaSetMessage);
631}
632
633
634/**
635 * Set the share of incoming bandwidth for the given
636 * peer to the specified amount.
637 *
638 * @param handle connection to transport service
639 * @param target who's bandwidth quota is being changed
640 * @param quota_in incoming bandwidth quota in bytes per ms; 0 can
641 * be used to force all traffic to be discarded
642 * @param quota_out outgoing bandwidth quota in bytes per ms; 0 can
643 * be used to force all traffic to be discarded
644 * @param timeout how long to wait until signaling failure if
645 * we can not communicate the quota change
646 * @param cont continuation to call when done, will be called
647 * either with reason "TIMEOUT" or with reason "PREREQ_DONE"
648 * @param cont_cls closure for continuation
649 */
650void
651GNUNET_TRANSPORT_set_quota (struct GNUNET_TRANSPORT_Handle *handle,
652 const struct GNUNET_PeerIdentity *target,
653 uint32_t quota_in,
654 uint32_t quota_out,
655 struct GNUNET_TIME_Relative timeout,
656 GNUNET_SCHEDULER_Task cont, void *cont_cls)
657{
658 struct NeighbourList *n;
659 struct SetQuotaContext *sqc;
660
661 n = find_neighbour (handle, target);
662 if (n != NULL)
663 {
664 update_quota (n);
665 if (n->quota_out < quota_out)
666 n->last_quota_update = GNUNET_TIME_absolute_get ();
667 n->quota_out = quota_out;
668 }
669 sqc = GNUNET_malloc (sizeof (struct SetQuotaContext));
670 sqc->handle = handle;
671 sqc->target = *target;
672 sqc->cont = cont;
673 sqc->cont_cls = cont_cls;
674 sqc->timeout = GNUNET_TIME_relative_to_absolute (timeout);
675 sqc->quota_in = quota_in;
676 schedule_control_transmit (handle,
677 sizeof (struct QuotaSetMessage),
678 GNUNET_NO, timeout, &send_set_quota, sqc);
679}
680
681
682/**
683 * A "get_hello" request has timed out. Signal the client
684 * and clean up.
685 */
686static void
687hello_wait_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
688{
689 struct HelloWaitList *hwl = cls;
690 struct HelloWaitList *pos;
691 struct HelloWaitList *prev;
692
693 prev = NULL;
694 pos = hwl->handle->hwl_head;
695 while (pos != hwl)
696 {
697 GNUNET_assert (pos != NULL);
698 prev = pos;
699 pos = pos->next;
700 }
701 if (prev == NULL)
702 hwl->handle->hwl_head = hwl->next;
703 else
704 prev->next = hwl->next;
705 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
706 _("Timeout trying to obtain `%s' from transport service.\n"),
707 "HELLO");
708 /* signal timeout */
709 if (hwl->rec != NULL)
710 hwl->rec (hwl->rec_cls, GNUNET_TIME_UNIT_ZERO, NULL, NULL);
711 GNUNET_free (hwl);
712}
713
714
715/**
716 * Obtain the HELLO message for this peer.
717 *
718 * @param handle connection to transport service
719 * @param timeout how long to wait for the HELLO
720 * @param rec function to call with the HELLO, sender will be our peer
721 * identity; message and sender will be NULL on timeout
722 * (handshake with transport service pending/failed).
723 * cost estimate will be 0.
724 * @param rec_cls closure for rec
725 */
726void
727GNUNET_TRANSPORT_get_hello (struct GNUNET_TRANSPORT_Handle *handle,
728 struct GNUNET_TIME_Relative timeout,
729 GNUNET_TRANSPORT_ReceiveCallback rec,
730 void *rec_cls)
731{
732 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk;
733 struct GNUNET_PeerIdentity me;
734 struct HelloWaitList *hwl;
735
736 if (handle->my_hello == NULL)
737 {
738 hwl = GNUNET_malloc (sizeof (struct HelloWaitList));
739 hwl->next = handle->hwl_head;
740 handle->hwl_head = hwl;
741 hwl->handle = handle;
742 hwl->rec = rec;
743 hwl->rec_cls = rec_cls;
744 hwl->timeout = GNUNET_TIME_relative_to_absolute (timeout);
745 hwl->task = GNUNET_SCHEDULER_add_delayed (handle->sched,
746 GNUNET_YES,
747 GNUNET_SCHEDULER_PRIORITY_KEEP,
748 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
749 timeout,
750 &hello_wait_timeout, hwl);
751 return;
752 }
753 GNUNET_assert (GNUNET_OK == GNUNET_HELLO_get_key (handle->my_hello, &pk));
754 GNUNET_CRYPTO_hash (&pk,
755 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
756 &me.hashPubKey);
757
758 rec (rec_cls,
759 GNUNET_TIME_UNIT_ZERO,
760 &me, (const struct GNUNET_MessageHeader *) handle->my_hello);
761}
762
763
764static size_t
765send_hello (void *cls, size_t size, void *buf)
766{
767 struct GNUNET_MessageHeader *hello = cls;
768 uint16_t msize;
769
770 if (buf == NULL)
771 {
772#if DEBUG_TRANSPORT
773 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
774 "Timeout while trying to transmit `%s' request.\n",
775 "HELLO");
776#endif
777 GNUNET_free (hello);
778 return 0;
779 }
780#if DEBUG_TRANSPORT
781 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
782 "Transmitting `%s' request.\n", "HELLO");
783#endif
784 msize = ntohs (hello->size);
785 GNUNET_assert (size >= msize);
786 memcpy (buf, hello, msize);
787 GNUNET_free (hello);
788 return msize;
789}
790
791
792/**
793 * Offer the transport service the HELLO of another peer. Note that
794 * the transport service may just ignore this message if the HELLO is
795 * malformed or useless due to our local configuration.
796 *
797 * @param handle connection to transport service
798 * @param hello the hello message
799 */
800void
801GNUNET_TRANSPORT_offer_hello (struct GNUNET_TRANSPORT_Handle *handle,
802 const struct GNUNET_MessageHeader *hello)
803{
804 struct GNUNET_MessageHeader *hc;
805 uint16_t size;
806
807 if (handle->client == NULL)
808 {
809#if DEBUG_TRANSPORT
810 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
811 "Not connected to transport service, dropping offered HELLO\n");
812#endif
813 return;
814 }
815 GNUNET_break (ntohs (hello->type) == GNUNET_MESSAGE_TYPE_HELLO);
816 size = ntohs (hello->size);
817 GNUNET_break (size >= sizeof (struct GNUNET_MessageHeader));
818 hc = GNUNET_malloc (size);
819 memcpy (hc, hello, size);
820 schedule_control_transmit (handle,
821 size,
822 GNUNET_NO, OFFER_HELLO_TIMEOUT, &send_hello, hc);
823}
824
825
826/**
827 * Function we use for handling incoming messages.
828 */
829static void demultiplexer (void *cls, const struct GNUNET_MessageHeader *msg);
830
831
832static size_t
833send_start (void *cls, size_t size, void *buf)
834{
835 struct GNUNET_MessageHeader *s = buf;
836
837 if (buf == NULL)
838 return 0;
839#if DEBUG_TRANSPORT
840 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
841 "Transmitting `%s' request.\n", "START");
842#endif
843 GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
844 s->size = htons (sizeof (struct GNUNET_MessageHeader));
845 s->type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_START);
846 return sizeof (struct GNUNET_MessageHeader);
847}
848
849
850/**
851 * Try again to connect to transport service.
852 */
853static void
854reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
855{
856 struct GNUNET_TRANSPORT_Handle *h = cls;
857 struct GNUNET_TRANSPORT_TransmitHandle *pos;
858 struct NeighbourList *n;
859
860 while (NULL != (n = h->neighbours))
861 {
862 h->neighbours = n->next;
863 pos = n->transmit_handle;
864 if (pos != NULL)
865 {
866 pos->neighbour = NULL;
867 pos->next = h->connect_wait_head;
868 h->connect_wait_head = pos;
869 if (pos->next != NULL)
870 pos->next->prev = pos;
871 pos->prev = NULL;
872 }
873 GNUNET_free (n);
874 }
875 h->connect_ready_head = NULL;
876#if DEBUG_TRANSPORT
877 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to transport service.\n");
878#endif
879 GNUNET_assert (h->client == NULL);
880 h->reconnect_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
881 h->client = GNUNET_CLIENT_connect (h->sched, "transport", h->cfg);
882 GNUNET_assert (h->client != NULL);
883 /* make sure we don't send "START" twice,
884 remove existing entry from queue (if present) */
885 pos = h->connect_ready_head;
886 while (pos != NULL)
887 {
888 if (pos->notify == &send_start)
889 {
890 if (pos->prev == NULL)
891 h->connect_ready_head = pos->next;
892 else
893 pos->prev->next = pos->next;
894 if (pos->next != NULL)
895 pos->next->prev = pos->prev;
896 GNUNET_assert (pos->neighbour == NULL);
897 GNUNET_free (pos);
898 break;
899 }
900 pos = pos->next;
901 }
902 schedule_control_transmit (h,
903 sizeof (struct GNUNET_MessageHeader),
904 GNUNET_YES,
905 GNUNET_TIME_UNIT_FOREVER_REL, &send_start, NULL);
906 GNUNET_CLIENT_receive (h->client,
907 &demultiplexer, h, GNUNET_TIME_UNIT_FOREVER_REL);
908}
909
910
911/**
912 * Function that will schedule the job that will try
913 * to connect us again to the client.
914 */
915static void
916schedule_reconnect (struct GNUNET_TRANSPORT_Handle *h)
917{
918#if DEBUG_TRANSPORT
919 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
920 "Scheduling task to reconnect to transport service in %llu ms.\n",
921 h->reconnect_delay.value);
922#endif
923 GNUNET_assert (h->client == NULL);
924 GNUNET_assert (h->reconnect_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK);
925 h->reconnect_task
926 = GNUNET_SCHEDULER_add_delayed (h->sched,
927 GNUNET_NO,
928 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
929 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
930 h->reconnect_delay, &reconnect, h);
931 h->reconnect_delay = GNUNET_TIME_UNIT_SECONDS;
932}
933
934
935/**
936 * Remove the given transmit handle from the wait list. Does NOT free
937 * it.
938 */
939static void
940remove_from_wait_list (struct GNUNET_TRANSPORT_TransmitHandle *th)
941{
942 if (th->prev == NULL)
943 th->handle->connect_wait_head = th->next;
944 else
945 th->prev->next = th->next;
946 if (th->next != NULL)
947 th->next->prev = th->prev;
948}
949
950
951/**
952 * We are connected to the respective peer, check the
953 * bandwidth limits and schedule the transmission.
954 */
955static void schedule_request (struct GNUNET_TRANSPORT_TransmitHandle *th);
956
957
958/**
959 * Function called by the scheduler when the timeout
960 * for bandwidth availablility for the target
961 * neighbour is reached.
962 */
963static void
964transmit_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
965{
966 struct GNUNET_TRANSPORT_TransmitHandle *th = cls;
967
968 th->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
969 schedule_request (th);
970}
971
972
973/**
974 * Called when our transmit request timed out before any transport
975 * reported success connecting to the desired peer or before the
976 * transport was ready to receive. Signal error and free
977 * TransmitHandle.
978 */
979static void
980transmit_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
981{
982 struct GNUNET_TRANSPORT_TransmitHandle *th = cls;
983
984 if (th->neighbour != NULL)
985 th->neighbour->transmit_handle = NULL;
986#if DEBUG_TRANSPORT
987 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmission request timed out.\n");
988#endif
989 th->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
990 remove_from_wait_list (th);
991 th->notify (th->notify_cls, 0, NULL);
992 GNUNET_free (th);
993}
994
995
996/**
997 * We are connected to the respective peer, check the
998 * bandwidth limits and schedule the transmission.
999 */
1000static void
1001schedule_request (struct GNUNET_TRANSPORT_TransmitHandle *th)
1002{
1003 struct GNUNET_TRANSPORT_Handle *h;
1004 struct GNUNET_TIME_Relative duration;
1005 struct NeighbourList *n;
1006 uint64_t available;
1007
1008 h = th->handle;
1009 n = th->neighbour;
1010 if (th->notify_delay_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1011 {
1012 GNUNET_SCHEDULER_cancel (h->sched, th->notify_delay_task);
1013 th->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1014 }
1015 /* check outgoing quota */
1016 duration = GNUNET_TIME_absolute_get_duration (n->last_quota_update);
1017 if (duration.value > MIN_QUOTA_REFRESH_TIME)
1018 {
1019 update_quota (n);
1020 duration = GNUNET_TIME_absolute_get_duration (n->last_quota_update);
1021 }
1022 available = duration.value * n->quota_out;
1023 if (available < n->last_sent + th->notify_size)
1024 {
1025 /* calculate how much bandwidth we'd still need to
1026 accumulate and based on that how long we'll have
1027 to wait... */
1028 available = n->last_sent + th->notify_size - available;
1029 duration = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
1030 available / n->quota_out);
1031 if (th->timeout.value <
1032 GNUNET_TIME_relative_to_absolute (duration).value)
1033 {
1034 /* signal timeout! */
1035#if DEBUG_TRANSPORT
1036 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1037 "Would need %llu ms before bandwidth is available for delivery, that is too long. Signaling timeout.\n",
1038 duration.value);
1039#endif
1040 remove_from_wait_list (th);
1041 th->notify (th->notify_cls, 0, NULL);
1042 GNUNET_free (th);
1043 return;
1044 }
1045#if DEBUG_TRANSPORT
1046 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1047 "Need more bandwidth, delaying delivery by %llu ms\n",
1048 duration.value);
1049#endif
1050 th->notify_delay_task
1051 = GNUNET_SCHEDULER_add_delayed (h->sched,
1052 GNUNET_NO,
1053 GNUNET_SCHEDULER_PRIORITY_KEEP,
1054 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1055 duration, &transmit_ready, th);
1056 return;
1057 }
1058#if DEBUG_TRANSPORT
1059 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1060 "Bandwidth available for transmission to `%4s'\n",
1061 GNUNET_i2s (&n->id));
1062#endif
1063 if (GNUNET_NO == n->transmit_ok)
1064 {
1065 /* we may be ready, but transport service is not;
1066 wait for SendOkMessage or timeout */
1067#if DEBUG_TRANSPORT
1068 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1069 "Need to wait for transport service `%s' message\n",
1070 "SEND_OK");
1071#endif
1072 th->notify_delay_task
1073 = GNUNET_SCHEDULER_add_delayed (h->sched,
1074 GNUNET_NO,
1075 GNUNET_SCHEDULER_PRIORITY_KEEP,
1076 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1077 GNUNET_TIME_absolute_get_remaining
1078 (th->timeout), &transmit_timeout, th);
1079 return;
1080 }
1081 n->transmit_ok = GNUNET_NO;
1082 remove_from_wait_list (th);
1083#if DEBUG_TRANSPORT
1084 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Moving message to ready list\n");
1085#endif
1086 insert_transmit_handle (&h->connect_ready_head, th);
1087 if (GNUNET_NO == h->transmission_scheduled)
1088 schedule_transmission (h);
1089}
1090
1091
1092/**
1093 * Add neighbour to our list
1094 */
1095static void
1096add_neighbour (struct GNUNET_TRANSPORT_Handle *h,
1097 uint32_t quota_out,
1098 struct GNUNET_TIME_Relative latency,
1099 const struct GNUNET_PeerIdentity *pid)
1100{
1101 struct NeighbourList *n;
1102 struct GNUNET_TRANSPORT_TransmitHandle *prev;
1103 struct GNUNET_TRANSPORT_TransmitHandle *pos;
1104 struct GNUNET_TRANSPORT_TransmitHandle *next;
1105
1106 /* check for duplicates */
1107 if (NULL != find_neighbour (h, pid))
1108 {
1109 GNUNET_break (0);
1110 return;
1111 }
1112#if DEBUG_TRANSPORT
1113 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1114 "Creating entry for new neighbour `%4s'.\n", GNUNET_i2s (pid));
1115#endif
1116 n = GNUNET_malloc (sizeof (struct NeighbourList));
1117 n->id = *pid;
1118 n->last_quota_update = GNUNET_TIME_absolute_get ();
1119 n->quota_out = quota_out;
1120 n->next = h->neighbours;
1121 n->transmit_ok = GNUNET_YES;
1122 h->neighbours = n;
1123 if (h->nc_cb != NULL)
1124 h->nc_cb (h->cls, &n->id, latency);
1125 prev = NULL;
1126 pos = h->connect_wait_head;
1127 while (pos != NULL)
1128 {
1129 next = pos->next;
1130#if DEBUG_TRANSPORT
1131 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1132 "Found entry in connect_wait_head for `%4s'.\n",
1133 GNUNET_i2s (&pos->target));
1134#endif
1135 if (0 == memcmp (pid,
1136 &pos->target, sizeof (struct GNUNET_PeerIdentity)))
1137 {
1138#if DEBUG_TRANSPORT
1139 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1140 "Found pending request for new connection, will trigger now.\n");
1141#endif
1142 pos->neighbour = n;
1143 if (pos->notify_delay_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1144 {
1145 GNUNET_SCHEDULER_cancel (h->sched, pos->notify_delay_task);
1146 pos->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1147 }
1148 GNUNET_assert (NULL == n->transmit_handle);
1149 n->transmit_handle = pos;
1150 if (GNUNET_YES == n->received_ack)
1151 {
1152#if DEBUG_TRANSPORT
1153 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1154 "`%s' already received, scheduling request\n",
1155 "ACK");
1156#endif
1157 schedule_request (pos);
1158 }
1159 else
1160 {
1161#if DEBUG_TRANSPORT
1162 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1163 "Still need to wait to receive `%s' message\n",
1164 "ACK");
1165#endif
1166 pos->notify_delay_task
1167 = GNUNET_SCHEDULER_add_delayed (h->sched,
1168 GNUNET_NO,
1169 GNUNET_SCHEDULER_PRIORITY_KEEP,
1170 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1171 GNUNET_TIME_absolute_get_remaining
1172 (pos->timeout),
1173 &transmit_timeout, pos);
1174 }
1175 if (prev == NULL)
1176 h->connect_wait_head = next;
1177 else
1178 prev->next = next;
1179 break;
1180 }
1181 prev = pos;
1182 pos = next;
1183 }
1184}
1185
1186
1187/**
1188 * Connect to the transport service. Note that the connection may
1189 * complete (or fail) asynchronously.
1190 *
1191
1192 * @param sched scheduler to use
1193 * @param cfg configuration to use
1194 * @param cls closure for the callbacks
1195 * @param rec receive function to call
1196 * @param nc function to call on connect events
1197 * @param dc function to call on disconnect events
1198 */
1199struct GNUNET_TRANSPORT_Handle *
1200GNUNET_TRANSPORT_connect (struct GNUNET_SCHEDULER_Handle *sched,
1201 struct GNUNET_CONFIGURATION_Handle *cfg,
1202 void *cls,
1203 GNUNET_TRANSPORT_ReceiveCallback rec,
1204 GNUNET_TRANSPORT_NotifyConnect nc,
1205 GNUNET_TRANSPORT_NotifyDisconnect nd)
1206{
1207 struct GNUNET_TRANSPORT_Handle *ret;
1208
1209 GNUNET_ARM_start_service ("peerinfo",
1210 cfg, sched, START_SERVICE_TIMEOUT, NULL, NULL);
1211 GNUNET_ARM_start_service ("transport",
1212 cfg, sched, START_SERVICE_TIMEOUT, NULL, NULL);
1213 ret = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_Handle));
1214 ret->sched = sched;
1215 ret->cfg = cfg;
1216 ret->cls = cls;
1217 ret->rec = rec;
1218 ret->nc_cb = nc;
1219 ret->nd_cb = nd;
1220 ret->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
1221 schedule_reconnect (ret);
1222 return ret;
1223}
1224
1225
1226/**
1227 * These stop activities must be run in a fresh
1228 * scheduler that is NOT in shutdown mode.
1229 */
1230static void
1231stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1232{
1233 struct GNUNET_TRANSPORT_Handle *handle = cls;
1234 GNUNET_ARM_stop_service ("transport",
1235 handle->cfg,
1236 tc->sched, STOP_SERVICE_TIMEOUT, NULL, NULL);
1237 GNUNET_ARM_stop_service ("peerinfo",
1238 handle->cfg,
1239 tc->sched, STOP_SERVICE_TIMEOUT, NULL, NULL);
1240}
1241
1242
1243/**
1244 * Disconnect from the transport service.
1245 */
1246void
1247GNUNET_TRANSPORT_disconnect (struct GNUNET_TRANSPORT_Handle *handle)
1248{
1249 struct GNUNET_TRANSPORT_TransmitHandle *th;
1250 struct NeighbourList *n;
1251 struct HelloWaitList *hwl;
1252 struct GNUNET_CLIENT_Connection *client;
1253
1254#if DEBUG_TRANSPORT
1255 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transport disconnect called!\n");
1256#endif
1257 while (NULL != (th = handle->connect_ready_head))
1258 {
1259 handle->connect_ready_head = th->next;
1260 th->notify (th->notify_cls, 0, NULL);
1261 GNUNET_free (th);
1262 }
1263
1264 while (NULL != (th = handle->connect_wait_head))
1265 {
1266 handle->connect_wait_head = th->next;
1267 if (th->notify_delay_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1268 {
1269 GNUNET_SCHEDULER_cancel (handle->sched, th->notify_delay_task);
1270 th->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1271 }
1272 th->notify (th->notify_cls, 0, NULL);
1273 GNUNET_free (th);
1274 }
1275 while (NULL != (n = handle->neighbours))
1276 {
1277 handle->neighbours = n->next;
1278 GNUNET_free (n);
1279 }
1280 while (NULL != (hwl = handle->hwl_head))
1281 {
1282 handle->hwl_head = hwl->next;
1283 GNUNET_SCHEDULER_cancel (handle->sched, hwl->task);
1284 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1285 _
1286 ("Disconnect while trying to obtain HELLO from transport service.\n"));
1287 if (hwl->rec != NULL)
1288 hwl->rec (hwl->rec_cls, GNUNET_TIME_UNIT_ZERO, NULL, NULL);
1289 GNUNET_free (hwl);
1290 }
1291 if (handle->reconnect_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1292 {
1293 GNUNET_SCHEDULER_cancel (handle->sched, handle->reconnect_task);
1294 handle->reconnect_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1295 }
1296 GNUNET_free_non_null (handle->my_hello);
1297 handle->my_hello = NULL;
1298 GNUNET_SCHEDULER_run (&stop_task, handle);
1299 if (NULL != (client = handle->client))
1300 {
1301#if DEBUG_TRANSPORT
1302 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1303 "Disconnecting from transport service for good.\n");
1304#endif
1305 handle->client = NULL;
1306 GNUNET_CLIENT_disconnect (client);
1307 }
1308 if (client == NULL)
1309 GNUNET_free (handle);
1310}
1311
1312
1313/**
1314 * We're ready to transmit the request that the transport service
1315 * should connect to a new peer. In addition to sending the
1316 * request, schedule the next phase for the transmission processing
1317 * that caused the connect request in the first place.
1318 */
1319static size_t
1320request_connect (void *cls, size_t size, void *buf)
1321{
1322 struct GNUNET_TRANSPORT_TransmitHandle *th = cls;
1323 struct TryConnectMessage *tcm;
1324 struct GNUNET_TRANSPORT_Handle *h;
1325
1326 h = th->handle;
1327 if (buf == NULL)
1328 {
1329#if DEBUG_TRANSPORT
1330 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1331 "Failed to transmit connect request to service.\n");
1332#endif
1333 th->notify (th->notify_cls, 0, NULL);
1334 GNUNET_free (th);
1335 return 0;
1336 }
1337#if DEBUG_TRANSPORT
1338 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1339 "Transmitting `%s' message for `%4s'.\n",
1340 "TRY_CONNECT", GNUNET_i2s (&th->target));
1341#endif
1342 GNUNET_assert (size >= sizeof (struct TryConnectMessage));
1343 tcm = buf;
1344 tcm->header.size = htons (sizeof (struct TryConnectMessage));
1345 tcm->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TRY_CONNECT);
1346 tcm->reserved = htonl (0);
1347 memcpy (&tcm->peer, &th->target, sizeof (struct GNUNET_PeerIdentity));
1348 th->notify_delay_task
1349 = GNUNET_SCHEDULER_add_delayed (h->sched,
1350 GNUNET_NO,
1351 GNUNET_SCHEDULER_PRIORITY_KEEP,
1352 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1353 GNUNET_TIME_absolute_get_remaining (th->
1354 timeout),
1355 &transmit_timeout, th);
1356 insert_transmit_handle (&h->connect_wait_head, th);
1357 return sizeof (struct TryConnectMessage);
1358}
1359
1360
1361/**
1362 * Schedule a request to connect to the given
1363 * neighbour (and if successful, add the specified
1364 * handle to the wait list).
1365 */
1366static void
1367try_connect (struct GNUNET_TRANSPORT_TransmitHandle *th)
1368{
1369 schedule_control_transmit (th->handle,
1370 sizeof (struct TryConnectMessage),
1371 GNUNET_NO,
1372 GNUNET_TIME_absolute_get_remaining (th->timeout),
1373 &request_connect, th);
1374}
1375
1376
1377/**
1378 * Cancel a pending notify transmit task
1379 * and also remove the given transmit handle
1380 * from whatever list is on.
1381 */
1382static void
1383remove_from_any_list (struct GNUNET_TRANSPORT_TransmitHandle *th)
1384{
1385 struct GNUNET_TRANSPORT_Handle *h;
1386
1387 h = th->handle;
1388 if (th->notify_delay_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1389 {
1390 GNUNET_SCHEDULER_cancel (h->sched, th->notify_delay_task);
1391 th->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1392 }
1393 if (th->prev == NULL)
1394 {
1395 if (th == h->connect_wait_head)
1396 h->connect_wait_head = th->next;
1397 else
1398 h->connect_ready_head = th->next;
1399 }
1400 else
1401 th->prev->next = th->next;
1402 if (th->next != NULL)
1403 th->next->prev = th->prev;
1404}
1405
1406
1407/**
1408 * Remove neighbour from our list
1409 */
1410static void
1411remove_neighbour (struct GNUNET_TRANSPORT_Handle *h,
1412 const struct GNUNET_PeerIdentity *peer)
1413{
1414 struct NeighbourList *prev;
1415 struct NeighbourList *pos;
1416 struct GNUNET_TRANSPORT_TransmitHandle *th;
1417
1418 prev = NULL;
1419 pos = h->neighbours;
1420 while ((pos != NULL) &&
1421 (0 != memcmp (peer, &pos->id, sizeof (struct GNUNET_PeerIdentity))))
1422 {
1423 prev = pos;
1424 pos = pos->next;
1425 }
1426 if (pos == NULL)
1427 {
1428 GNUNET_break (0);
1429 return;
1430 }
1431 if (prev == NULL)
1432 h->neighbours = pos->next;
1433 else
1434 prev->next = pos->next;
1435 if (NULL != (th = pos->transmit_handle))
1436 {
1437 pos->transmit_handle = NULL;
1438 th->neighbour = NULL;
1439 remove_from_any_list (th);
1440 try_connect (th);
1441 }
1442 if (h->nc_cb != NULL)
1443 h->nd_cb (h->cls, peer);
1444 GNUNET_free (pos);
1445}
1446
1447
1448/**
1449 * Type of a function to call when we receive a message
1450 * from the service.
1451 *
1452 * @param cls closure
1453 * @param msg message received, NULL on timeout or fatal error
1454 */
1455static void
1456demultiplexer (void *cls, const struct GNUNET_MessageHeader *msg)
1457{
1458 struct GNUNET_TRANSPORT_Handle *h = cls;
1459 const struct DisconnectInfoMessage *dim;
1460 const struct ConnectInfoMessage *cim;
1461 const struct InboundMessage *im;
1462 const struct GNUNET_MessageHeader *imm;
1463 const struct SendOkMessage *okm;
1464 struct HelloWaitList *hwl;
1465 struct NeighbourList *n;
1466 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
1467 struct GNUNET_PeerIdentity me;
1468 uint16_t size;
1469
1470 if ((msg == NULL) || (h->client == NULL))
1471 {
1472 if (h->client != NULL)
1473 {
1474#if DEBUG_TRANSPORT
1475 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1476 "Error receiving from transport service, disconnecting temporarily.\n");
1477#endif
1478 if (h->network_handle != NULL)
1479 {
1480 GNUNET_NETWORK_notify_transmit_ready_cancel (h->network_handle);
1481 h->network_handle = NULL;
1482 h->transmission_scheduled = GNUNET_NO;
1483 }
1484 GNUNET_CLIENT_disconnect (h->client);
1485 h->client = NULL;
1486 schedule_reconnect (h);
1487 }
1488 else
1489 {
1490 /* shutdown initiated from 'GNUNET_TRANSPORT_disconnect',
1491 finish clean up work! */
1492 GNUNET_free (h);
1493 }
1494 return;
1495 }
1496 GNUNET_CLIENT_receive (h->client,
1497 &demultiplexer, h, GNUNET_TIME_UNIT_FOREVER_REL);
1498 size = ntohs (msg->size);
1499 switch (ntohs (msg->type))
1500 {
1501 case GNUNET_MESSAGE_TYPE_HELLO:
1502 if (GNUNET_OK !=
1503 GNUNET_HELLO_get_key ((const struct GNUNET_HELLO_Message *) msg,
1504 &pkey))
1505 {
1506 GNUNET_break (0);
1507 break;
1508 }
1509 GNUNET_CRYPTO_hash (&pkey,
1510 sizeof (struct
1511 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1512 &me.hashPubKey);
1513#if DEBUG_TRANSPORT
1514 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1515 "Receiving (my own) `%s' message, I am `%4s'.\n",
1516 "HELLO", GNUNET_i2s (&me));
1517#endif
1518 GNUNET_free_non_null (h->my_hello);
1519 h->my_hello = NULL;
1520 if (size < sizeof (struct GNUNET_MessageHeader))
1521 {
1522 GNUNET_break (0);
1523 break;
1524 }
1525 h->my_hello = GNUNET_malloc (size);
1526 memcpy (h->my_hello, msg, size);
1527 while (NULL != (hwl = h->hwl_head))
1528 {
1529 h->hwl_head = hwl->next;
1530 GNUNET_SCHEDULER_cancel (h->sched, hwl->task);
1531 GNUNET_TRANSPORT_get_hello (h,
1532 GNUNET_TIME_UNIT_ZERO,
1533 hwl->rec, hwl->rec_cls);
1534 GNUNET_free (hwl);
1535 }
1536 break;
1537 case GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT:
1538 if (size != sizeof (struct ConnectInfoMessage))
1539 {
1540 GNUNET_break (0);
1541 break;
1542 }
1543 cim = (const struct ConnectInfoMessage *) msg;
1544#if DEBUG_TRANSPORT
1545 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1546 "Receiving `%s' message for `%4s'.\n",
1547 "CONNECT", GNUNET_i2s (&cim->id));
1548#endif
1549 add_neighbour (h,
1550 ntohl (cim->quota_out),
1551 GNUNET_TIME_relative_ntoh (cim->latency), &cim->id);
1552 break;
1553 case GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT:
1554 if (size != sizeof (struct DisconnectInfoMessage))
1555 {
1556 GNUNET_break (0);
1557 break;
1558 }
1559 dim = (const struct DisconnectInfoMessage *) msg;
1560#if DEBUG_TRANSPORT
1561 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1562 "Receiving `%s' message for `%4s'.\n",
1563 "DISCONNECT", GNUNET_i2s (&dim->peer));
1564#endif
1565 remove_neighbour (h, &dim->peer);
1566 break;
1567 case GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK:
1568#if DEBUG_TRANSPORT
1569 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1570 "Receiving `%s' message.\n", "SEND_OK");
1571#endif
1572 if (size != sizeof (struct SendOkMessage))
1573 {
1574 GNUNET_break (0);
1575 break;
1576 }
1577 okm = (const struct SendOkMessage *) msg;
1578 n = find_neighbour (h, &okm->peer);
1579 GNUNET_assert (n != NULL);
1580 n->transmit_ok = GNUNET_YES;
1581 if (n->transmit_handle != NULL)
1582 {
1583#if DEBUG_TRANSPORT
1584 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1585 "Processing pending message\n");
1586#endif
1587 GNUNET_SCHEDULER_cancel (h->sched,
1588 n->transmit_handle->notify_delay_task);
1589 n->transmit_handle->notify_delay_task =
1590 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1591 GNUNET_assert (GNUNET_YES == n->received_ack);
1592 schedule_request (n->transmit_handle);
1593 }
1594 break;
1595 case GNUNET_MESSAGE_TYPE_TRANSPORT_RECV:
1596#if DEBUG_TRANSPORT
1597 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1598 "Receiving `%s' message.\n", "RECV");
1599#endif
1600 if (size <
1601 sizeof (struct InboundMessage) +
1602 sizeof (struct GNUNET_MessageHeader))
1603 {
1604 GNUNET_break (0);
1605 break;
1606 }
1607 im = (const struct InboundMessage *) msg;
1608 imm = (const struct GNUNET_MessageHeader *) &im[1];
1609 if (ntohs (imm->size) + sizeof (struct InboundMessage) != size)
1610 {
1611 GNUNET_break (0);
1612 break;
1613 }
1614 switch (ntohs (imm->type))
1615 {
1616 case GNUNET_MESSAGE_TYPE_TRANSPORT_ACK:
1617#if DEBUG_TRANSPORT
1618 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1619 "Receiving `%s' message from `%4s'.\n",
1620 "ACK", GNUNET_i2s (&im->peer));
1621#endif
1622 n = find_neighbour (h, &im->peer);
1623 if (n == NULL)
1624 {
1625 GNUNET_break (0);
1626 break;
1627 }
1628 if (n->received_ack == GNUNET_NO)
1629 {
1630 n->received_ack = GNUNET_YES;
1631 if (NULL != n->transmit_handle)
1632 {
1633#if DEBUG_TRANSPORT
1634 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1635 "Peer connected, scheduling delayed message for deliverery now.\n");
1636#endif
1637 schedule_request (n->transmit_handle);
1638 }
1639 }
1640 break;
1641 default:
1642#if DEBUG_TRANSPORT
1643 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1644 "Received message of type %u from `%4s'.\n",
1645 ntohs (imm->type), GNUNET_i2s (&im->peer));
1646#endif
1647 if (h->rec != NULL)
1648 h->rec (h->cls,
1649 GNUNET_TIME_relative_ntoh (im->latency), &im->peer, imm);
1650 break;
1651 }
1652 break;
1653 default:
1654 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1655 _
1656 ("Received unexpected message of type %u from `%4s' in %s:%u\n"),
1657 ntohs (msg->type), GNUNET_i2s (&im->peer), __FILE__,
1658 __LINE__);
1659 GNUNET_break (0);
1660 break;
1661 }
1662}
1663
1664
1665struct ClientTransmitWrapper
1666{
1667 GNUNET_NETWORK_TransmitReadyNotify notify;
1668 void *notify_cls;
1669 struct GNUNET_TRANSPORT_TransmitHandle *th;
1670};
1671
1672
1673/**
1674 * Transmit message of a client destined for another
1675 * peer to the service.
1676 */
1677static size_t
1678client_notify_wrapper (void *cls, size_t size, void *buf)
1679{
1680 struct ClientTransmitWrapper *ctw = cls;
1681 struct OutboundMessage *obm;
1682 struct GNUNET_MessageHeader *hdr;
1683 size_t ret;
1684
1685 if (size == 0)
1686 {
1687#if DEBUG_TRANSPORT
1688 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1689 "Transmission request could not be satisfied.\n");
1690#endif
1691 ret = ctw->notify (ctw->notify_cls, 0, NULL);
1692 GNUNET_assert (ret == 0);
1693 GNUNET_free (ctw);
1694 return 0;
1695 }
1696 GNUNET_assert (size >= sizeof (struct OutboundMessage));
1697 obm = buf;
1698 ret = ctw->notify (ctw->notify_cls,
1699 size - sizeof (struct OutboundMessage),
1700 (void *) &obm[1]);
1701 if (ret == 0)
1702 {
1703 /* Need to reset flag, no SEND means no SEND_OK! */
1704 ctw->th->neighbour->transmit_ok = GNUNET_YES;
1705 GNUNET_free (ctw);
1706 return 0;
1707 }
1708 GNUNET_assert (ret >= sizeof (struct GNUNET_MessageHeader));
1709 hdr = (struct GNUNET_MessageHeader *) &obm[1];
1710 GNUNET_assert (ntohs (hdr->size) == ret);
1711 GNUNET_assert (ret + sizeof (struct OutboundMessage) <
1712 GNUNET_SERVER_MAX_MESSAGE_SIZE);
1713#if DEBUG_TRANSPORT
1714 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1715 "Transmitting `%s' message with data for `%4s'\n",
1716 "SEND", GNUNET_i2s (&ctw->th->target));
1717#endif
1718 ret += sizeof (struct OutboundMessage);
1719 obm->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SEND);
1720 obm->header.size = htons (ret);
1721 obm->reserved = htonl (0);
1722 obm->peer = ctw->th->target;
1723 GNUNET_free (ctw);
1724 return ret;
1725}
1726
1727
1728
1729/**
1730 * Check if we could queue a message of the given size for
1731 * transmission. The transport service will take both its
1732 * internal buffers and bandwidth limits imposed by the
1733 * other peer into consideration when answering this query.
1734 *
1735 * @param handle connection to transport service
1736 * @param target who should receive the message
1737 * @param size how big is the message we want to transmit?
1738 * @param timeout after how long should we give up (and call
1739 * notify with buf NULL and size 0)?
1740 * @param notify function to call when we are ready to
1741 * send such a message
1742 * @param notify_cls closure for notify
1743 * @return NULL if someone else is already waiting to be notified
1744 * non-NULL if the notify callback was queued (can be used to cancel
1745 * using GNUNET_TRANSPORT_notify_transmit_ready_cancel)
1746 */
1747struct GNUNET_TRANSPORT_TransmitHandle *
1748GNUNET_TRANSPORT_notify_transmit_ready (struct GNUNET_TRANSPORT_Handle
1749 *handle,
1750 const struct GNUNET_PeerIdentity
1751 *target, size_t size,
1752 struct GNUNET_TIME_Relative timeout,
1753 GNUNET_NETWORK_TransmitReadyNotify
1754 notify, void *notify_cls)
1755{
1756 struct GNUNET_TRANSPORT_TransmitHandle *pos;
1757 struct GNUNET_TRANSPORT_TransmitHandle *th;
1758 struct NeighbourList *n;
1759 struct ClientTransmitWrapper *ctw;
1760
1761 if (size + sizeof (struct OutboundMessage) >=
1762 GNUNET_SERVER_MAX_MESSAGE_SIZE)
1763 return NULL;
1764#if DEBUG_TRANSPORT
1765 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1766 "Asking transport service for transmission of %u bytes to peer `%4s'.\n",
1767 size, GNUNET_i2s (target));
1768#endif
1769 n = find_neighbour (handle, target);
1770 ctw = GNUNET_malloc (sizeof (struct ClientTransmitWrapper));
1771 th = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_TransmitHandle));
1772 ctw->notify = notify;
1773 ctw->notify_cls = notify_cls;
1774 ctw->th = th;
1775 th->handle = handle;
1776 th->target = *target;
1777 th->notify = &client_notify_wrapper;
1778 th->notify_cls = ctw;
1779 th->notify_size = size + sizeof (struct OutboundMessage);
1780 th->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1781 th->neighbour = n;
1782 if (NULL == n)
1783 {
1784#if DEBUG_TRANSPORT
1785 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1786 "Transmission request could not be satisfied (not yet connected), adding it to pending request list.\n");
1787#endif
1788 pos = handle->connect_wait_head;
1789 while (pos != NULL)
1790 {
1791 GNUNET_assert (0 != memcmp (target,
1792 &pos->target,
1793 sizeof (struct GNUNET_PeerIdentity)));
1794 pos = pos->next;
1795 }
1796#if DEBUG_TRANSPORT
1797 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1798 "Will now try to connect to `%4s'.\n", GNUNET_i2s (target));
1799#endif
1800 try_connect (th);
1801 }
1802 else
1803 {
1804#if DEBUG_TRANSPORT
1805 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1806 "Transmission request queued for transmission to transport service.\n");
1807#endif
1808 GNUNET_assert (NULL == n->transmit_handle);
1809 n->transmit_handle = th;
1810 if (GNUNET_YES == n->received_ack)
1811 {
1812#if DEBUG_TRANSPORT
1813 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1814 "Peer `%4s' is connected, scheduling for delivery now.\n",
1815 GNUNET_i2s (target));
1816#endif
1817 schedule_request (th);
1818 }
1819 else
1820 {
1821#if DEBUG_TRANSPORT
1822 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1823 "Connection to `%4s' is not yet confirmed connected, scheduling timeout (%llums) only.\n",
1824 GNUNET_i2s (target), timeout.value);
1825#endif
1826 th->notify_delay_task
1827 = GNUNET_SCHEDULER_add_delayed (handle->sched,
1828 GNUNET_NO,
1829 GNUNET_SCHEDULER_PRIORITY_KEEP,
1830 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1831 timeout, &transmit_timeout, th);
1832 }
1833 }
1834 return th;
1835}
1836
1837
1838/**
1839 * Cancel the specified transmission-ready
1840 * notification.
1841 */
1842void
1843GNUNET_TRANSPORT_notify_transmit_ready_cancel (struct
1844 GNUNET_TRANSPORT_TransmitHandle
1845 *th)
1846{
1847 struct GNUNET_TRANSPORT_Handle *h;
1848
1849 GNUNET_assert (th->notify == &client_notify_wrapper);
1850 remove_from_any_list (th);
1851 h = th->handle;
1852 if ((h->connect_ready_head == NULL) && (h->network_handle != NULL))
1853 {
1854 GNUNET_NETWORK_notify_transmit_ready_cancel (h->network_handle);
1855 h->network_handle = NULL;
1856 h->transmission_scheduled = GNUNET_NO;
1857 }
1858 GNUNET_free (th->notify_cls);
1859 GNUNET_free (th);
1860}
1861
1862
1863/* end of transport_api.c */