diff options
Diffstat (limited to 'src/transport/plugin_transport_http_server.c')
-rw-r--r-- | src/transport/plugin_transport_http_server.c | 1461 |
1 files changed, 216 insertions, 1245 deletions
diff --git a/src/transport/plugin_transport_http_server.c b/src/transport/plugin_transport_http_server.c index f81f5cf6e..da0c11e24 100644 --- a/src/transport/plugin_transport_http_server.c +++ b/src/transport/plugin_transport_http_server.c | |||
@@ -19,1318 +19,289 @@ | |||
19 | */ | 19 | */ |
20 | 20 | ||
21 | /** | 21 | /** |
22 | * @file transport/plugin_transport_http.c | 22 | * @file transport/plugin_transport_http_server.c |
23 | * @brief http transport service plugin | 23 | * @brief HTTP/S server transport plugin |
24 | * @author Matthias Wachs | 24 | * @author Matthias Wachs |
25 | */ | 25 | */ |
26 | 26 | ||
27 | #include "plugin_transport_http.h" | 27 | #include "platform.h" |
28 | #include "gnunet_protocols.h" | ||
29 | #include "gnunet_connection_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 "gnunet_transport_plugin.h" | ||
35 | |||
36 | #if BUILD_HTTPS | ||
37 | #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_https_server_init | ||
38 | #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_https_server_done | ||
39 | #else | ||
40 | #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_http_server_init | ||
41 | #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_http_server_done | ||
42 | #endif | ||
28 | 43 | ||
29 | #define HTTP_ERROR_RESPONSE "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\"><HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD><BODY><H1>Not Found</H1>The requested URL was not found on this server.<P><HR><ADDRESS></ADDRESS></BODY></HTML>" | ||
30 | #define _RECEIVE 0 | ||
31 | #define _SEND 1 | ||
32 | 44 | ||
33 | static struct Plugin * p; | 45 | #define DEBUG_TEMPLATE GNUNET_EXTRA_LOGGING |
34 | 46 | ||
35 | /** | 47 | /** |
36 | * Function that queries MHD's select sets and | 48 | * After how long do we expire an address that we |
37 | * starts the task waiting for them. | 49 | * learned from another peer if it is not reconfirmed |
38 | * @param plugin plugin | 50 | * by anyone? |
39 | * @param daemon_handle the MHD daemon handle | ||
40 | * @param now schedule now or with MHD delay | ||
41 | * @return gnunet task identifier | ||
42 | */ | 51 | */ |
43 | static GNUNET_SCHEDULER_TaskIdentifier | 52 | #define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6) |
44 | server_schedule (struct Plugin *plugin, | ||
45 | struct MHD_Daemon *daemon_handle, | ||
46 | int now); | ||
47 | 53 | ||
48 | static void | ||
49 | server_log (void *arg, const char *fmt, va_list ap) | ||
50 | { | ||
51 | char text[1024]; | ||
52 | |||
53 | vsnprintf (text, sizeof (text), fmt, ap); | ||
54 | va_end (ap); | ||
55 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Server: %s\n", text); | ||
56 | } | ||
57 | 54 | ||
58 | /** | 55 | /** |
59 | * Check if incoming connection is accepted. | 56 | * Encapsulation of all of the state of the plugin. |
60 | * NOTE: Here every connection is accepted | ||
61 | * @param cls plugin as closure | ||
62 | * @param addr address of incoming connection | ||
63 | * @param addr_len address length of incoming connection | ||
64 | * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected | ||
65 | * | ||
66 | */ | 57 | */ |
67 | static int | 58 | struct Plugin; |
68 | server_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len) | ||
69 | { | ||
70 | struct Plugin *plugin = cls; | ||
71 | |||
72 | if (plugin->cur_connections <= plugin->max_connections) | ||
73 | return MHD_YES; | ||
74 | else | ||
75 | { | ||
76 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
77 | "Server: Cannot accept new connections\n"); | ||
78 | return MHD_NO; | ||
79 | } | ||
80 | } | ||
81 | |||
82 | |||
83 | #if BUILD_HTTPS | ||
84 | static char * | ||
85 | server_load_file (const char *file) | ||
86 | { | ||
87 | struct GNUNET_DISK_FileHandle *gn_file; | ||
88 | uint64_t fsize; | ||
89 | char *text = NULL; | ||
90 | |||
91 | if (GNUNET_OK != GNUNET_DISK_file_size (file, | ||
92 | &fsize, GNUNET_NO, GNUNET_YES)) | ||
93 | return NULL; | ||
94 | text = GNUNET_malloc (fsize + 1); | ||
95 | gn_file = | ||
96 | GNUNET_DISK_file_open (file, GNUNET_DISK_OPEN_READ, | ||
97 | GNUNET_DISK_PERM_USER_READ); | ||
98 | if (gn_file == NULL) | ||
99 | { | ||
100 | GNUNET_free (text); | ||
101 | return NULL; | ||
102 | } | ||
103 | if (GNUNET_SYSERR == GNUNET_DISK_file_read (gn_file, text, fsize)) | ||
104 | { | ||
105 | GNUNET_free (text); | ||
106 | GNUNET_DISK_file_close (gn_file); | ||
107 | return NULL; | ||
108 | } | ||
109 | text[fsize] = '\0'; | ||
110 | GNUNET_DISK_file_close (gn_file); | ||
111 | return text; | ||
112 | } | ||
113 | #endif | ||
114 | |||
115 | |||
116 | #if BUILD_HTTPS | ||
117 | |||
118 | static int | ||
119 | server_load_certificate (struct Plugin *plugin) | ||
120 | { | ||
121 | int res = GNUNET_OK; | ||
122 | |||
123 | char *key_file; | ||
124 | char *cert_file; | ||
125 | |||
126 | /* Get crypto init string from config | ||
127 | * If not present just use default values */ | ||
128 | |||
129 | GNUNET_assert (GNUNET_OK == | ||
130 | GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, | ||
131 | plugin->name, | ||
132 | "CRYPTO_INIT", | ||
133 | &plugin->crypto_init)); | ||
134 | |||
135 | if (GNUNET_OK != | ||
136 | GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name, | ||
137 | "KEY_FILE", &key_file)) | ||
138 | { | ||
139 | key_file = GNUNET_strdup ("https_key.key"); | ||
140 | } | ||
141 | |||
142 | if (GNUNET_OK != | ||
143 | GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name, | ||
144 | "CERT_FILE", &cert_file)) | ||
145 | { | ||
146 | GNUNET_asprintf (&cert_file, "%s", "https_cert.crt"); | ||
147 | } | ||
148 | |||
149 | /* read key & certificates from file */ | ||
150 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
151 | "Loading TLS certificate from key-file `%s' cert-file`%s'\n", | ||
152 | key_file, cert_file); | ||
153 | |||
154 | plugin->key = server_load_file (key_file); | ||
155 | plugin->cert = server_load_file (cert_file); | ||
156 | |||
157 | if ((plugin->key == NULL) || (plugin->cert == NULL)) | ||
158 | { | ||
159 | struct GNUNET_OS_Process *cert_creation; | ||
160 | |||
161 | GNUNET_free_non_null (plugin->key); | ||
162 | plugin->key = NULL; | ||
163 | GNUNET_free_non_null (plugin->cert); | ||
164 | plugin->cert = NULL; | ||
165 | |||
166 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
167 | "No usable TLS certificate found, creating certificate\n"); | ||
168 | errno = 0; | ||
169 | cert_creation = | ||
170 | GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL, | ||
171 | "gnunet-transport-certificate-creation", | ||
172 | "gnunet-transport-certificate-creation", | ||
173 | key_file, cert_file, NULL); | ||
174 | if (cert_creation == NULL) | ||
175 | { | ||
176 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, | ||
177 | _ | ||
178 | ("Could not create a new TLS certificate, program `gnunet-transport-certificate-creation' could not be started!\n")); | ||
179 | GNUNET_free (key_file); | ||
180 | GNUNET_free (cert_file); | ||
181 | |||
182 | GNUNET_free_non_null (plugin->key); | ||
183 | plugin->key = NULL; | ||
184 | GNUNET_free_non_null (plugin->cert); | ||
185 | plugin->cert = NULL; | ||
186 | GNUNET_free_non_null (plugin->crypto_init); | ||
187 | plugin->crypto_init = NULL; | ||
188 | |||
189 | return GNUNET_SYSERR; | ||
190 | } | ||
191 | GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (cert_creation)); | ||
192 | GNUNET_OS_process_destroy (cert_creation); | ||
193 | |||
194 | plugin->key = server_load_file (key_file); | ||
195 | plugin->cert = server_load_file (cert_file); | ||
196 | } | ||
197 | |||
198 | if ((plugin->key == NULL) || (plugin->cert == NULL)) | ||
199 | { | ||
200 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, | ||
201 | _ | ||
202 | ("No usable TLS certificate found and creating one failed!\n"), | ||
203 | "transport-https"); | ||
204 | GNUNET_free (key_file); | ||
205 | GNUNET_free (cert_file); | ||
206 | |||
207 | GNUNET_free_non_null (plugin->key); | ||
208 | plugin->key = NULL; | ||
209 | GNUNET_free_non_null (plugin->cert); | ||
210 | plugin->cert = NULL; | ||
211 | GNUNET_free_non_null (plugin->crypto_init); | ||
212 | plugin->crypto_init = NULL; | ||
213 | |||
214 | return GNUNET_SYSERR; | ||
215 | } | ||
216 | GNUNET_free (key_file); | ||
217 | GNUNET_free (cert_file); | ||
218 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n"); | ||
219 | return res; | ||
220 | } | ||
221 | #endif | ||
222 | 59 | ||
223 | 60 | ||
224 | /** | 61 | /** |
225 | * Reschedule the execution of both IPv4 and IPv6 server | 62 | * Session handle for connections. |
226 | * @param plugin the plugin | ||
227 | * @param server which server to schedule v4 or v6? | ||
228 | * @param now GNUNET_YES to schedule execution immediately, GNUNET_NO to wait | ||
229 | * until timeout | ||
230 | */ | 63 | */ |
231 | static void | 64 | struct Session |
232 | server_reschedule (struct Plugin *plugin, struct MHD_Daemon *server, int now) | ||
233 | { | 65 | { |
234 | if ((server == plugin->server_v4) && (plugin->server_v4 != NULL)) | 66 | /** |
235 | { | 67 | * To whom are we talking to (set to our identity |
236 | if (GNUNET_YES == plugin->server_v4_immediately) | 68 | * if we are still waiting for the welcome message) |
237 | return; /* No rescheduling, server will run asap */ | 69 | */ |
238 | 70 | struct GNUNET_PeerIdentity sender; | |
239 | if (GNUNET_YES == now) | 71 | |
240 | plugin->server_v4_immediately = GNUNET_YES; | 72 | /** |
241 | 73 | * Stored in a linked list. | |
242 | if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK) | 74 | */ |
243 | { | 75 | struct Session *next; |
244 | GNUNET_SCHEDULER_cancel (plugin->server_v4_task); | 76 | |
245 | plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; | 77 | /** |
246 | } | 78 | * Pointer to the global plugin struct. |
247 | plugin->server_v4_task = server_schedule (plugin, plugin->server_v4, now); | 79 | */ |
248 | } | 80 | struct Plugin *plugin; |
249 | 81 | ||
250 | if ((server == plugin->server_v6) && (plugin->server_v6 != NULL)) | 82 | /** |
251 | { | 83 | * The client (used to identify this connection) |
252 | if (GNUNET_YES == plugin->server_v6_immediately) | 84 | */ |
253 | return; /* No rescheduling, server will run asap */ | 85 | /* void *client; */ |
254 | 86 | ||
255 | if (GNUNET_YES == now) | 87 | /** |
256 | plugin->server_v6_immediately = GNUNET_YES; | 88 | * Continuation function to call once the transmission buffer |
257 | 89 | * has again space available. NULL if there is no | |
258 | if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK) | 90 | * continuation to call. |
259 | { | 91 | */ |
260 | GNUNET_SCHEDULER_cancel (plugin->server_v6_task); | 92 | GNUNET_TRANSPORT_TransmitContinuation transmit_cont; |
261 | plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; | 93 | |
262 | } | 94 | /** |
263 | plugin->server_v6_task = server_schedule (plugin, plugin->server_v6, now); | 95 | * Closure for transmit_cont. |
264 | } | 96 | */ |
265 | } | 97 | void *transmit_cont_cls; |
98 | |||
99 | /** | ||
100 | * At what time did we reset last_received last? | ||
101 | */ | ||
102 | struct GNUNET_TIME_Absolute last_quota_update; | ||
103 | |||
104 | /** | ||
105 | * How many bytes have we received since the "last_quota_update" | ||
106 | * timestamp? | ||
107 | */ | ||
108 | uint64_t last_received; | ||
109 | |||
110 | /** | ||
111 | * Number of bytes per ms that this peer is allowed | ||
112 | * to send to us. | ||
113 | */ | ||
114 | uint32_t quota; | ||
115 | |||
116 | }; | ||
266 | 117 | ||
267 | /** | 118 | /** |
268 | * Callback called by MessageStreamTokenizer when a message has arrived | 119 | * Encapsulation of all of the state of the plugin. |
269 | * @param cls current session as closure | ||
270 | * @param client clien | ||
271 | * @param message the message to be forwarded to transport service | ||
272 | */ | 120 | */ |
273 | static int | 121 | struct Plugin |
274 | server_receive_mst_cb (void *cls, void *client, | ||
275 | const struct GNUNET_MessageHeader *message) | ||
276 | { | 122 | { |
277 | struct Session *s = cls; | 123 | /** |
278 | 124 | * Our environment. | |
279 | GNUNET_assert (NULL != p); | 125 | */ |
280 | if (GNUNET_NO == exist_session(p, s)) | 126 | struct GNUNET_TRANSPORT_PluginEnvironment *env; |
281 | return GNUNET_OK; | ||
282 | 127 | ||
283 | struct Plugin *plugin = s->plugin; | 128 | /** |
284 | struct GNUNET_TIME_Relative delay; | 129 | * List of open sessions. |
130 | */ | ||
131 | struct Session *sessions; | ||
285 | 132 | ||
286 | delay = http_plugin_receive (s, &s->target, message, s, s->addr, s->addrlen); | 133 | }; |
287 | |||
288 | s->next_receive = | ||
289 | GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay); | ||
290 | |||
291 | if (delay.rel_value > 0) | ||
292 | { | ||
293 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
294 | "Server: peer `%s' address `%s' next read delayed for %llu ms\n", | ||
295 | GNUNET_i2s (&s->target), | ||
296 | http_plugin_address_to_string (NULL, s->addr, s->addrlen), | ||
297 | delay); | ||
298 | } | ||
299 | return GNUNET_OK; | ||
300 | } | ||
301 | 134 | ||
302 | 135 | ||
303 | /** | 136 | /** |
304 | * Callback called by MHD when it needs data to send | 137 | * Function that can be used by the transport service to transmit |
305 | * @param cls current session | 138 | * a message using the plugin. Note that in the case of a |
306 | * @param pos position in buffer | 139 | * peer disconnecting, the continuation MUST be called |
307 | * @param buf the buffer to write data to | 140 | * prior to the disconnect notification itself. This function |
308 | * @param max max number of bytes available in buffer | 141 | * will be called with this peer's HELLO message to initiate |
309 | * @return bytes written to buffer | 142 | * a fresh connection to another peer. |
143 | * | ||
144 | * @param cls closure | ||
145 | * @param session which session must be used | ||
146 | * @param msgbuf the message to transmit | ||
147 | * @param msgbuf_size number of bytes in 'msgbuf' | ||
148 | * @param priority how important is the message (most plugins will | ||
149 | * ignore message priority and just FIFO) | ||
150 | * @param to how long to wait at most for the transmission (does not | ||
151 | * require plugins to discard the message after the timeout, | ||
152 | * just advisory for the desired delay; most plugins will ignore | ||
153 | * this as well) | ||
154 | * @param cont continuation to call once the message has | ||
155 | * been transmitted (or if the transport is ready | ||
156 | * for the next transmission call; or if the | ||
157 | * peer disconnected...); can be NULL | ||
158 | * @param cont_cls closure for cont | ||
159 | * @return number of bytes used (on the physical network, with overheads); | ||
160 | * -1 on hard errors (i.e. address invalid); 0 is a legal value | ||
161 | * and does NOT mean that the message was not transmitted (DV) | ||
310 | */ | 162 | */ |
311 | static ssize_t | 163 | static ssize_t |
312 | server_send_callback (void *cls, uint64_t pos, char *buf, size_t max) | 164 | http_server_plugin_send (void *cls, |
165 | struct Session *session, | ||
166 | const char *msgbuf, size_t msgbuf_size, | ||
167 | unsigned int priority, | ||
168 | struct GNUNET_TIME_Relative to, | ||
169 | GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls) | ||
313 | { | 170 | { |
314 | struct Session *s = cls; | 171 | struct Plugin *plugin = cls; |
315 | ssize_t bytes_read = 0; | 172 | int bytes_sent = 0; |
316 | struct HTTP_Message *msg; | ||
317 | |||
318 | GNUNET_assert (NULL != p); | ||
319 | if (GNUNET_NO == exist_session(p, s)) | ||
320 | return 0; | ||
321 | msg = s->msg_head; | ||
322 | if (NULL != msg) | ||
323 | { | ||
324 | /* sending */ | ||
325 | bytes_read = GNUNET_MIN (msg->size - msg->pos, | ||
326 | max); | ||
327 | memcpy (buf, &msg->buf[msg->pos], bytes_read); | ||
328 | msg->pos += bytes_read; | ||
329 | |||
330 | /* removing message */ | ||
331 | if (msg->pos == msg->size) | ||
332 | { | ||
333 | GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); | ||
334 | if (NULL != msg->transmit_cont) | ||
335 | msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK); | ||
336 | GNUNET_free (msg); | ||
337 | } | ||
338 | } | ||
339 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, | ||
340 | "Server: %p: sent %u bytes\n", s, bytes_read); | ||
341 | return bytes_read; | ||
342 | } | ||
343 | |||
344 | 173 | ||
345 | static struct Session * | 174 | GNUNET_assert (plugin != NULL); |
346 | server_lookup_session (struct Plugin *plugin, | 175 | GNUNET_assert (session != NULL); |
347 | struct ServerConnection * sc) | ||
348 | { | ||
349 | struct Session *s; | ||
350 | 176 | ||
351 | for (s = plugin->head; NULL != s; s = s->next) | 177 | /* struct Plugin *plugin = cls; */ |
352 | if ((s->server_recv == sc) || (s->server_send == sc)) | 178 | return bytes_sent; |
353 | return s; | ||
354 | for (s = plugin->server_semi_head; NULL != s; s = s->next) | ||
355 | if ((s->server_recv == sc) || (s->server_send == sc)) | ||
356 | return s; | ||
357 | return NULL; | ||
358 | } | 179 | } |
359 | 180 | ||
360 | 181 | ||
361 | static struct ServerConnection * | ||
362 | server_lookup_serverconnection (struct Plugin *plugin, | ||
363 | struct MHD_Connection *mhd_connection, const char *url, | ||
364 | const char *method) | ||
365 | { | ||
366 | struct Session *s = NULL; | ||
367 | struct Session *t; | ||
368 | struct ServerConnection *sc = NULL; | ||
369 | const union MHD_ConnectionInfo *conn_info; | ||
370 | struct GNUNET_ATS_Information ats; | ||
371 | struct IPv4HttpAddress a4; | ||
372 | struct IPv6HttpAddress a6; | ||
373 | struct sockaddr_in *s4; | ||
374 | struct sockaddr_in6 *s6; | ||
375 | void *a; | ||
376 | size_t a_len; | ||
377 | struct GNUNET_PeerIdentity target; | ||
378 | uint32_t tag = 0; | ||
379 | int direction = GNUNET_SYSERR; | ||
380 | |||
381 | /* url parsing variables */ | ||
382 | size_t url_len; | ||
383 | char *url_end; | ||
384 | char *hash_start; | ||
385 | char *hash_end; | ||
386 | char *tag_start; | ||
387 | char *tag_end; | ||
388 | |||
389 | conn_info = MHD_get_connection_info (mhd_connection, | ||
390 | MHD_CONNECTION_INFO_CLIENT_ADDRESS); | ||
391 | if ((conn_info->client_addr->sa_family != AF_INET) && | ||
392 | (conn_info->client_addr->sa_family != AF_INET6)) | ||
393 | return NULL; | ||
394 | |||
395 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
396 | "New %s connection from %s\n", | ||
397 | method, url); | ||
398 | /* URL parsing | ||
399 | * URL is valid if it is in the form [peerid[103];tag]*/ | ||
400 | url_len = strlen (url); | ||
401 | url_end = (char *) &url[url_len]; | ||
402 | |||
403 | if (url_len < 105) | ||
404 | { | ||
405 | goto error; /* too short */ | ||
406 | } | ||
407 | hash_start = strrchr (url, '/'); | ||
408 | if (NULL == hash_start) | ||
409 | { | ||
410 | goto error; /* '/' delimiter not found */ | ||
411 | } | ||
412 | if (hash_start >= url_end) | ||
413 | { | ||
414 | goto error; /* mal formed */ | ||
415 | } | ||
416 | hash_start++; | ||
417 | |||
418 | hash_end = strrchr (hash_start, ';'); | ||
419 | if (NULL == hash_end) | ||
420 | goto error; /* ';' delimiter not found */ | ||
421 | if (hash_end >= url_end) | ||
422 | { | ||
423 | goto error; /* mal formed */ | ||
424 | } | ||
425 | |||
426 | if (hash_start >= hash_end) | ||
427 | { | ||
428 | goto error; /* mal formed */ | ||
429 | } | ||
430 | |||
431 | if ((strlen(hash_start) - strlen(hash_end)) != 103) | ||
432 | { | ||
433 | goto error; /* invalid hash length */ | ||
434 | } | ||
435 | |||
436 | char hash[104]; | ||
437 | memcpy (hash, hash_start, 103); | ||
438 | hash[103] = '\0'; | ||
439 | if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((const char *) hash, &(target.hashPubKey))) | ||
440 | { | ||
441 | goto error; /* mal formed */ | ||
442 | } | ||
443 | |||
444 | if (hash_end >= url_end) | ||
445 | { | ||
446 | goto error; /* mal formed */ | ||
447 | } | ||
448 | |||
449 | tag_start = &hash_end[1]; | ||
450 | /* Converting tag */ | ||
451 | tag_end = NULL; | ||
452 | tag = strtoul (tag_start, &tag_end, 10); | ||
453 | if (tag == 0) | ||
454 | { | ||
455 | goto error; /* mal formed */ | ||
456 | } | ||
457 | if (tag_end == NULL) | ||
458 | { | ||
459 | goto error; /* mal formed */ | ||
460 | } | ||
461 | if (tag_end != url_end) | ||
462 | { | ||
463 | goto error; /* mal formed */ | ||
464 | } | ||
465 | |||
466 | if (0 == strcmp (MHD_HTTP_METHOD_PUT, method)) | ||
467 | direction = _RECEIVE; | ||
468 | else if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) | ||
469 | direction = _SEND; | ||
470 | else | ||
471 | { | ||
472 | goto error; | ||
473 | } | ||
474 | |||
475 | plugin->cur_connections++; | ||
476 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
477 | "Server: New %s connection from %s with tag %u\n", | ||
478 | method, | ||
479 | GNUNET_i2s (&target), tag); | ||
480 | |||
481 | /* find duplicate session */ | ||
482 | t = plugin->head; | ||
483 | while (t != NULL) | ||
484 | { | ||
485 | if ((t->inbound) && | ||
486 | (0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity))) | ||
487 | && | ||
488 | /* FIXME add source address comparison */ | ||
489 | (t->tag == tag)) | ||
490 | break; | ||
491 | t = t->next; | ||
492 | } | ||
493 | if (t != NULL) | ||
494 | { | ||
495 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
496 | "Server: Duplicate session, dismissing new connection from peer `%s'\n", | ||
497 | GNUNET_i2s (&target)); | ||
498 | goto error; | ||
499 | } | ||
500 | |||
501 | /* find semi-session */ | ||
502 | t = plugin->server_semi_head; | ||
503 | |||
504 | while (t != NULL) | ||
505 | { | ||
506 | /* FIXME add source address comparison */ | ||
507 | if ((0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity))) | ||
508 | && (t->tag == tag)) | ||
509 | { | ||
510 | break; | ||
511 | } | ||
512 | t = t->next; | ||
513 | } | ||
514 | |||
515 | if (t == NULL) | ||
516 | goto create; | ||
517 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
518 | "Server: Found existing semi-session for `%s'\n", | ||
519 | GNUNET_i2s (&target)); | ||
520 | |||
521 | if ((direction == _SEND) && (t->server_send != NULL)) | ||
522 | { | ||
523 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
524 | "Server: Duplicate GET session, dismissing new connection from peer `%s'\n", | ||
525 | GNUNET_i2s (&target)); | ||
526 | goto error; | ||
527 | } | ||
528 | else | ||
529 | { | ||
530 | s = t; | ||
531 | GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head, | ||
532 | plugin->server_semi_tail, s); | ||
533 | GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s); | ||
534 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
535 | "Server: Found matching semi-session, merging session for peer `%s'\n", | ||
536 | GNUNET_i2s (&target)); | ||
537 | |||
538 | plugin->inbound_sessions ++; | ||
539 | GNUNET_STATISTICS_set (plugin->env->stats, | ||
540 | "# HTTP inbound sessions", | ||
541 | plugin->inbound_sessions, | ||
542 | GNUNET_NO); | ||
543 | GNUNET_assert (NULL != s); | ||
544 | goto found; | ||
545 | } | ||
546 | if ((direction == _RECEIVE) && (t->server_recv != NULL)) | ||
547 | { | ||
548 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
549 | "Server: Duplicate PUT session, dismissing new connection from peer `%s'\n", | ||
550 | GNUNET_i2s (&target)); | ||
551 | goto error; | ||
552 | } | ||
553 | else | ||
554 | { | ||
555 | s = t; | ||
556 | GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head, | ||
557 | plugin->server_semi_tail, s); | ||
558 | GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s); | ||
559 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
560 | "Server: Found matching semi-session, merging session for peer `%s'\n", | ||
561 | GNUNET_i2s (&target)); | ||
562 | plugin->inbound_sessions ++; | ||
563 | GNUNET_STATISTICS_set (plugin->env->stats, | ||
564 | "# HTTP inbound sessions", | ||
565 | plugin->inbound_sessions, | ||
566 | GNUNET_NO); | ||
567 | GNUNET_assert (NULL != s); | ||
568 | goto found; | ||
569 | } | ||
570 | |||
571 | create: | ||
572 | /* create new session */ | ||
573 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
574 | "Server: Creating new session for peer `%s' \n", | ||
575 | GNUNET_i2s (&target)); | ||
576 | switch (conn_info->client_addr->sa_family) | ||
577 | { | ||
578 | case (AF_INET): | ||
579 | s4 = ((struct sockaddr_in *) conn_info->client_addr); | ||
580 | a4.u4_port = s4->sin_port; | ||
581 | memcpy (&a4.ipv4_addr, &s4->sin_addr, sizeof (struct in_addr)); | ||
582 | a = &a4; | ||
583 | a_len = sizeof (struct IPv4HttpAddress); | ||
584 | ats = plugin->env->get_address_type (plugin->env->cls, (const struct sockaddr *) s4, sizeof (struct sockaddr_in)); | ||
585 | break; | ||
586 | case (AF_INET6): | ||
587 | s6 = ((struct sockaddr_in6 *) conn_info->client_addr); | ||
588 | a6.u6_port = s6->sin6_port; | ||
589 | memcpy (&a6.ipv6_addr, &s6->sin6_addr, sizeof (struct in6_addr)); | ||
590 | a = &a6; | ||
591 | a_len = sizeof (struct IPv6HttpAddress); | ||
592 | ats = plugin->env->get_address_type (plugin->env->cls, (const struct sockaddr *) s6, sizeof (struct sockaddr_in6)); | ||
593 | break; | ||
594 | default: | ||
595 | GNUNET_break (0); | ||
596 | goto error; | ||
597 | } | ||
598 | s = create_session (plugin, &target, a, a_len); | ||
599 | GNUNET_assert (NULL != s); | ||
600 | s->ats_address_network_type = ats.value; | ||
601 | s->inbound = GNUNET_YES; | ||
602 | s->next_receive = GNUNET_TIME_UNIT_ZERO_ABS; | ||
603 | s->tag = tag; | ||
604 | s->server_recv = NULL; | ||
605 | s->server_send = NULL; | ||
606 | |||
607 | GNUNET_CONTAINER_DLL_insert (plugin->server_semi_head, | ||
608 | plugin->server_semi_tail, s); | ||
609 | goto found; | ||
610 | |||
611 | error: | ||
612 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
613 | "Server: Invalid connection request\n"); | ||
614 | return NULL; | ||
615 | |||
616 | found: | ||
617 | sc = GNUNET_malloc (sizeof (struct ServerConnection)); | ||
618 | sc->mhd_conn = mhd_connection; | ||
619 | sc->direction = direction; | ||
620 | sc->session = s; | ||
621 | if (direction == _SEND) | ||
622 | s->server_send = sc; | ||
623 | if (direction == _RECEIVE) | ||
624 | s->server_recv = sc; | ||
625 | |||
626 | #if MHD_VERSION >= 0x00090E00 | ||
627 | int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000); | ||
628 | |||
629 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
630 | "Server: Setting timeout for %p to %u sec.\n", sc, to); | ||
631 | MHD_set_connection_option (mhd_connection, MHD_CONNECTION_OPTION_TIMEOUT, to); | ||
632 | |||
633 | struct MHD_Daemon *d = NULL; | ||
634 | |||
635 | if (s->addrlen == sizeof (struct IPv6HttpAddress)) | ||
636 | d = plugin->server_v6; | ||
637 | if (s->addrlen == sizeof (struct IPv4HttpAddress)) | ||
638 | d = plugin->server_v4; | ||
639 | |||
640 | server_reschedule (plugin, d, GNUNET_NO); | ||
641 | #endif | ||
642 | return sc; | ||
643 | } | ||
644 | 182 | ||
645 | /** | 183 | /** |
646 | * Process GET or PUT request received via MHD. For | 184 | * Function that can be used to force the plugin to disconnect |
647 | * GET, queue response that will send back our pending | 185 | * from the given peer and cancel all previous transmissions |
648 | * messages. For PUT, process incoming data and send | 186 | * (and their continuationc). |
649 | * to GNUnet core. In either case, check if a session | 187 | * |
650 | * already exists and create a new one if not. | 188 | * @param cls closure |
189 | * @param target peer from which to disconnect | ||
651 | */ | 190 | */ |
652 | static int | 191 | static void |
653 | server_access_cb (void *cls, struct MHD_Connection *mhd_connection, | 192 | http_server_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target) |
654 | const char *url, const char *method, const char *version, | ||
655 | const char *upload_data, size_t * upload_data_size, | ||
656 | void **httpSessionCache) | ||
657 | { | 193 | { |
658 | struct Plugin *plugin = cls; | 194 | // struct Plugin *plugin = cls; |
659 | struct ServerConnection *sc = *httpSessionCache; | 195 | // FIXME |
660 | struct Session *s; | ||
661 | struct MHD_Response *response; | ||
662 | int res = MHD_YES; | ||
663 | |||
664 | GNUNET_assert (cls != NULL); | ||
665 | if (sc == NULL) | ||
666 | { | ||
667 | /* new connection */ | ||
668 | sc = server_lookup_serverconnection (plugin, mhd_connection, url, method); | ||
669 | if (sc != NULL) | ||
670 | (*httpSessionCache) = sc; | ||
671 | else | ||
672 | { | ||
673 | response = | ||
674 | MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE), | ||
675 | HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO); | ||
676 | res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response); | ||
677 | MHD_destroy_response (response); | ||
678 | return res; | ||
679 | } | ||
680 | } | ||
681 | else | ||
682 | { | ||
683 | /* 'old' connection */ | ||
684 | if (NULL == server_lookup_session (plugin, sc)) | ||
685 | { | ||
686 | /* Session was already disconnected */ | ||
687 | return MHD_NO; | ||
688 | } | ||
689 | } | ||
690 | |||
691 | /* existing connection */ | ||
692 | sc = (*httpSessionCache); | ||
693 | s = sc->session; | ||
694 | |||
695 | GNUNET_assert (NULL != s); | ||
696 | |||
697 | /* connection is to be disconnected */ | ||
698 | if (sc->disconnect == GNUNET_YES) | ||
699 | { | ||
700 | /* Sent HTTP/1.1: 200 OK as PUT Response\ */ | ||
701 | response = | ||
702 | MHD_create_response_from_data (strlen ("Thank you!"), "Thank you!", | ||
703 | MHD_NO, MHD_NO); | ||
704 | res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); | ||
705 | MHD_destroy_response (response); | ||
706 | return MHD_YES; | ||
707 | } | ||
708 | |||
709 | GNUNET_assert (s != NULL); | ||
710 | /* Check if both directions are connected */ | ||
711 | if ((sc->session->server_recv == NULL) || (sc->session->server_send == NULL)) | ||
712 | { | ||
713 | /* Delayed read from since not both semi-connections are connected */ | ||
714 | return MHD_YES; | ||
715 | } | ||
716 | |||
717 | if (sc->direction == _SEND) | ||
718 | { | ||
719 | response = | ||
720 | MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, | ||
721 | 32 * 1024, | ||
722 | &server_send_callback, s, | ||
723 | NULL); | ||
724 | MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); | ||
725 | MHD_destroy_response (response); | ||
726 | return MHD_YES; | ||
727 | } | ||
728 | if (sc->direction == _RECEIVE) | ||
729 | { | ||
730 | if (*upload_data_size == 0) | ||
731 | { | ||
732 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
733 | "Server: Peer `%s' PUT on address `%s' connected\n", | ||
734 | GNUNET_i2s (&s->target), | ||
735 | http_plugin_address_to_string (NULL, s->addr, | ||
736 | s->addrlen)); | ||
737 | return MHD_YES; | ||
738 | } | ||
739 | |||
740 | /* Receiving data */ | ||
741 | if ((*upload_data_size > 0)) | ||
742 | { | ||
743 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
744 | "Server: peer `%s' PUT on address `%s' received %u bytes\n", | ||
745 | GNUNET_i2s (&s->target), | ||
746 | http_plugin_address_to_string (NULL, s->addr, | ||
747 | s->addrlen), | ||
748 | *upload_data_size); | ||
749 | struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); | ||
750 | |||
751 | if ((s->next_receive.abs_value <= now.abs_value)) | ||
752 | { | ||
753 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
754 | "Server: %p: PUT with %u bytes forwarded to MST\n", s, | ||
755 | *upload_data_size); | ||
756 | if (s->msg_tk == NULL) | ||
757 | { | ||
758 | s->msg_tk = GNUNET_SERVER_mst_create (&server_receive_mst_cb, s); | ||
759 | } | ||
760 | GNUNET_SERVER_mst_receive (s->msg_tk, s, upload_data, | ||
761 | *upload_data_size, GNUNET_NO, GNUNET_NO); | ||
762 | |||
763 | #if MHD_VERSION >= 0x00090E00 | ||
764 | int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000); | ||
765 | struct ServerConnection *t = NULL; | ||
766 | |||
767 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
768 | "Server: Received %u bytes\n", *upload_data_size); | ||
769 | /* Setting timeouts for other connections */ | ||
770 | if (s->server_recv != NULL) | ||
771 | { | ||
772 | t = s->server_recv; | ||
773 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
774 | "Server: Setting timeout for %p to %u sec.\n", t, | ||
775 | to); | ||
776 | MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, | ||
777 | to); | ||
778 | } | ||
779 | if (s->server_send != NULL) | ||
780 | { | ||
781 | t = s->server_send; | ||
782 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
783 | "Server: Setting timeout for %p to %u sec.\n", t, | ||
784 | to); | ||
785 | MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, | ||
786 | to); | ||
787 | } | ||
788 | struct MHD_Daemon *d = NULL; | ||
789 | |||
790 | if (s->addrlen == sizeof (struct IPv6HttpAddress)) | ||
791 | d = plugin->server_v6; | ||
792 | if (s->addrlen == sizeof (struct IPv4HttpAddress)) | ||
793 | d = plugin->server_v4; | ||
794 | server_reschedule (plugin, d, GNUNET_NO); | ||
795 | #endif | ||
796 | (*upload_data_size) = 0; | ||
797 | } | ||
798 | else | ||
799 | { | ||
800 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
801 | "Server: %p no inbound bandwidth available! Next read was delayed by %llu ms\n", | ||
802 | s, now.abs_value - s->next_receive.abs_value); | ||
803 | } | ||
804 | return MHD_YES; | ||
805 | } | ||
806 | else | ||
807 | return MHD_NO; | ||
808 | } | ||
809 | return res; | ||
810 | } | 196 | } |
811 | 197 | ||
198 | |||
199 | /** | ||
200 | * Convert the transports address to a nice, human-readable | ||
201 | * format. | ||
202 | * | ||
203 | * @param cls closure | ||
204 | * @param type name of the transport that generated the address | ||
205 | * @param addr one of the addresses of the host, NULL for the last address | ||
206 | * the specific address format depends on the transport | ||
207 | * @param addrlen length of the address | ||
208 | * @param numeric should (IP) addresses be displayed in numeric form? | ||
209 | * @param timeout after how long should we give up? | ||
210 | * @param asc function to call on each string | ||
211 | * @param asc_cls closure for asc | ||
212 | */ | ||
812 | static void | 213 | static void |
813 | server_disconnect_cb (void *cls, struct MHD_Connection *connection, | 214 | http_server_plugin_address_pretty_printer (void *cls, const char *type, |
814 | void **httpSessionCache) | 215 | const void *addr, size_t addrlen, |
216 | int numeric, | ||
217 | struct GNUNET_TIME_Relative timeout, | ||
218 | GNUNET_TRANSPORT_AddressStringCallback | ||
219 | asc, void *asc_cls) | ||
815 | { | 220 | { |
816 | struct ServerConnection *sc = *httpSessionCache; | 221 | asc (asc_cls, NULL); |
817 | struct ServerConnection *tc = NULL; | ||
818 | struct Session *s = NULL; | ||
819 | struct Session *t = NULL; | ||
820 | struct Plugin *plugin = NULL; | ||
821 | |||
822 | if (sc == NULL) | ||
823 | return; | ||
824 | |||
825 | if (NULL == (s = server_lookup_session (p, sc))) | ||
826 | return; | ||
827 | |||
828 | GNUNET_assert (NULL != p); | ||
829 | if (GNUNET_NO == exist_session(p, s)) | ||
830 | return; | ||
831 | |||
832 | plugin = s->plugin; | ||
833 | if (sc->direction == _SEND) | ||
834 | { | ||
835 | |||
836 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
837 | "Server: %p peer `%s' GET on address `%s' disconnected\n", | ||
838 | s->server_send, GNUNET_i2s (&s->target), | ||
839 | http_plugin_address_to_string (NULL, s->addr, s->addrlen)); | ||
840 | s->server_send = NULL; | ||
841 | if (NULL != (tc = s->server_recv)) | ||
842 | { | ||
843 | tc->disconnect = GNUNET_YES; | ||
844 | GNUNET_assert (NULL != tc->mhd_conn); | ||
845 | #if MHD_VERSION >= 0x00090E00 | ||
846 | MHD_set_connection_option (tc->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, | ||
847 | 1); | ||
848 | #endif | ||
849 | } | ||
850 | } | ||
851 | if (sc->direction == _RECEIVE) | ||
852 | { | ||
853 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
854 | "Server: %p peer `%s' PUT on address `%s' disconnected\n", | ||
855 | s->server_recv, GNUNET_i2s (&s->target), | ||
856 | http_plugin_address_to_string (NULL, s->addr, s->addrlen)); | ||
857 | s->server_recv = NULL; | ||
858 | if (NULL != (tc = s->server_send)) | ||
859 | { | ||
860 | tc->disconnect = GNUNET_YES; | ||
861 | GNUNET_assert (NULL != tc->mhd_conn); | ||
862 | #if MHD_VERSION >= 0x00090E00 | ||
863 | MHD_set_connection_option (tc->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, | ||
864 | 1); | ||
865 | #endif | ||
866 | } | ||
867 | if (s->msg_tk != NULL) | ||
868 | { | ||
869 | GNUNET_SERVER_mst_destroy (s->msg_tk); | ||
870 | s->msg_tk = NULL; | ||
871 | } | ||
872 | } | ||
873 | |||
874 | GNUNET_free (sc); | ||
875 | |||
876 | t = plugin->server_semi_head; | ||
877 | while (t != NULL) | ||
878 | { | ||
879 | if (t == s) | ||
880 | { | ||
881 | GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head, | ||
882 | plugin->server_semi_tail, s); | ||
883 | GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s); | ||
884 | break; | ||
885 | } | ||
886 | t = t->next; | ||
887 | } | ||
888 | plugin->cur_connections--; | ||
889 | |||
890 | struct MHD_Daemon *d = NULL; | ||
891 | |||
892 | if (s->addrlen == sizeof (struct IPv6HttpAddress)) | ||
893 | d = plugin->server_v6; | ||
894 | if (s->addrlen == sizeof (struct IPv4HttpAddress)) | ||
895 | d = plugin->server_v4; | ||
896 | server_reschedule (plugin, d, GNUNET_NO); | ||
897 | |||
898 | if ((s->server_send == NULL) && (s->server_recv == NULL)) | ||
899 | { | ||
900 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
901 | "Server: peer `%s' on address `%s' disconnected\n", | ||
902 | GNUNET_i2s (&s->target), | ||
903 | http_plugin_address_to_string (NULL, s->addr, s->addrlen)); | ||
904 | if (s->msg_tk != NULL) | ||
905 | { | ||
906 | GNUNET_SERVER_mst_destroy (s->msg_tk); | ||
907 | s->msg_tk = NULL; | ||
908 | } | ||
909 | |||
910 | GNUNET_assert (plugin->inbound_sessions > 0); | ||
911 | plugin->inbound_sessions --; | ||
912 | GNUNET_STATISTICS_set (plugin->env->stats, | ||
913 | "# HTTP inbound sessions", | ||
914 | plugin->inbound_sessions, GNUNET_NO); | ||
915 | |||
916 | notify_session_end (s->plugin, &s->target, s); | ||
917 | } | ||
918 | } | 222 | } |
919 | 223 | ||
920 | int | ||
921 | server_disconnect (struct Session *s) | ||
922 | { | ||
923 | struct ServerConnection * send; | ||
924 | struct ServerConnection * recv; | ||
925 | 224 | ||
926 | send = (struct ServerConnection *) s->server_send; | ||
927 | if (s->server_send != NULL) | ||
928 | { | ||
929 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, | ||
930 | "Server: %p / %p Terminating inbound PUT session to peer `%s'\n", | ||
931 | s, s->server_send, GNUNET_i2s (&s->target)); | ||
932 | 225 | ||
933 | send->disconnect = GNUNET_YES; | 226 | /** |
934 | #if MHD_VERSION >= 0x00090E00 | 227 | * Another peer has suggested an address for this |
935 | MHD_set_connection_option (send->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, | 228 | * peer and transport plugin. Check that this could be a valid |
936 | 1); | 229 | * address. If so, consider adding it to the list |
937 | #endif | 230 | * of addresses. |
938 | } | 231 | * |
939 | 232 | * @param cls closure | |
940 | recv = (struct ServerConnection *) s->server_recv; | 233 | * @param addr pointer to the address |
941 | if (recv != NULL) | 234 | * @param addrlen length of addr |
942 | { | 235 | * @return GNUNET_OK if this is a plausible address for this peer |
943 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, | 236 | * and transport |
944 | "Server: %p / %p Terminating inbound GET session to peer `%s'\n", | 237 | */ |
945 | s, s->server_recv, GNUNET_i2s (&s->target)); | 238 | static int |
946 | 239 | http_server_plugin_address_suggested (void *cls, const void *addr, size_t addrlen) | |
947 | recv->disconnect = GNUNET_YES; | ||
948 | #if MHD_VERSION >= 0x00090E00 | ||
949 | MHD_set_connection_option (recv->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, | ||
950 | 1); | ||
951 | #endif | ||
952 | } | ||
953 | |||
954 | /* Schedule connection immediately */ | ||
955 | if (s->addrlen == sizeof (struct IPv4HttpAddress)) | ||
956 | { | ||
957 | server_reschedule (s->plugin, s->plugin->server_v4, GNUNET_YES); | ||
958 | } | ||
959 | else if (s->addrlen == sizeof (struct IPv6HttpAddress)) | ||
960 | { | ||
961 | server_reschedule (s->plugin, s->plugin->server_v6, GNUNET_YES); | ||
962 | } | ||
963 | return GNUNET_OK; | ||
964 | } | ||
965 | |||
966 | int | ||
967 | server_send (struct Session *s, struct HTTP_Message *msg) | ||
968 | { | 240 | { |
969 | GNUNET_CONTAINER_DLL_insert_tail (s->msg_head, s->msg_tail, msg); | 241 | /* struct Plugin *plugin = cls; */ |
970 | 242 | ||
971 | if (s->addrlen == sizeof (struct IPv4HttpAddress)) | 243 | /* check if the address is plausible; if so, |
972 | { | 244 | * add it to our list! */ |
973 | server_reschedule (s->plugin, s->plugin->server_v4, GNUNET_YES); | ||
974 | } | ||
975 | else if (s->addrlen == sizeof (struct IPv6HttpAddress)) | ||
976 | { | ||
977 | server_reschedule (s->plugin, s->plugin->server_v6, GNUNET_YES); | ||
978 | } | ||
979 | else | ||
980 | return GNUNET_SYSERR; | ||
981 | return GNUNET_OK; | 245 | return GNUNET_OK; |
982 | } | 246 | } |
983 | 247 | ||
984 | 248 | ||
985 | |||
986 | /** | 249 | /** |
987 | * Call MHD IPv4 to process pending requests and then go back | 250 | * Function called for a quick conversion of the binary address to |
988 | * and schedule the next run. | 251 | * a numeric address. Note that the caller must not free the |
989 | * @param cls plugin as closure | 252 | * address and that the next call to this function is allowed |
990 | * @param tc task context | 253 | * to override the address again. |
254 | * | ||
255 | * @param cls closure | ||
256 | * @param addr binary address | ||
257 | * @param addrlen length of the address | ||
258 | * @return string representing the same address | ||
991 | */ | 259 | */ |
992 | static void | 260 | static const char * |
993 | server_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | 261 | http_server_plugin_address_to_string (void *cls, const void *addr, size_t addrlen) |
994 | { | 262 | { |
995 | struct Plugin *plugin = cls; | 263 | GNUNET_break (0); |
996 | 264 | return NULL; | |
997 | GNUNET_assert (cls != NULL); | ||
998 | |||
999 | plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; | ||
1000 | if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) | ||
1001 | return; | ||
1002 | #if 0 | ||
1003 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
1004 | "Running IPv4 server\n"); | ||
1005 | #endif | ||
1006 | plugin->server_v4_immediately = GNUNET_NO; | ||
1007 | GNUNET_assert (MHD_YES == MHD_run (plugin->server_v4)); | ||
1008 | server_reschedule (plugin, plugin->server_v4, GNUNET_NO); | ||
1009 | } | 265 | } |
1010 | 266 | ||
1011 | 267 | ||
1012 | /** | ||
1013 | * Call MHD IPv6 to process pending requests and then go back | ||
1014 | * and schedule the next run. | ||
1015 | * @param cls plugin as closure | ||
1016 | * @param tc task context | ||
1017 | */ | ||
1018 | static void | ||
1019 | server_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
1020 | { | ||
1021 | struct Plugin *plugin = cls; | ||
1022 | 268 | ||
1023 | GNUNET_assert (cls != NULL); | ||
1024 | plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; | ||
1025 | if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) | ||
1026 | return; | ||
1027 | #if 0 | ||
1028 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
1029 | "Running IPv6 server\n"); | ||
1030 | #endif | ||
1031 | plugin->server_v6_immediately = GNUNET_NO; | ||
1032 | GNUNET_assert (MHD_YES == MHD_run (plugin->server_v6)); | ||
1033 | server_reschedule (plugin, plugin->server_v6, GNUNET_NO); | ||
1034 | } | ||
1035 | 269 | ||
1036 | /** | 270 | /** |
1037 | * Function that queries MHD's select sets and | 271 | * Entry point for the plugin. |
1038 | * starts the task waiting for them. | ||
1039 | * @param plugin plugin | ||
1040 | * @param daemon_handle the MHD daemon handle | ||
1041 | * @return gnunet task identifier | ||
1042 | */ | 272 | */ |
1043 | static GNUNET_SCHEDULER_TaskIdentifier | 273 | void * |
1044 | server_schedule (struct Plugin *plugin, struct MHD_Daemon *daemon_handle, | 274 | LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls) |
1045 | int now) | ||
1046 | { | 275 | { |
1047 | GNUNET_SCHEDULER_TaskIdentifier ret; | 276 | struct GNUNET_TRANSPORT_PluginEnvironment *env = cls; |
1048 | fd_set rs; | 277 | struct GNUNET_TRANSPORT_PluginFunctions *api; |
1049 | fd_set ws; | 278 | struct Plugin *plugin; |
1050 | fd_set es; | 279 | |
1051 | struct GNUNET_NETWORK_FDSet *wrs; | 280 | plugin = GNUNET_malloc (sizeof (struct Plugin)); |
1052 | struct GNUNET_NETWORK_FDSet *wws; | 281 | plugin->env = env; |
1053 | struct GNUNET_NETWORK_FDSet *wes; | 282 | api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions)); |
1054 | int max; | 283 | api->cls = plugin; |
1055 | unsigned MHD_LONG_LONG timeout; | 284 | api->send = &http_server_plugin_send; |
1056 | static unsigned long long last_timeout = 0; | 285 | api->disconnect = &http_server_plugin_disconnect; |
1057 | int haveto; | 286 | api->address_pretty_printer = &http_server_plugin_address_pretty_printer; |
1058 | 287 | api->check_address = &http_server_plugin_address_suggested; | |
1059 | struct GNUNET_TIME_Relative tv; | 288 | api->address_to_string = &http_server_plugin_address_to_string; |
1060 | 289 | return api; | |
1061 | ret = GNUNET_SCHEDULER_NO_TASK; | ||
1062 | FD_ZERO (&rs); | ||
1063 | FD_ZERO (&ws); | ||
1064 | FD_ZERO (&es); | ||
1065 | wrs = GNUNET_NETWORK_fdset_create (); | ||
1066 | wes = GNUNET_NETWORK_fdset_create (); | ||
1067 | wws = GNUNET_NETWORK_fdset_create (); | ||
1068 | max = -1; | ||
1069 | GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max)); | ||
1070 | haveto = MHD_get_timeout (daemon_handle, &timeout); | ||
1071 | if (haveto == MHD_YES) | ||
1072 | { | ||
1073 | if (timeout != last_timeout) | ||
1074 | { | ||
1075 | #if VERBOSE_SERVER | ||
1076 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
1077 | "SELECT Timeout changed from %llu to %llu\n", | ||
1078 | last_timeout, timeout); | ||
1079 | #endif | ||
1080 | last_timeout = timeout; | ||
1081 | } | ||
1082 | tv.rel_value = (uint64_t) timeout; | ||
1083 | } | ||
1084 | else | ||
1085 | tv = GNUNET_TIME_UNIT_SECONDS; | ||
1086 | /* Force immediate run, since we have outbound data to send */ | ||
1087 | if (now == GNUNET_YES) | ||
1088 | tv = GNUNET_TIME_UNIT_MILLISECONDS; | ||
1089 | GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1); | ||
1090 | GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1); | ||
1091 | GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1); | ||
1092 | |||
1093 | if (daemon_handle == plugin->server_v4) | ||
1094 | { | ||
1095 | if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK) | ||
1096 | { | ||
1097 | GNUNET_SCHEDULER_cancel (plugin->server_v4_task); | ||
1098 | plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; | ||
1099 | } | ||
1100 | #if 0 | ||
1101 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
1102 | "Scheduling IPv4 server task in %llu ms\n", tv); | ||
1103 | #endif | ||
1104 | ret = | ||
1105 | GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, | ||
1106 | tv, wrs, wws, | ||
1107 | &server_v4_run, plugin); | ||
1108 | } | ||
1109 | if (daemon_handle == plugin->server_v6) | ||
1110 | { | ||
1111 | if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK) | ||
1112 | { | ||
1113 | GNUNET_SCHEDULER_cancel (plugin->server_v6_task); | ||
1114 | plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; | ||
1115 | } | ||
1116 | #if 0 | ||
1117 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
1118 | "Scheduling IPv6 server task in %llu ms\n", tv); | ||
1119 | #endif | ||
1120 | ret = | ||
1121 | GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, | ||
1122 | tv, wrs, wws, | ||
1123 | &server_v6_run, plugin); | ||
1124 | } | ||
1125 | GNUNET_NETWORK_fdset_destroy (wrs); | ||
1126 | GNUNET_NETWORK_fdset_destroy (wws); | ||
1127 | GNUNET_NETWORK_fdset_destroy (wes); | ||
1128 | return ret; | ||
1129 | } | 290 | } |
1130 | 291 | ||
1131 | int | ||
1132 | server_start (struct Plugin *plugin) | ||
1133 | { | ||
1134 | int res = GNUNET_OK; | ||
1135 | unsigned int timeout; | ||
1136 | p = plugin; | ||
1137 | GNUNET_assert (NULL != plugin); | ||
1138 | |||
1139 | #if BUILD_HTTPS | ||
1140 | res = server_load_certificate (plugin); | ||
1141 | if (res == GNUNET_SYSERR) | ||
1142 | { | ||
1143 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, | ||
1144 | "Could not load or create server certificate! Loading plugin failed!\n"); | ||
1145 | return res; | ||
1146 | } | ||
1147 | #endif | ||
1148 | |||
1149 | 292 | ||
1150 | #if MHD_VERSION >= 0x00090E00 | 293 | /** |
1151 | timeout = HTTP_NOT_VALIDATED_TIMEOUT.rel_value / 1000; | 294 | * Exit point from the plugin. |
1152 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | 295 | */ |
1153 | "MHD can set timeout per connection! Default time out %u sec.\n", | 296 | void * |
1154 | timeout); | 297 | LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls) |
1155 | #else | ||
1156 | timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000; | ||
1157 | GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name, | ||
1158 | "MHD cannot set timeout per connection! Default time out %u sec.\n", | ||
1159 | timeout); | ||
1160 | #endif | ||
1161 | plugin->server_v4 = NULL; | ||
1162 | if (plugin->ipv4 == GNUNET_YES) | ||
1163 | { | ||
1164 | plugin->server_v4 = MHD_start_daemon ( | ||
1165 | #if VERBOSE_SERVER | ||
1166 | MHD_USE_DEBUG | | ||
1167 | #endif | ||
1168 | #if BUILD_HTTPS | ||
1169 | MHD_USE_SSL | | ||
1170 | #endif | ||
1171 | MHD_NO_FLAG, plugin->port, | ||
1172 | &server_accept_cb, plugin, | ||
1173 | &server_access_cb, plugin, | ||
1174 | MHD_OPTION_SOCK_ADDR, | ||
1175 | (struct sockaddr_in *) | ||
1176 | plugin->server_addr_v4, | ||
1177 | MHD_OPTION_CONNECTION_LIMIT, | ||
1178 | (unsigned int) | ||
1179 | plugin->max_connections, | ||
1180 | #if BUILD_HTTPS | ||
1181 | MHD_OPTION_HTTPS_PRIORITIES, | ||
1182 | plugin->crypto_init, | ||
1183 | MHD_OPTION_HTTPS_MEM_KEY, | ||
1184 | plugin->key, | ||
1185 | MHD_OPTION_HTTPS_MEM_CERT, | ||
1186 | plugin->cert, | ||
1187 | #endif | ||
1188 | MHD_OPTION_CONNECTION_TIMEOUT, | ||
1189 | timeout, | ||
1190 | MHD_OPTION_CONNECTION_MEMORY_LIMIT, | ||
1191 | (size_t) (2 * | ||
1192 | GNUNET_SERVER_MAX_MESSAGE_SIZE), | ||
1193 | MHD_OPTION_NOTIFY_COMPLETED, | ||
1194 | &server_disconnect_cb, plugin, | ||
1195 | MHD_OPTION_EXTERNAL_LOGGER, | ||
1196 | server_log, NULL, MHD_OPTION_END); | ||
1197 | } | ||
1198 | plugin->server_v6 = NULL; | ||
1199 | if (plugin->ipv6 == GNUNET_YES) | ||
1200 | { | ||
1201 | plugin->server_v6 = MHD_start_daemon ( | ||
1202 | #if VERBOSE_SERVER | ||
1203 | MHD_USE_DEBUG | | ||
1204 | #endif | ||
1205 | #if BUILD_HTTPS | ||
1206 | MHD_USE_SSL | | ||
1207 | #endif | ||
1208 | MHD_USE_IPv6, plugin->port, | ||
1209 | &server_accept_cb, plugin, | ||
1210 | &server_access_cb, plugin, | ||
1211 | MHD_OPTION_SOCK_ADDR, | ||
1212 | (struct sockaddr_in6 *) | ||
1213 | plugin->server_addr_v6, | ||
1214 | MHD_OPTION_CONNECTION_LIMIT, | ||
1215 | (unsigned int) | ||
1216 | plugin->max_connections, | ||
1217 | #if BUILD_HTTPS | ||
1218 | MHD_OPTION_HTTPS_PRIORITIES, | ||
1219 | plugin->crypto_init, | ||
1220 | MHD_OPTION_HTTPS_MEM_KEY, | ||
1221 | plugin->key, | ||
1222 | MHD_OPTION_HTTPS_MEM_CERT, | ||
1223 | plugin->cert, | ||
1224 | #endif | ||
1225 | MHD_OPTION_CONNECTION_TIMEOUT, | ||
1226 | timeout, | ||
1227 | MHD_OPTION_CONNECTION_MEMORY_LIMIT, | ||
1228 | (size_t) (2 * | ||
1229 | GNUNET_SERVER_MAX_MESSAGE_SIZE), | ||
1230 | MHD_OPTION_NOTIFY_COMPLETED, | ||
1231 | &server_disconnect_cb, plugin, | ||
1232 | MHD_OPTION_EXTERNAL_LOGGER, | ||
1233 | server_log, NULL, MHD_OPTION_END); | ||
1234 | |||
1235 | } | ||
1236 | |||
1237 | if ((plugin->ipv4 == GNUNET_YES) && (plugin->server_v4 == NULL)) | ||
1238 | { | ||
1239 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, | ||
1240 | "Failed to start %s IPv4 server component on port %u\n", | ||
1241 | plugin->name, plugin->port); | ||
1242 | return GNUNET_SYSERR; | ||
1243 | } | ||
1244 | server_reschedule (plugin, plugin->server_v4, GNUNET_NO); | ||
1245 | |||
1246 | if ((plugin->ipv6 == GNUNET_YES) && (plugin->server_v6 == NULL)) | ||
1247 | { | ||
1248 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, | ||
1249 | "Failed to start %s IPv6 server component on port %u\n", | ||
1250 | plugin->name, plugin->port); | ||
1251 | return GNUNET_SYSERR; | ||
1252 | } | ||
1253 | server_reschedule (plugin, plugin->server_v6, GNUNET_NO); | ||
1254 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
1255 | "%s server component started on port %u\n", plugin->name, | ||
1256 | plugin->port); | ||
1257 | return res; | ||
1258 | } | ||
1259 | |||
1260 | void | ||
1261 | server_stop (struct Plugin *plugin) | ||
1262 | { | 298 | { |
1263 | struct Session *s = NULL; | 299 | struct GNUNET_TRANSPORT_PluginFunctions *api = cls; |
1264 | struct Session *t = NULL; | 300 | struct Plugin *plugin = api->cls; |
1265 | |||
1266 | struct MHD_Daemon *server_v4_tmp = plugin->server_v4; | ||
1267 | plugin->server_v4 = NULL; | ||
1268 | |||
1269 | struct MHD_Daemon *server_v6_tmp = plugin->server_v6; | ||
1270 | plugin->server_v6 = NULL; | ||
1271 | |||
1272 | if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK) | ||
1273 | { | ||
1274 | GNUNET_SCHEDULER_cancel (plugin->server_v4_task); | ||
1275 | plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; | ||
1276 | } | ||
1277 | |||
1278 | if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK) | ||
1279 | { | ||
1280 | GNUNET_SCHEDULER_cancel (plugin->server_v6_task); | ||
1281 | plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; | ||
1282 | } | ||
1283 | |||
1284 | if (server_v6_tmp != NULL) | ||
1285 | { | ||
1286 | MHD_stop_daemon (server_v4_tmp); | ||
1287 | } | ||
1288 | if (server_v6_tmp != NULL) | ||
1289 | { | ||
1290 | MHD_stop_daemon (server_v6_tmp); | ||
1291 | } | ||
1292 | 301 | ||
1293 | /* cleaning up semi-sessions never propagated */ | 302 | GNUNET_free (plugin); |
1294 | s = plugin->server_semi_head; | 303 | GNUNET_free (api); |
1295 | while (s != NULL) | 304 | return NULL; |
1296 | { | ||
1297 | #if VERBOSE_SERVER | ||
1298 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
1299 | "Deleting semi-sessions %p\n", s); | ||
1300 | #endif | ||
1301 | t = s->next; | ||
1302 | struct HTTP_Message *msg = s->msg_head; | ||
1303 | struct HTTP_Message *tmp = NULL; | ||
1304 | |||
1305 | while (msg != NULL) | ||
1306 | { | ||
1307 | tmp = msg->next; | ||
1308 | |||
1309 | GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); | ||
1310 | if (msg->transmit_cont != NULL) | ||
1311 | { | ||
1312 | msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR); | ||
1313 | } | ||
1314 | GNUNET_free (msg); | ||
1315 | msg = tmp; | ||
1316 | } | ||
1317 | |||
1318 | delete_session (s); | ||
1319 | s = t; | ||
1320 | } | ||
1321 | |||
1322 | p = NULL; | ||
1323 | |||
1324 | #if BUILD_HTTPS | ||
1325 | GNUNET_free_non_null (plugin->crypto_init); | ||
1326 | GNUNET_free_non_null (plugin->cert); | ||
1327 | GNUNET_free_non_null (plugin->key); | ||
1328 | #endif | ||
1329 | |||
1330 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
1331 | "%s server component stopped\n", plugin->name); | ||
1332 | } | 305 | } |
1333 | 306 | ||
1334 | 307 | /* end of plugin_transport_http_server.c */ | |
1335 | |||
1336 | /* end of plugin_transport_http.c */ | ||