diff options
author | Christian Grothoff <christian@grothoff.org> | 2009-05-29 00:46:26 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2009-05-29 00:46:26 +0000 |
commit | 0a217a8df1657b4334b55b0e4a6c7837a8dbcfd9 (patch) | |
tree | 6b552f40eb089db96409a312a98d9b12bd669102 /src/transport | |
download | gnunet-0a217a8df1657b4334b55b0e4a6c7837a8dbcfd9.tar.gz gnunet-0a217a8df1657b4334b55b0e4a6c7837a8dbcfd9.zip |
ng
Diffstat (limited to 'src/transport')
-rw-r--r-- | src/transport/Makefile.am | 84 | ||||
-rw-r--r-- | src/transport/NOTES | 46 | ||||
-rw-r--r-- | src/transport/gnunet-service-transport.c | 2852 | ||||
-rw-r--r-- | src/transport/gnunet-transport.c | 42 | ||||
-rw-r--r-- | src/transport/plugin_transport.h | 468 | ||||
-rw-r--r-- | src/transport/plugin_transport_http.c | 2085 | ||||
-rw-r--r-- | src/transport/plugin_transport_smtp.c | 906 | ||||
-rw-r--r-- | src/transport/plugin_transport_tcp.c | 1782 | ||||
-rw-r--r-- | src/transport/plugin_transport_template.c | 335 | ||||
-rw-r--r-- | src/transport/plugin_transport_udp.c | 592 | ||||
-rw-r--r-- | src/transport/test_transport_api.c | 305 | ||||
-rw-r--r-- | src/transport/test_transport_api_data.conf | 24 | ||||
-rw-r--r-- | src/transport/test_transport_api_peer1.conf | 25 | ||||
-rw-r--r-- | src/transport/test_transport_api_peer2.conf | 25 | ||||
-rw-r--r-- | src/transport/transport.h | 238 | ||||
-rw-r--r-- | src/transport/transport_api.c | 1863 |
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 @@ | |||
1 | INCLUDES = -I$(top_srcdir)/src/include | ||
2 | |||
3 | plugindir = $(libdir)/gnunet | ||
4 | |||
5 | if MINGW | ||
6 | WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols | ||
7 | endif | ||
8 | |||
9 | if USE_COVERAGE | ||
10 | AM_CFLAGS = -fprofile-arcs -ftest-coverage | ||
11 | endif | ||
12 | |||
13 | |||
14 | lib_LTLIBRARIES = \ | ||
15 | libgnunettransport.la | ||
16 | |||
17 | libgnunettransport_la_SOURCES = \ | ||
18 | transport_api.c transport.h | ||
19 | libgnunettransport_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) | ||
24 | libgnunettransport_la_LDFLAGS = \ | ||
25 | $(GN_LIB_LDFLAGS) $(WINFLAGS) \ | ||
26 | -version-info 0:0:0 | ||
27 | |||
28 | |||
29 | bin_PROGRAMS = \ | ||
30 | gnunet-transport \ | ||
31 | gnunet-service-transport | ||
32 | |||
33 | gnunet_transport_SOURCES = \ | ||
34 | gnunet-transport.c | ||
35 | gnunet_transport_LDADD = \ | ||
36 | $(top_builddir)/src/transport/libgnunettransport.la \ | ||
37 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
38 | $(GN_LIBINTL) | ||
39 | |||
40 | gnunet_service_transport_SOURCES = \ | ||
41 | gnunet-service-transport.c | ||
42 | gnunet_service_transport_LDADD = \ | ||
43 | $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ | ||
44 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
45 | $(GN_LIBINTL) | ||
46 | |||
47 | |||
48 | |||
49 | plugin_LTLIBRARIES = \ | ||
50 | libgnunet_plugin_transport_tcp.la \ | ||
51 | libgnunet_plugin_transport_template.la | ||
52 | # TODO: add udp, http, nat, etc. | ||
53 | |||
54 | libgnunet_plugin_transport_tcp_la_SOURCES = \ | ||
55 | plugin_transport_tcp.c | ||
56 | libgnunet_plugin_transport_tcp_la_LIBADD = \ | ||
57 | $(top_builddir)/src/resolver/libgnunetresolver.la \ | ||
58 | $(top_builddir)/src/util/libgnunetutil.la | ||
59 | libgnunet_plugin_transport_tcp_la_LDFLAGS = \ | ||
60 | $(GN_PLUGIN_LDFLAGS) | ||
61 | |||
62 | libgnunet_plugin_transport_template_la_SOURCES = \ | ||
63 | plugin_transport_template.c | ||
64 | libgnunet_plugin_transport_template_la_LDFLAGS = \ | ||
65 | $(GN_PLUGIN_LDFLAGS) | ||
66 | |||
67 | |||
68 | check_PROGRAMS = \ | ||
69 | test_transport_api | ||
70 | # TODO: add tests for tcp, udp, http, nat, etc. | ||
71 | |||
72 | TESTS = $(check_PROGRAMS) | ||
73 | |||
74 | test_transport_api_SOURCES = \ | ||
75 | test_transport_api.c | ||
76 | test_transport_api_LDADD = \ | ||
77 | $(top_builddir)/src/transport/libgnunettransport.la \ | ||
78 | $(top_builddir)/src/util/libgnunetutil.la | ||
79 | |||
80 | |||
81 | EXTRA_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 @@ | |||
1 | KEY 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 | */ | ||
95 | struct 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 | */ | ||
125 | struct 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 | |||
176 | struct NeighbourList; | ||
177 | |||
178 | /** | ||
179 | * For each neighbour we keep a list of messages | ||
180 | * that we still want to transmit to the neighbour. | ||
181 | */ | ||
182 | struct 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 | */ | ||
229 | struct 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 | */ | ||
303 | struct 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 | */ | ||
392 | struct 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 | */ | ||
404 | struct 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 | */ | ||
451 | struct 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 | */ | ||
483 | struct 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 | */ | ||
513 | struct 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 | */ | ||
553 | struct 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 | */ | ||
584 | static struct ValidationList *pending_validations; | ||
585 | |||
586 | /** | ||
587 | * Our HELLO message. | ||
588 | */ | ||
589 | static 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 | */ | ||
596 | static unsigned int our_hello_version; | ||
597 | |||
598 | /** | ||
599 | * Our public key. | ||
600 | */ | ||
601 | static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key; | ||
602 | |||
603 | /** | ||
604 | * Our identity. | ||
605 | */ | ||
606 | static struct GNUNET_PeerIdentity my_identity; | ||
607 | |||
608 | /** | ||
609 | * Our private key. | ||
610 | */ | ||
611 | static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key; | ||
612 | |||
613 | /** | ||
614 | * Our scheduler. | ||
615 | */ | ||
616 | struct GNUNET_SCHEDULER_Handle *sched; | ||
617 | |||
618 | /** | ||
619 | * Our configuration. | ||
620 | */ | ||
621 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
622 | |||
623 | /** | ||
624 | * Linked list of all clients to this service. | ||
625 | */ | ||
626 | static struct TransportClient *clients; | ||
627 | |||
628 | /** | ||
629 | * All loaded plugins. | ||
630 | */ | ||
631 | static struct TransportPlugin *plugins; | ||
632 | |||
633 | /** | ||
634 | * Our server. | ||
635 | */ | ||
636 | static struct GNUNET_SERVER_Handle *server; | ||
637 | |||
638 | /** | ||
639 | * All known neighbours and their HELLOs. | ||
640 | */ | ||
641 | static struct NeighbourList *neighbours; | ||
642 | |||
643 | /** | ||
644 | * Default bandwidth quota for receiving for new peers in bytes/ms. | ||
645 | */ | ||
646 | static uint32_t default_quota_in; | ||
647 | |||
648 | /** | ||
649 | * Default bandwidth quota for sending for new peers in bytes/ms. | ||
650 | */ | ||
651 | static uint32_t default_quota_out; | ||
652 | |||
653 | /** | ||
654 | * Number of neighbours we'd like to have. | ||
655 | */ | ||
656 | static 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 | */ | ||
664 | static struct NeighbourList * | ||
665 | find_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 | */ | ||
680 | static struct TransportPlugin * | ||
681 | find_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 | */ | ||
693 | static void | ||
694 | update_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 | */ | ||
743 | static size_t | ||
744 | transmit_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 | */ | ||
806 | static void | ||
807 | transmit_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 | */ | ||
856 | static void | ||
857 | try_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 | */ | ||
886 | static 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 | */ | ||
906 | static void | ||
907 | transmit_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 | */ | ||
960 | static struct ReadyList * | ||
961 | try_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 | */ | ||
1063 | static void | ||
1064 | try_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 | */ | ||
1146 | static void | ||
1147 | transmit_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 | |||
1203 | struct GeneratorContext | ||
1204 | { | ||
1205 | struct TransportPlugin *plug_pos; | ||
1206 | struct AddressList *addr_pos; | ||
1207 | struct GNUNET_TIME_Absolute expiration; | ||
1208 | }; | ||
1209 | |||
1210 | |||
1211 | static size_t | ||
1212 | address_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 | */ | ||
1237 | static void | ||
1238 | refresh_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 | */ | ||
1282 | static void | ||
1283 | expire_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 | */ | ||
1296 | static void | ||
1297 | update_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 | */ | ||
1357 | static void | ||
1358 | expire_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 | */ | ||
1378 | static void | ||
1379 | plugin_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 | |||
1418 | struct LookupHelloContext | ||
1419 | { | ||
1420 | GNUNET_TRANSPORT_AddressCallback iterator; | ||
1421 | |||
1422 | void *iterator_cls; | ||
1423 | }; | ||
1424 | |||
1425 | |||
1426 | static int | ||
1427 | lookup_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 | |||
1438 | static void | ||
1439 | lookup_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 | */ | ||
1468 | static void | ||
1469 | plugin_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 | */ | ||
1489 | static void | ||
1490 | notify_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 | */ | ||
1518 | static void | ||
1519 | notify_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 | */ | ||
1548 | static size_t | ||
1549 | list_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 | */ | ||
1569 | static void | ||
1570 | cleanup_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 | |||
1641 | struct 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 | */ | ||
1664 | static int | ||
1665 | run_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 | */ | ||
1713 | static void | ||
1714 | check_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 | */ | ||
1795 | static int | ||
1796 | process_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 | */ | ||
1875 | static void | ||
1876 | process_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 | */ | ||
1947 | static void | ||
1948 | process_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 | */ | ||
2046 | static void | ||
2047 | disconnect_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 | */ | ||
2105 | static void | ||
2106 | add_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 | |||
2130 | static void | ||
2131 | neighbour_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 | */ | ||
2154 | static struct NeighbourList * | ||
2155 | setup_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 | */ | ||
2209 | static struct ReadyList * | ||
2210 | plugin_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 | */ | ||
2357 | static void | ||
2358 | handle_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 | */ | ||
2436 | static void | ||
2437 | handle_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 | */ | ||
2461 | static void | ||
2462 | handle_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 | */ | ||
2522 | static void | ||
2523 | handle_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 | */ | ||
2569 | static void | ||
2570 | handle_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 | */ | ||
2593 | static 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 | */ | ||
2612 | static void | ||
2613 | create_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 | */ | ||
2630 | static void | ||
2631 | start_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 | */ | ||
2665 | static void | ||
2666 | client_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 | */ | ||
2713 | static void | ||
2714 | run (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 | */ | ||
2807 | static void | ||
2808 | unload_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 | */ | ||
2842 | int | ||
2843 | main (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 | |||
35 | int | ||
36 | main (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 | */ | ||
53 | struct 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 | */ | ||
80 | typedef 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 | */ | ||
104 | typedef 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 | */ | ||
122 | typedef 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 | */ | ||
138 | typedef 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 | */ | ||
152 | struct 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 | */ | ||
224 | typedef 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 | */ | ||
249 | typedef 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 | */ | ||
284 | typedef 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 | */ | ||
321 | typedef 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 | */ | ||
337 | typedef 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 | */ | ||
355 | typedef 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 | */ | ||
376 | typedef 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 | */ | ||
393 | typedef 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 | */ | ||
401 | struct 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 | */ | ||
78 | struct 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 | */ | ||
123 | struct 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 | */ | ||
182 | struct 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 | */ | ||
236 | typedef 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 | |||
364 | static int stat_bytesReceived; | ||
365 | |||
366 | static int stat_bytesSent; | ||
367 | |||
368 | static int stat_bytesDropped; | ||
369 | |||
370 | static int stat_get_issued; | ||
371 | |||
372 | static int stat_get_received; | ||
373 | |||
374 | static int stat_put_issued; | ||
375 | |||
376 | static int stat_put_received; | ||
377 | |||
378 | static int stat_select_calls; | ||
379 | |||
380 | static int stat_send_calls; | ||
381 | |||
382 | static int stat_connect_calls; | ||
383 | |||
384 | static int stat_curl_send_callbacks; | ||
385 | |||
386 | static int stat_curl_receive_callbacks; | ||
387 | |||
388 | static int stat_mhd_access_callbacks; | ||
389 | |||
390 | static int stat_mhd_read_callbacks; | ||
391 | |||
392 | static int stat_mhd_close_callbacks; | ||
393 | |||
394 | static int stat_connect_calls; | ||
395 | |||
396 | /** | ||
397 | * How many requests do we have currently pending | ||
398 | * (with libcurl)? | ||
399 | */ | ||
400 | static unsigned int http_requests_pending; | ||
401 | |||
402 | static int signal_pipe[2]; | ||
403 | |||
404 | static char *proxy; | ||
405 | |||
406 | /** | ||
407 | * Daemon for listening for new connections. | ||
408 | */ | ||
409 | static struct MHD_Daemon *mhd_daemon; | ||
410 | |||
411 | /** | ||
412 | * Curl multi for managing client operations. | ||
413 | */ | ||
414 | static CURLM *curl_multi; | ||
415 | |||
416 | /** | ||
417 | * Set to GNUNET_YES while the transport is running. | ||
418 | */ | ||
419 | static int http_running; | ||
420 | |||
421 | /** | ||
422 | * Thread running libcurl activities. | ||
423 | */ | ||
424 | static struct GNUNET_ThreadHandle *curl_thread; | ||
425 | |||
426 | /** | ||
427 | * Array of currently active HTTP sessions. | ||
428 | */ | ||
429 | static GNUNET_TSession **tsessions; | ||
430 | |||
431 | /** | ||
432 | * Number of valid entries in tsessions. | ||
433 | */ | ||
434 | static unsigned int tsessionCount; | ||
435 | |||
436 | /** | ||
437 | * Sie of the tsessions array. | ||
438 | */ | ||
439 | static unsigned int tsessionArrayLength; | ||
440 | |||
441 | /** | ||
442 | * Lock for concurrent access to all structures used | ||
443 | * by http, including CURL. | ||
444 | */ | ||
445 | static struct GNUNET_Mutex *lock; | ||
446 | |||
447 | |||
448 | /** | ||
449 | * Signal select thread that its selector | ||
450 | * set may have changed. | ||
451 | */ | ||
452 | static void | ||
453 | signal_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 | */ | ||
462 | static int | ||
463 | acceptPolicyCallback (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 | */ | ||
484 | static int | ||
485 | httpDisconnect (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 | |||
499 | static void | ||
500 | destroy_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 | */ | ||
574 | static void | ||
575 | requestCompletedCallback (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 | */ | ||
642 | static int | ||
643 | httpAssociate (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 | */ | ||
671 | static unsigned int | ||
672 | addTSession (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 | */ | ||
697 | static int | ||
698 | contentReaderCallback (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 | */ | ||
733 | static void | ||
734 | contentReaderFreeCallback (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 | */ | ||
751 | static int | ||
752 | accessHandlerCallback (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 | */ | ||
982 | static size_t | ||
983 | receiveContentCallback (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 | */ | ||
1060 | static size_t | ||
1061 | sendContentCallback (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 | |||
1086 | static void | ||
1087 | create_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 | */ | ||
1148 | static int | ||
1149 | create_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 | */ | ||
1234 | static int | ||
1235 | httpConnect (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 | */ | ||
1306 | static size_t | ||
1307 | discardContentCallback (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 | */ | ||
1321 | static int | ||
1322 | create_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 | */ | ||
1405 | static int | ||
1406 | httpTestWouldTry (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 | */ | ||
1461 | static int | ||
1462 | httpSend (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 | */ | ||
1592 | static void | ||
1593 | cleanup_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 | */ | ||
1741 | static void * | ||
1742 | curl_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 | */ | ||
1845 | static int | ||
1846 | startTransportServer () | ||
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 | */ | ||
1938 | static int | ||
1939 | stopTransportServer () | ||
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 | */ | ||
1979 | GNUNET_TransportAPI * | ||
1980 | inittransport_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 | |||
2075 | void | ||
2076 | donetransport_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 | */ | ||
56 | typedef 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 | */ | ||
79 | typedef 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 | */ | ||
95 | static GNUNET_CoreAPIForTransport *coreAPI; | ||
96 | |||
97 | static struct GNUNET_GE_Context *ectx; | ||
98 | |||
99 | /** | ||
100 | * Thread that listens for inbound messages | ||
101 | */ | ||
102 | static struct GNUNET_ThreadHandle *dispatchThread; | ||
103 | |||
104 | /** | ||
105 | * Flag to indicate that server has been shut down. | ||
106 | */ | ||
107 | static int smtp_shutdown = GNUNET_YES; | ||
108 | |||
109 | /** | ||
110 | * Set to the SMTP server hostname (and port) for outgoing messages. | ||
111 | */ | ||
112 | static char *smtp_server_name; | ||
113 | |||
114 | static char *pipename; | ||
115 | |||
116 | /** | ||
117 | * Lock for uses of libesmtp (not thread-safe). | ||
118 | */ | ||
119 | static struct GNUNET_Mutex *lock; | ||
120 | |||
121 | /** | ||
122 | * Old handler for SIGPIPE (kept to be able to restore). | ||
123 | */ | ||
124 | static struct sigaction old_handler; | ||
125 | |||
126 | static char *email; | ||
127 | |||
128 | static GNUNET_TransportAPI smtpAPI; | ||
129 | |||
130 | static GNUNET_Stats_ServiceAPI *stats; | ||
131 | |||
132 | static int stat_bytesReceived; | ||
133 | |||
134 | static int stat_bytesSent; | ||
135 | |||
136 | static int stat_bytesDropped; | ||
137 | |||
138 | /** | ||
139 | * How many e-mails are we allowed to send per hour? | ||
140 | */ | ||
141 | static unsigned long long rate_limit; | ||
142 | |||
143 | static GNUNET_CronTime last_transmission; | ||
144 | |||
145 | /** ******************** Base64 encoding ***********/ | ||
146 | |||
147 | #define FILLCHAR '=' | ||
148 | static 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 | */ | ||
160 | static unsigned int | ||
161 | base64_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 | */ | ||
235 | static unsigned int | ||
236 | base64_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 | } | ||
287 | END: | ||
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 | */ | ||
298 | static void * | ||
299 | listenAndDistribute (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 | */ | ||
418 | static int | ||
419 | api_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 | */ | ||
444 | static GNUNET_MessageHello * | ||
445 | api_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 | |||
490 | struct GetMessageClosure | ||
491 | { | ||
492 | unsigned int esize; | ||
493 | unsigned int pos; | ||
494 | char *ebody; | ||
495 | }; | ||
496 | |||
497 | static const char * | ||
498 | get_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 | */ | ||
523 | static int | ||
524 | api_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 | */ | ||
696 | static int | ||
697 | api_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 | */ | ||
717 | static int | ||
718 | api_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 | */ | ||
733 | static int | ||
734 | api_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 | */ | ||
754 | static int | ||
755 | api_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 | */ | ||
768 | static int | ||
769 | api_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 | */ | ||
778 | static int | ||
779 | api_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 | */ | ||
788 | static int | ||
789 | api_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 | */ | ||
799 | GNUNET_TransportAPI * | ||
800 | inittransport_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 | |||
887 | void | ||
888 | donetransport_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 | */ | ||
71 | struct 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 | */ | ||
86 | struct 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 | */ | ||
119 | struct Plugin; | ||
120 | |||
121 | |||
122 | /** | ||
123 | * Information kept for each message that is yet to | ||
124 | * be transmitted. | ||
125 | */ | ||
126 | struct 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 | */ | ||
170 | struct 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 | */ | ||
275 | struct 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 | */ | ||
335 | static struct Session * | ||
336 | find_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 | */ | ||
353 | static struct Session * | ||
354 | find_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 | */ | ||
369 | static struct PendingMessage * | ||
370 | create_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 | */ | ||
402 | static struct Session * | ||
403 | create_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 | */ | ||
434 | static struct Session * | ||
435 | connect_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 | */ | ||
515 | static 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 | */ | ||
529 | static size_t | ||
530 | do_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 | */ | ||
613 | static void | ||
614 | process_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 | */ | ||
650 | static void * | ||
651 | tcp_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 | */ | ||
718 | static void | ||
719 | disconnect_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 | */ | ||
790 | static int | ||
791 | count_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 | |||
805 | struct 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 | */ | ||
830 | static int | ||
831 | try_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 | */ | ||
877 | static void | ||
878 | session_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 | */ | ||
990 | static void * | ||
991 | tcp_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 | */ | ||
1084 | static void | ||
1085 | tcp_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 | |||
1119 | struct 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 | */ | ||
1130 | static void | ||
1131 | append_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 | */ | ||
1162 | static void | ||
1163 | tcp_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 | */ | ||
1215 | static void | ||
1216 | update_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 | */ | ||
1258 | static void | ||
1259 | tcp_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 | */ | ||
1284 | static uint16_t | ||
1285 | check_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 | */ | ||
1307 | static int | ||
1308 | tcp_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 | */ | ||
1361 | static void | ||
1362 | handle_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 | */ | ||
1422 | static struct GNUNET_TIME_Relative | ||
1423 | calculate_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 | */ | ||
1454 | static void | ||
1455 | delayed_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 | */ | ||
1471 | static void | ||
1472 | handle_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 | */ | ||
1567 | static 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 | |||
1574 | static void | ||
1575 | create_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 | */ | ||
1596 | static void | ||
1597 | disconnect_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 | */ | ||
1623 | static int | ||
1624 | process_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 | */ | ||
1668 | static void | ||
1669 | process_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 | */ | ||
1685 | void * | ||
1686 | libgnunet_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 | */ | ||
1769 | void * | ||
1770 | libgnunet_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 | */ | ||
49 | struct Plugin; | ||
50 | |||
51 | |||
52 | /** | ||
53 | * Session handle for connections. | ||
54 | */ | ||
55 | struct 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 | */ | ||
113 | struct 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 | */ | ||
150 | static void * | ||
151 | template_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 | */ | ||
183 | static void * | ||
184 | template_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 | */ | ||
213 | static void | ||
214 | template_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 | */ | ||
238 | static void | ||
239 | template_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 | */ | ||
260 | static void | ||
261 | template_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 | */ | ||
282 | static int | ||
283 | template_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 | */ | ||
298 | void * | ||
299 | gnunet_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 | */ | ||
324 | void * | ||
325 | gnunet_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 | */ | ||
46 | typedef 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 | |||
65 | static int stat_bytesReceived; | ||
66 | |||
67 | static int stat_bytesSent; | ||
68 | |||
69 | static int stat_bytesDropped; | ||
70 | |||
71 | static int stat_udpConnected; | ||
72 | |||
73 | /** | ||
74 | * thread that listens for inbound messages | ||
75 | */ | ||
76 | static struct GNUNET_SelectHandle *selector; | ||
77 | |||
78 | /** | ||
79 | * the socket that we transmit all data with | ||
80 | */ | ||
81 | static struct GNUNET_SocketHandle *udp_sock; | ||
82 | |||
83 | static 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 | */ | ||
92 | static int | ||
93 | select_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 | |||
124 | static void * | ||
125 | select_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 | */ | ||
141 | static void | ||
142 | select_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 | */ | ||
156 | static int | ||
157 | udp_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 | */ | ||
185 | int | ||
186 | udp_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 | */ | ||
197 | static int | ||
198 | udp_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 | */ | ||
215 | static int | ||
216 | udp_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 | */ | ||
242 | static int | ||
243 | udp_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 | */ | ||
270 | static int | ||
271 | udp_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 | */ | ||
318 | static int | ||
319 | udp_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 | */ | ||
420 | static int | ||
421 | udp_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 | */ | ||
511 | GNUNET_TransportAPI * | ||
512 | inittransport_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 | |||
586 | void | ||
587 | donetransport_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 | |||
45 | struct 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 | |||
55 | static struct PeerContext p1; | ||
56 | |||
57 | static struct PeerContext p2; | ||
58 | |||
59 | static struct GNUNET_SCHEDULER_Handle *sched; | ||
60 | |||
61 | static 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 | |||
70 | static void | ||
71 | end () | ||
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 | */ | ||
90 | static void | ||
91 | notify_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 | */ | ||
116 | static void | ||
117 | notify_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 | */ | ||
135 | static void | ||
136 | notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer) | ||
137 | { | ||
138 | GNUNET_assert (0); | ||
139 | } | ||
140 | |||
141 | |||
142 | static void | ||
143 | setup_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 | ¬ify_receive, | ||
159 | ¬ify_connect, ¬ify_disconnect); | ||
160 | GNUNET_assert (p->th != NULL); | ||
161 | } | ||
162 | |||
163 | |||
164 | static size_t | ||
165 | notify_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 | |||
181 | static void | ||
182 | exchange_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, ¬ify_ready, &p1); | ||
208 | } | ||
209 | |||
210 | |||
211 | static void | ||
212 | exchange_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 | |||
236 | static void | ||
237 | run (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 | |||
252 | static void | ||
253 | stop_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 | |||
264 | static int | ||
265 | check () | ||
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 | |||
288 | int | ||
289 | main (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] | ||
2 | SERVICEHOME = /tmp/test-gnunetd-transport-master/ | ||
3 | |||
4 | [resolver] | ||
5 | PORT = 2364 | ||
6 | |||
7 | [transport] | ||
8 | PORT = 2365 | ||
9 | PLUGINS = tcp | ||
10 | |||
11 | [arm] | ||
12 | PORT = 2366 | ||
13 | |||
14 | [statistics] | ||
15 | PORT = 2367 | ||
16 | |||
17 | [tcp] | ||
18 | PORT = 2368 | ||
19 | |||
20 | [peerinfo] | ||
21 | PORT = 2369 | ||
22 | |||
23 | [testing] | ||
24 | WEAKRANDOM = 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] | ||
2 | SERVICEHOME = /tmp/test-gnunetd-transport-peer-1/ | ||
3 | DEFAULTCONFIG = test_transport_api_peer1.conf | ||
4 | |||
5 | [resolver] | ||
6 | PORT = 12364 | ||
7 | |||
8 | [transport] | ||
9 | PORT = 12365 | ||
10 | PLUGINS = tcp | ||
11 | |||
12 | [arm] | ||
13 | PORT = 12366 | ||
14 | |||
15 | [statistics] | ||
16 | PORT = 12367 | ||
17 | |||
18 | [tcp] | ||
19 | PORT = 12368 | ||
20 | |||
21 | [peerinfo] | ||
22 | PORT = 12369 | ||
23 | |||
24 | [testing] | ||
25 | WEAKRANDOM = 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] | ||
2 | SERVICEHOME = /tmp/test-gnunetd-transport-peer-2/ | ||
3 | DEFAULTCONFIG = test_transport_api_peer2.conf | ||
4 | |||
5 | [resolver] | ||
6 | PORT = 22364 | ||
7 | |||
8 | [transport] | ||
9 | PORT = 22365 | ||
10 | PLUGINS = tcp | ||
11 | |||
12 | [arm] | ||
13 | PORT = 22366 | ||
14 | |||
15 | [statistics] | ||
16 | PORT = 22367 | ||
17 | |||
18 | [tcp] | ||
19 | PORT = 22368 | ||
20 | |||
21 | [peerinfo] | ||
22 | PORT = 22369 | ||
23 | |||
24 | [testing] | ||
25 | WEAKRANDOM = 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 | */ | ||
48 | struct 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 | */ | ||
79 | struct 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 | */ | ||
105 | struct 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 | */ | ||
131 | struct 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 | */ | ||
155 | struct 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 | */ | ||
185 | struct 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 | */ | ||
213 | struct 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 | */ | ||
61 | struct 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 | */ | ||
124 | struct 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 | */ | ||
165 | struct 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 | */ | ||
232 | struct 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 | |||
331 | static struct NeighbourList * | ||
332 | find_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 | */ | ||
349 | static void schedule_transmission (struct GNUNET_TRANSPORT_Handle *h); | ||
350 | |||
351 | |||
352 | /** | ||
353 | * Transmit message to client... | ||
354 | */ | ||
355 | static size_t | ||
356 | transport_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 | */ | ||
423 | static void | ||
424 | schedule_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 | */ | ||
469 | static void | ||
470 | insert_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 | */ | ||
512 | static void | ||
513 | schedule_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 | */ | ||
550 | static void | ||
551 | update_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 | |||
580 | struct 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 | |||
596 | static size_t | ||
597 | send_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 | */ | ||
650 | void | ||
651 | GNUNET_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 | */ | ||
686 | static void | ||
687 | hello_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 | */ | ||
726 | void | ||
727 | GNUNET_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 | |||
764 | static size_t | ||
765 | send_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 | */ | ||
800 | void | ||
801 | GNUNET_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 | */ | ||
829 | static void demultiplexer (void *cls, const struct GNUNET_MessageHeader *msg); | ||
830 | |||
831 | |||
832 | static size_t | ||
833 | send_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 | */ | ||
853 | static void | ||
854 | reconnect (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 | */ | ||
915 | static void | ||
916 | schedule_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 | */ | ||
939 | static void | ||
940 | remove_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 | */ | ||
955 | static 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 | */ | ||
963 | static void | ||
964 | transmit_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 | */ | ||
979 | static void | ||
980 | transmit_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 | */ | ||
1000 | static void | ||
1001 | schedule_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 | */ | ||
1095 | static void | ||
1096 | add_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 | */ | ||
1199 | struct GNUNET_TRANSPORT_Handle * | ||
1200 | GNUNET_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 | */ | ||
1230 | static void | ||
1231 | stop_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 | */ | ||
1246 | void | ||
1247 | GNUNET_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 | */ | ||
1319 | static size_t | ||
1320 | request_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 | */ | ||
1366 | static void | ||
1367 | try_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 | */ | ||
1382 | static void | ||
1383 | remove_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 | */ | ||
1410 | static void | ||
1411 | remove_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 | */ | ||
1455 | static void | ||
1456 | demultiplexer (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 | |||
1665 | struct 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 | */ | ||
1677 | static size_t | ||
1678 | client_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 | */ | ||
1747 | struct GNUNET_TRANSPORT_TransmitHandle * | ||
1748 | GNUNET_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 | */ | ||
1842 | void | ||
1843 | GNUNET_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 */ | ||