diff options
author | Christian Grothoff <christian@grothoff.org> | 2013-08-13 22:00:45 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2013-08-13 22:00:45 +0000 |
commit | 78f323a26ba33c640162741d4f44b6996239b0e8 (patch) | |
tree | 54f1f6cd78ce5232dbf2fa363cbbbf4f224058fc /src/namestore/gnunet-namestore-fcfsd.c | |
parent | 59b84fb41a89b6237001e30d75e878d694de957d (diff) | |
download | gnunet-78f323a26ba33c640162741d4f44b6996239b0e8.tar.gz gnunet-78f323a26ba33c640162741d4f44b6996239b0e8.zip |
-adapting fcfsd to new namestore API, moving from gns to namestore subsystem
Diffstat (limited to 'src/namestore/gnunet-namestore-fcfsd.c')
-rw-r--r-- | src/namestore/gnunet-namestore-fcfsd.c | 947 |
1 files changed, 947 insertions, 0 deletions
diff --git a/src/namestore/gnunet-namestore-fcfsd.c b/src/namestore/gnunet-namestore-fcfsd.c new file mode 100644 index 000000000..d949806db --- /dev/null +++ b/src/namestore/gnunet-namestore-fcfsd.c | |||
@@ -0,0 +1,947 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | (C) 2012-2013 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 3, 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 gnunet-gns-fcfsd.c | ||
22 | * @brief HTTP daemon that offers first-come-first-serve GNS domain registration | ||
23 | * @author Christian Grothoff | ||
24 | * | ||
25 | * TODO: | ||
26 | * - the code currently contains a 'race' between checking that the | ||
27 | * domain name is available and allocating it to the new public key | ||
28 | * (should this race be solved by namestore or by fcfsd?) | ||
29 | * - nicer error reporting to browser | ||
30 | */ | ||
31 | #include "platform.h" | ||
32 | #include <microhttpd.h> | ||
33 | #include "gnunet_util_lib.h" | ||
34 | #include "gnunet_namestore_service.h" | ||
35 | |||
36 | /** | ||
37 | * Invalid method page. | ||
38 | */ | ||
39 | #define METHOD_ERROR "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>Illegal request</title></head><body>Go away.</body></html>" | ||
40 | |||
41 | /** | ||
42 | * Front page. (/) | ||
43 | */ | ||
44 | #define MAIN_PAGE "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>GNUnet FCFS Authority Name Registration Service</title></head><body><form action=\"S\" method=\"post\">What is your desired domain name? (at most 63 lowercase characters, no dots allowed.) <input type=\"text\" name=\"domain\" /> <p> What is your public key? (Copy from gnunet-setup.) <input type=\"text\" name=\"pkey\" /> <input type=\"submit\" value=\"Next\" /><br/><a href=./Zoneinfo> List of all registered names </a></body></html>" | ||
45 | |||
46 | /** | ||
47 | * Second page (/S) | ||
48 | */ | ||
49 | #define SUBMIT_PAGE "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>%s</title></head><body>%s</body></html>" | ||
50 | |||
51 | /** | ||
52 | * Fcfs zoneinfo page (/Zoneinfo) | ||
53 | */ | ||
54 | #define ZONEINFO_PAGE "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>FCFS Zoneinfo</title></head><body><h1> FCFS Zoneinfo </h1><table border=\"1\"><th>name</th><th>PKEY</th>%s</table></body></html>" | ||
55 | |||
56 | #define FCFS_ZONEINFO_URL "/Zoneinfo" | ||
57 | |||
58 | /** | ||
59 | * Mime type for HTML pages. | ||
60 | */ | ||
61 | #define MIME_HTML "text/html" | ||
62 | |||
63 | /** | ||
64 | * Name of our cookie. | ||
65 | */ | ||
66 | #define COOKIE_NAME "gns-fcfs" | ||
67 | |||
68 | #define DEFAULT_ZONEINFO_BUFSIZE 2048 | ||
69 | |||
70 | /** | ||
71 | * Phases a request goes through. | ||
72 | */ | ||
73 | enum Phase | ||
74 | { | ||
75 | /** | ||
76 | * Start phase (parsing POST, checking). | ||
77 | */ | ||
78 | RP_START = 0, | ||
79 | |||
80 | /** | ||
81 | * Lookup to see if the domain name is taken. | ||
82 | */ | ||
83 | RP_LOOKUP, | ||
84 | |||
85 | /** | ||
86 | * Storing of the record. | ||
87 | */ | ||
88 | RP_PUT, | ||
89 | |||
90 | /** | ||
91 | * We're done with success. | ||
92 | */ | ||
93 | RP_SUCCESS, | ||
94 | |||
95 | /** | ||
96 | * Send failure message. | ||
97 | */ | ||
98 | RP_FAIL | ||
99 | }; | ||
100 | |||
101 | |||
102 | /** | ||
103 | * Data kept per request. | ||
104 | */ | ||
105 | struct Request | ||
106 | { | ||
107 | |||
108 | /** | ||
109 | * Associated session. | ||
110 | */ | ||
111 | struct Session *session; | ||
112 | |||
113 | /** | ||
114 | * Post processor handling form data (IF this is | ||
115 | * a POST request). | ||
116 | */ | ||
117 | struct MHD_PostProcessor *pp; | ||
118 | |||
119 | /** | ||
120 | * URL to serve in response to this POST (if this request | ||
121 | * was a 'POST') | ||
122 | */ | ||
123 | const char *post_url; | ||
124 | |||
125 | /** | ||
126 | * Active request with the namestore. | ||
127 | */ | ||
128 | struct GNUNET_NAMESTORE_QueueEntry *qe; | ||
129 | |||
130 | /** | ||
131 | * Current processing phase. | ||
132 | */ | ||
133 | enum Phase phase; | ||
134 | |||
135 | /** | ||
136 | * Domain name submitted via form. | ||
137 | */ | ||
138 | char domain_name[64]; | ||
139 | |||
140 | /** | ||
141 | * Public key submitted via form. | ||
142 | */ | ||
143 | char public_key[128]; | ||
144 | |||
145 | }; | ||
146 | |||
147 | /** | ||
148 | * Zoneinfo request | ||
149 | */ | ||
150 | struct ZoneinfoRequest | ||
151 | { | ||
152 | /** | ||
153 | * Connection | ||
154 | */ | ||
155 | struct MHD_Connection *connection; | ||
156 | |||
157 | /** | ||
158 | * List iterator | ||
159 | */ | ||
160 | struct GNUNET_NAMESTORE_ZoneIterator *list_it; | ||
161 | |||
162 | /** | ||
163 | * Buffer | ||
164 | */ | ||
165 | char* zoneinfo; | ||
166 | |||
167 | /** | ||
168 | * Buffer length | ||
169 | */ | ||
170 | size_t buf_len; | ||
171 | |||
172 | /** | ||
173 | * Buffer write offset | ||
174 | */ | ||
175 | size_t write_offset; | ||
176 | }; | ||
177 | |||
178 | /** | ||
179 | * MHD deamon reference. | ||
180 | */ | ||
181 | static struct MHD_Daemon *httpd; | ||
182 | |||
183 | /** | ||
184 | * Main HTTP task. | ||
185 | */ | ||
186 | static GNUNET_SCHEDULER_TaskIdentifier httpd_task; | ||
187 | |||
188 | /** | ||
189 | * Handle to the namestore. | ||
190 | */ | ||
191 | static struct GNUNET_NAMESTORE_Handle *ns; | ||
192 | |||
193 | /** | ||
194 | * Private key for the fcfsd zone. | ||
195 | */ | ||
196 | static struct GNUNET_CRYPTO_EccPrivateKey *fcfs_zone_pkey; | ||
197 | |||
198 | |||
199 | /** | ||
200 | * Task run whenever HTTP server operations are pending. | ||
201 | * | ||
202 | * @param cls unused | ||
203 | * @param tc scheduler context | ||
204 | */ | ||
205 | static void | ||
206 | do_httpd (void *cls, | ||
207 | const struct GNUNET_SCHEDULER_TaskContext *tc); | ||
208 | |||
209 | |||
210 | /** | ||
211 | * Schedule task to run MHD server now. | ||
212 | */ | ||
213 | static void | ||
214 | run_httpd_now () | ||
215 | { | ||
216 | if (GNUNET_SCHEDULER_NO_TASK != httpd_task) | ||
217 | { | ||
218 | GNUNET_SCHEDULER_cancel (httpd_task); | ||
219 | httpd_task = GNUNET_SCHEDULER_NO_TASK; | ||
220 | } | ||
221 | httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL); | ||
222 | } | ||
223 | |||
224 | |||
225 | static void | ||
226 | iterate_cb (void *cls, | ||
227 | const struct GNUNET_CRYPTO_EccPrivateKey *zone_key, | ||
228 | const char *name, | ||
229 | unsigned int rd_len, | ||
230 | const struct GNUNET_NAMESTORE_RecordData *rd) | ||
231 | { | ||
232 | struct ZoneinfoRequest *zr = cls; | ||
233 | struct MHD_Response *response; | ||
234 | char* full_page; | ||
235 | size_t bytes_free; | ||
236 | char* pkey; | ||
237 | char* new_buf; | ||
238 | |||
239 | |||
240 | if (NULL == name) | ||
241 | { | ||
242 | zr->list_it = NULL; | ||
243 | |||
244 | /* return static form */ | ||
245 | GNUNET_asprintf (&full_page, | ||
246 | ZONEINFO_PAGE, | ||
247 | zr->zoneinfo, | ||
248 | zr->zoneinfo); | ||
249 | response = MHD_create_response_from_buffer (strlen (full_page), | ||
250 | (void *) full_page, | ||
251 | MHD_RESPMEM_MUST_FREE); | ||
252 | MHD_add_response_header (response, | ||
253 | MHD_HTTP_HEADER_CONTENT_TYPE, | ||
254 | MIME_HTML); | ||
255 | MHD_queue_response (zr->connection, | ||
256 | MHD_HTTP_OK, | ||
257 | response); | ||
258 | MHD_destroy_response (response); | ||
259 | GNUNET_free (zr->zoneinfo); | ||
260 | GNUNET_free (zr); | ||
261 | run_httpd_now (); | ||
262 | return; | ||
263 | } | ||
264 | |||
265 | if (1 != rd_len) | ||
266 | { | ||
267 | GNUNET_NAMESTORE_zone_iterator_next (zr->list_it); | ||
268 | return; | ||
269 | } | ||
270 | |||
271 | if (GNUNET_NAMESTORE_TYPE_PKEY != rd->record_type) | ||
272 | { | ||
273 | GNUNET_NAMESTORE_zone_iterator_next (zr->list_it); | ||
274 | return; | ||
275 | } | ||
276 | |||
277 | bytes_free = zr->buf_len - zr->write_offset; | ||
278 | pkey = GNUNET_NAMESTORE_value_to_string (rd->record_type, | ||
279 | rd->data, | ||
280 | rd->data_size); | ||
281 | |||
282 | if (bytes_free < (strlen (name) + strlen (pkey) + 40)) | ||
283 | { | ||
284 | new_buf = GNUNET_malloc (zr->buf_len * 2); | ||
285 | memcpy (new_buf, zr->zoneinfo, zr->write_offset); | ||
286 | GNUNET_free (zr->zoneinfo); | ||
287 | zr->zoneinfo = new_buf; | ||
288 | zr->buf_len *= 2; | ||
289 | } | ||
290 | sprintf (zr->zoneinfo + zr->write_offset, | ||
291 | "<tr><td>%s</td><td>%s</td></tr>", | ||
292 | name, | ||
293 | pkey); | ||
294 | zr->write_offset = strlen (zr->zoneinfo); | ||
295 | GNUNET_NAMESTORE_zone_iterator_next (zr->list_it); | ||
296 | GNUNET_free (pkey); | ||
297 | } | ||
298 | |||
299 | |||
300 | |||
301 | /** | ||
302 | * Handler that returns FCFS zoneinfo page. | ||
303 | * | ||
304 | * @param connection connection to use | ||
305 | * @return MHD_YES on success | ||
306 | */ | ||
307 | static int | ||
308 | serve_zoneinfo_page (struct MHD_Connection *connection) | ||
309 | { | ||
310 | struct ZoneinfoRequest *zr; | ||
311 | |||
312 | zr = GNUNET_new (struct ZoneinfoRequest); | ||
313 | zr->zoneinfo = GNUNET_malloc (DEFAULT_ZONEINFO_BUFSIZE); | ||
314 | zr->buf_len = DEFAULT_ZONEINFO_BUFSIZE; | ||
315 | zr->connection = connection; | ||
316 | zr->write_offset = 0; | ||
317 | zr->list_it = GNUNET_NAMESTORE_zone_iteration_start (ns, | ||
318 | fcfs_zone_pkey, | ||
319 | &iterate_cb, | ||
320 | zr); | ||
321 | return MHD_YES; | ||
322 | } | ||
323 | |||
324 | |||
325 | /** | ||
326 | * Handler that returns a simple static HTTP page. | ||
327 | * | ||
328 | * @param connection connection to use | ||
329 | * @return MHD_YES on success | ||
330 | */ | ||
331 | static int | ||
332 | serve_main_page (struct MHD_Connection *connection) | ||
333 | { | ||
334 | int ret; | ||
335 | struct MHD_Response *response; | ||
336 | |||
337 | /* return static form */ | ||
338 | response = MHD_create_response_from_buffer (strlen (MAIN_PAGE), | ||
339 | (void *) MAIN_PAGE, | ||
340 | MHD_RESPMEM_PERSISTENT); | ||
341 | MHD_add_response_header (response, | ||
342 | MHD_HTTP_HEADER_CONTENT_TYPE, | ||
343 | MIME_HTML); | ||
344 | ret = MHD_queue_response (connection, | ||
345 | MHD_HTTP_OK, | ||
346 | response); | ||
347 | MHD_destroy_response (response); | ||
348 | return ret; | ||
349 | } | ||
350 | |||
351 | |||
352 | /** | ||
353 | * Send the 'SUBMIT_PAGE'. | ||
354 | * | ||
355 | * @param info information string to send to the user | ||
356 | * @param request request information | ||
357 | * @param connection connection to use | ||
358 | */ | ||
359 | static int | ||
360 | fill_s_reply (const char *info, | ||
361 | struct Request *request, | ||
362 | struct MHD_Connection *connection) | ||
363 | { | ||
364 | int ret; | ||
365 | char *reply; | ||
366 | struct MHD_Response *response; | ||
367 | |||
368 | GNUNET_asprintf (&reply, | ||
369 | SUBMIT_PAGE, | ||
370 | info, | ||
371 | info); | ||
372 | /* return static form */ | ||
373 | response = MHD_create_response_from_buffer (strlen (reply), | ||
374 | (void *) reply, | ||
375 | MHD_RESPMEM_MUST_FREE); | ||
376 | MHD_add_response_header (response, | ||
377 | MHD_HTTP_HEADER_CONTENT_TYPE, | ||
378 | MIME_HTML); | ||
379 | ret = MHD_queue_response (connection, | ||
380 | MHD_HTTP_OK, | ||
381 | response); | ||
382 | MHD_destroy_response (response); | ||
383 | return ret; | ||
384 | } | ||
385 | |||
386 | |||
387 | /** | ||
388 | * Iterator over key-value pairs where the value | ||
389 | * maybe made available in increments and/or may | ||
390 | * not be zero-terminated. Used for processing | ||
391 | * POST data. | ||
392 | * | ||
393 | * @param cls user-specified closure | ||
394 | * @param kind type of the value | ||
395 | * @param key 0-terminated key for the value | ||
396 | * @param filename name of the uploaded file, NULL if not known | ||
397 | * @param content_type mime-type of the data, NULL if not known | ||
398 | * @param transfer_encoding encoding of the data, NULL if not known | ||
399 | * @param data pointer to size bytes of data at the | ||
400 | * specified offset | ||
401 | * @param off offset of data in the overall value | ||
402 | * @param size number of bytes in data available | ||
403 | * @return MHD_YES to continue iterating, | ||
404 | * MHD_NO to abort the iteration | ||
405 | */ | ||
406 | static int | ||
407 | post_iterator (void *cls, | ||
408 | enum MHD_ValueKind kind, | ||
409 | const char *key, | ||
410 | const char *filename, | ||
411 | const char *content_type, | ||
412 | const char *transfer_encoding, | ||
413 | const char *data, uint64_t off, size_t size) | ||
414 | { | ||
415 | struct Request *request = cls; | ||
416 | |||
417 | if (0 == strcmp ("domain", key)) | ||
418 | { | ||
419 | if (size + off >= sizeof(request->domain_name)) | ||
420 | size = sizeof (request->domain_name) - off - 1; | ||
421 | memcpy (&request->domain_name[off], | ||
422 | data, | ||
423 | size); | ||
424 | request->domain_name[size+off] = '\0'; | ||
425 | return MHD_YES; | ||
426 | } | ||
427 | if (0 == strcmp ("pkey", key)) | ||
428 | { | ||
429 | if (size + off >= sizeof(request->public_key)) | ||
430 | size = sizeof (request->public_key) - off - 1; | ||
431 | memcpy (&request->public_key[off], | ||
432 | data, | ||
433 | size); | ||
434 | request->public_key[size+off] = '\0'; | ||
435 | return MHD_YES; | ||
436 | } | ||
437 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
438 | _("Unsupported form value `%s'\n"), | ||
439 | key); | ||
440 | return MHD_YES; | ||
441 | } | ||
442 | |||
443 | |||
444 | |||
445 | |||
446 | /** | ||
447 | * Continuation called to notify client about result of the | ||
448 | * operation. | ||
449 | * | ||
450 | * @param cls closure | ||
451 | * @param success GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate) | ||
452 | * GNUNET_NO if content was already there | ||
453 | * GNUNET_YES (or other positive value) on success | ||
454 | * @param emsg NULL on success, otherwise an error message | ||
455 | */ | ||
456 | static void | ||
457 | put_continuation (void *cls, | ||
458 | int32_t success, | ||
459 | const char *emsg) | ||
460 | { | ||
461 | struct Request *request = cls; | ||
462 | |||
463 | request->qe = NULL; | ||
464 | if (0 >= success) | ||
465 | { | ||
466 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
467 | _("Failed to create record for domain `%s': %s\n"), | ||
468 | request->domain_name, | ||
469 | emsg); | ||
470 | request->phase = RP_FAIL; | ||
471 | } | ||
472 | else | ||
473 | request->phase = RP_SUCCESS; | ||
474 | run_httpd_now (); | ||
475 | } | ||
476 | |||
477 | |||
478 | /** | ||
479 | * Test if a name mapping was found, if so, refuse. If not, initiate storing of the record. | ||
480 | * | ||
481 | * @param cls closure | ||
482 | * @param zone_key public key of the zone | ||
483 | * @param name name that is being mapped (at most 255 characters long) | ||
484 | * @param rd_count number of entries in 'rd' array | ||
485 | * @param rd array of records with data to store | ||
486 | */ | ||
487 | static void | ||
488 | zone_to_name_cb (void *cls, | ||
489 | const struct GNUNET_CRYPTO_EccPrivateKey *zone_key, | ||
490 | const char *name, | ||
491 | unsigned int rd_count, | ||
492 | const struct GNUNET_NAMESTORE_RecordData *rd) | ||
493 | { | ||
494 | struct Request *request = cls; | ||
495 | struct GNUNET_NAMESTORE_RecordData r; | ||
496 | struct GNUNET_CRYPTO_ShortHashCode pub; | ||
497 | |||
498 | request->qe = NULL; | ||
499 | if (NULL != name) | ||
500 | { | ||
501 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
502 | _("Found existing name `%s' for the given key\n"), | ||
503 | name); | ||
504 | request->phase = RP_FAIL; | ||
505 | run_httpd_now (); | ||
506 | return; | ||
507 | } | ||
508 | r.data = &pub; | ||
509 | r.data_size = sizeof (pub); | ||
510 | r.expiration_time = UINT64_MAX; | ||
511 | r.record_type = GNUNET_NAMESTORE_TYPE_PKEY; | ||
512 | r.flags = GNUNET_NAMESTORE_RF_AUTHORITY; | ||
513 | request->qe = GNUNET_NAMESTORE_records_store (ns, | ||
514 | fcfs_zone_pkey, | ||
515 | request->domain_name, | ||
516 | 1, &r, | ||
517 | &put_continuation, | ||
518 | request); | ||
519 | } | ||
520 | |||
521 | |||
522 | /** | ||
523 | * Process a record that was stored in the namestore. Used to check if | ||
524 | * the requested name already exists in the namestore. If not, | ||
525 | * proceed to check if the requested key already exists. | ||
526 | * | ||
527 | * @param cls closure | ||
528 | * @param zone_key private key of the zone | ||
529 | * @param name name that is being mapped (at most 255 characters long) | ||
530 | * @param rd_count number of entries in 'rd' array | ||
531 | * @param rd array of records with data to store | ||
532 | */ | ||
533 | static void | ||
534 | lookup_result_processor (void *cls, | ||
535 | const struct GNUNET_CRYPTO_EccPrivateKey *zone_key, | ||
536 | const char *name, | ||
537 | unsigned int rd_count, | ||
538 | const struct GNUNET_NAMESTORE_RecordData *rd) | ||
539 | { | ||
540 | struct Request *request = cls; | ||
541 | struct GNUNET_CRYPTO_EccPublicKey pub; | ||
542 | |||
543 | request->qe = NULL; | ||
544 | if (0 != rd_count) | ||
545 | { | ||
546 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
547 | _("Found %u existing records for domain `%s'\n"), | ||
548 | rd_count, | ||
549 | request->domain_name); | ||
550 | request->phase = RP_FAIL; | ||
551 | run_httpd_now (); | ||
552 | return; | ||
553 | } | ||
554 | if (GNUNET_OK != | ||
555 | GNUNET_CRYPTO_ecc_public_key_from_string (request->public_key, | ||
556 | strlen (request->public_key), | ||
557 | &pub)) | ||
558 | { | ||
559 | GNUNET_break (0); | ||
560 | request->phase = RP_FAIL; | ||
561 | run_httpd_now (); | ||
562 | return; | ||
563 | } | ||
564 | request->qe = GNUNET_NAMESTORE_zone_to_name (ns, | ||
565 | fcfs_zone_pkey, | ||
566 | &pub, | ||
567 | &zone_to_name_cb, | ||
568 | request); | ||
569 | } | ||
570 | |||
571 | |||
572 | /** | ||
573 | * Main MHD callback for handling requests. | ||
574 | * | ||
575 | * @param cls unused | ||
576 | * @param connection MHD connection handle | ||
577 | * @param url the requested url | ||
578 | * @param method the HTTP method used ("GET", "PUT", etc.) | ||
579 | * @param version the HTTP version string (i.e. "HTTP/1.1") | ||
580 | * @param upload_data the data being uploaded (excluding HEADERS, | ||
581 | * for a POST that fits into memory and that is encoded | ||
582 | * with a supported encoding, the POST data will NOT be | ||
583 | * given in upload_data and is instead available as | ||
584 | * part of MHD_get_connection_values; very large POST | ||
585 | * data *will* be made available incrementally in | ||
586 | * upload_data) | ||
587 | * @param upload_data_size set initially to the size of the | ||
588 | * upload_data provided; the method must update this | ||
589 | * value to the number of bytes NOT processed; | ||
590 | * @param ptr pointer to location where we store the 'struct Request' | ||
591 | * @return MHD_YES if the connection was handled successfully, | ||
592 | * MHD_NO if the socket must be closed due to a serious | ||
593 | * error while handling the request | ||
594 | */ | ||
595 | static int | ||
596 | create_response (void *cls, | ||
597 | struct MHD_Connection *connection, | ||
598 | const char *url, | ||
599 | const char *method, | ||
600 | const char *version, | ||
601 | const char *upload_data, | ||
602 | size_t *upload_data_size, | ||
603 | void **ptr) | ||
604 | { | ||
605 | struct MHD_Response *response; | ||
606 | struct Request *request; | ||
607 | int ret; | ||
608 | struct GNUNET_CRYPTO_EccPublicKey pub; | ||
609 | |||
610 | if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) || | ||
611 | (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) ) | ||
612 | { | ||
613 | if (0 == strcmp (url, FCFS_ZONEINFO_URL)) | ||
614 | ret = serve_zoneinfo_page (connection); | ||
615 | else | ||
616 | ret = serve_main_page (connection); | ||
617 | if (ret != MHD_YES) | ||
618 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
619 | _("Failed to create page for `%s'\n"), | ||
620 | url); | ||
621 | return ret; | ||
622 | } | ||
623 | if (0 == strcmp (method, MHD_HTTP_METHOD_POST)) | ||
624 | { | ||
625 | request = *ptr; | ||
626 | if (NULL == request) | ||
627 | { | ||
628 | request = GNUNET_malloc (sizeof (struct Request)); | ||
629 | *ptr = request; | ||
630 | request->pp = MHD_create_post_processor (connection, 1024, | ||
631 | &post_iterator, request); | ||
632 | if (NULL == request->pp) | ||
633 | { | ||
634 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
635 | _("Failed to setup post processor for `%s'\n"), | ||
636 | url); | ||
637 | return MHD_NO; /* internal error */ | ||
638 | } | ||
639 | return MHD_YES; | ||
640 | } | ||
641 | if (NULL != request->pp) | ||
642 | { | ||
643 | /* evaluate POST data */ | ||
644 | MHD_post_process (request->pp, | ||
645 | upload_data, | ||
646 | *upload_data_size); | ||
647 | if (0 != *upload_data_size) | ||
648 | { | ||
649 | *upload_data_size = 0; | ||
650 | return MHD_YES; | ||
651 | } | ||
652 | /* done with POST data, serve response */ | ||
653 | MHD_destroy_post_processor (request->pp); | ||
654 | request->pp = NULL; | ||
655 | } | ||
656 | if (GNUNET_OK != | ||
657 | GNUNET_CRYPTO_ecc_public_key_from_string (request->public_key, | ||
658 | strlen (request->public_key), | ||
659 | &pub)) | ||
660 | { | ||
661 | /* parse error */ | ||
662 | return fill_s_reply ("Failed to parse given public key", | ||
663 | request, connection); | ||
664 | } | ||
665 | switch (request->phase) | ||
666 | { | ||
667 | case RP_START: | ||
668 | if (NULL != strchr (request->domain_name, (int) '.')) | ||
669 | { | ||
670 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
671 | _("Domain name must not contain `.'\n")); | ||
672 | request->phase = RP_FAIL; | ||
673 | return fill_s_reply ("Domain name must not contain `.', sorry.", | ||
674 | request, connection); | ||
675 | } | ||
676 | if (NULL != strchr (request->domain_name, (int) '+')) | ||
677 | { | ||
678 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
679 | _("Domain name must not contain `+'\n")); | ||
680 | request->phase = RP_FAIL; | ||
681 | return fill_s_reply ("Domain name must not contain `+', sorry.", | ||
682 | request, connection); | ||
683 | } | ||
684 | request->phase = RP_LOOKUP; | ||
685 | GNUNET_CRYPTO_ecc_key_get_public (fcfs_zone_pkey, | ||
686 | &pub); | ||
687 | request->qe = GNUNET_NAMESTORE_lookup (ns, | ||
688 | &pub, | ||
689 | request->domain_name, | ||
690 | &lookup_result_processor, | ||
691 | request); | ||
692 | break; | ||
693 | case RP_LOOKUP: | ||
694 | break; | ||
695 | case RP_PUT: | ||
696 | break; | ||
697 | case RP_FAIL: | ||
698 | return fill_s_reply ("Request failed, sorry.", | ||
699 | request, connection); | ||
700 | case RP_SUCCESS: | ||
701 | return fill_s_reply ("Success.", | ||
702 | request, connection); | ||
703 | default: | ||
704 | GNUNET_break (0); | ||
705 | return MHD_NO; | ||
706 | } | ||
707 | return MHD_YES; /* will have a reply later... */ | ||
708 | } | ||
709 | /* unsupported HTTP method */ | ||
710 | response = MHD_create_response_from_buffer (strlen (METHOD_ERROR), | ||
711 | (void *) METHOD_ERROR, | ||
712 | MHD_RESPMEM_PERSISTENT); | ||
713 | ret = MHD_queue_response (connection, | ||
714 | MHD_HTTP_METHOD_NOT_ACCEPTABLE, | ||
715 | response); | ||
716 | MHD_destroy_response (response); | ||
717 | return ret; | ||
718 | } | ||
719 | |||
720 | |||
721 | /** | ||
722 | * Callback called upon completion of a request. | ||
723 | * Decrements session reference counter. | ||
724 | * | ||
725 | * @param cls not used | ||
726 | * @param connection connection that completed | ||
727 | * @param con_cls session handle | ||
728 | * @param toe status code | ||
729 | */ | ||
730 | static void | ||
731 | request_completed_callback (void *cls, | ||
732 | struct MHD_Connection *connection, | ||
733 | void **con_cls, | ||
734 | enum MHD_RequestTerminationCode toe) | ||
735 | { | ||
736 | struct Request *request = *con_cls; | ||
737 | |||
738 | if (NULL == request) | ||
739 | return; | ||
740 | if (NULL != request->pp) | ||
741 | MHD_destroy_post_processor (request->pp); | ||
742 | if (NULL != request->qe) | ||
743 | GNUNET_NAMESTORE_cancel (request->qe); | ||
744 | GNUNET_free (request); | ||
745 | } | ||
746 | |||
747 | |||
748 | #define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG | ||
749 | |||
750 | |||
751 | /** | ||
752 | * Schedule tasks to run MHD server. | ||
753 | */ | ||
754 | static void | ||
755 | run_httpd () | ||
756 | { | ||
757 | fd_set rs; | ||
758 | fd_set ws; | ||
759 | fd_set es; | ||
760 | struct GNUNET_NETWORK_FDSet *wrs; | ||
761 | struct GNUNET_NETWORK_FDSet *wws; | ||
762 | struct GNUNET_NETWORK_FDSet *wes; | ||
763 | int max; | ||
764 | int haveto; | ||
765 | UNSIGNED_MHD_LONG_LONG timeout; | ||
766 | struct GNUNET_TIME_Relative tv; | ||
767 | |||
768 | FD_ZERO (&rs); | ||
769 | FD_ZERO (&ws); | ||
770 | FD_ZERO (&es); | ||
771 | wrs = GNUNET_NETWORK_fdset_create (); | ||
772 | wes = GNUNET_NETWORK_fdset_create (); | ||
773 | wws = GNUNET_NETWORK_fdset_create (); | ||
774 | max = -1; | ||
775 | GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max)); | ||
776 | haveto = MHD_get_timeout (httpd, &timeout); | ||
777 | if (haveto == MHD_YES) | ||
778 | tv.rel_value_us = (uint64_t) timeout * 1000LL; | ||
779 | else | ||
780 | tv = GNUNET_TIME_UNIT_FOREVER_REL; | ||
781 | GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1); | ||
782 | GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1); | ||
783 | GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1); | ||
784 | httpd_task = | ||
785 | GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH, | ||
786 | tv, wrs, wws, | ||
787 | &do_httpd, NULL); | ||
788 | GNUNET_NETWORK_fdset_destroy (wrs); | ||
789 | GNUNET_NETWORK_fdset_destroy (wws); | ||
790 | GNUNET_NETWORK_fdset_destroy (wes); | ||
791 | } | ||
792 | |||
793 | |||
794 | /** | ||
795 | * Task run whenever HTTP server operations are pending. | ||
796 | * | ||
797 | * @param cls unused | ||
798 | * @param tc scheduler context | ||
799 | */ | ||
800 | static void | ||
801 | do_httpd (void *cls, | ||
802 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
803 | { | ||
804 | httpd_task = GNUNET_SCHEDULER_NO_TASK; | ||
805 | MHD_run (httpd); | ||
806 | run_httpd (); | ||
807 | } | ||
808 | |||
809 | |||
810 | /** | ||
811 | * Task run on shutdown. Cleans up everything. | ||
812 | * | ||
813 | * @param cls unused | ||
814 | * @param tc scheduler context | ||
815 | */ | ||
816 | static void | ||
817 | do_shutdown (void *cls, | ||
818 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
819 | { | ||
820 | if (GNUNET_SCHEDULER_NO_TASK != httpd_task) | ||
821 | { | ||
822 | GNUNET_SCHEDULER_cancel (httpd_task); | ||
823 | httpd_task = GNUNET_SCHEDULER_NO_TASK; | ||
824 | } | ||
825 | if (NULL != ns) | ||
826 | { | ||
827 | GNUNET_NAMESTORE_disconnect (ns); | ||
828 | ns = NULL; | ||
829 | } | ||
830 | if (NULL != httpd) | ||
831 | { | ||
832 | MHD_stop_daemon (httpd); | ||
833 | httpd = NULL; | ||
834 | } | ||
835 | if (NULL != fcfs_zone_pkey) | ||
836 | { | ||
837 | GNUNET_CRYPTO_ecc_key_free (fcfs_zone_pkey); | ||
838 | fcfs_zone_pkey = NULL; | ||
839 | } | ||
840 | } | ||
841 | |||
842 | |||
843 | /** | ||
844 | * Main function that will be run. | ||
845 | * | ||
846 | * @param cls closure | ||
847 | * @param args remaining command-line arguments | ||
848 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
849 | * @param cfg configuration | ||
850 | */ | ||
851 | static void | ||
852 | run (void *cls, char *const *args, const char *cfgfile, | ||
853 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
854 | { | ||
855 | char *keyfile; | ||
856 | unsigned long long port; | ||
857 | |||
858 | if (GNUNET_OK != | ||
859 | GNUNET_CONFIGURATION_get_value_number (cfg, | ||
860 | "fcfsd", | ||
861 | "HTTPPORT", | ||
862 | &port)) | ||
863 | { | ||
864 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
865 | "fcfsd", "HTTPPORT"); | ||
866 | return; | ||
867 | } | ||
868 | if (GNUNET_OK != | ||
869 | GNUNET_CONFIGURATION_get_value_filename (cfg, | ||
870 | "fcfsd", | ||
871 | "ZONEKEY", | ||
872 | &keyfile)) | ||
873 | { | ||
874 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
875 | "fcfsd", "ZONEKEY"); | ||
876 | return; | ||
877 | } | ||
878 | fcfs_zone_pkey = GNUNET_CRYPTO_ecc_key_create_from_file (keyfile); | ||
879 | GNUNET_free (keyfile); | ||
880 | if (NULL == fcfs_zone_pkey) | ||
881 | { | ||
882 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
883 | _("Failed to read or create private zone key\n")); | ||
884 | return; | ||
885 | } | ||
886 | ns = GNUNET_NAMESTORE_connect (cfg); | ||
887 | if (NULL == ns) | ||
888 | { | ||
889 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
890 | _("Failed to connect to namestore\n")); | ||
891 | return; | ||
892 | } | ||
893 | httpd = MHD_start_daemon (MHD_USE_DEBUG, | ||
894 | (uint16_t) port, | ||
895 | NULL, NULL, | ||
896 | &create_response, NULL, | ||
897 | MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128, | ||
898 | MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1, | ||
899 | MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16, | ||
900 | MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (4 * 1024), | ||
901 | MHD_OPTION_NOTIFY_COMPLETED, &request_completed_callback, NULL, | ||
902 | MHD_OPTION_END); | ||
903 | if (NULL == httpd) | ||
904 | { | ||
905 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
906 | _("Failed to start HTTP server\n")); | ||
907 | GNUNET_NAMESTORE_disconnect (ns); | ||
908 | ns = NULL; | ||
909 | return; | ||
910 | } | ||
911 | run_httpd (); | ||
912 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, | ||
913 | &do_shutdown, NULL); | ||
914 | } | ||
915 | |||
916 | |||
917 | /** | ||
918 | * The main function for the fcfs daemon. | ||
919 | * | ||
920 | * @param argc number of arguments from the command line | ||
921 | * @param argv command line arguments | ||
922 | * @return 0 ok, 1 on error | ||
923 | */ | ||
924 | int | ||
925 | main (int argc, char *const *argv) | ||
926 | { | ||
927 | static const struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
928 | GNUNET_GETOPT_OPTION_END | ||
929 | }; | ||
930 | |||
931 | int ret; | ||
932 | |||
933 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
934 | return 2; | ||
935 | |||
936 | GNUNET_log_setup ("fcfsd", "WARNING", NULL); | ||
937 | ret = | ||
938 | (GNUNET_OK == | ||
939 | GNUNET_PROGRAM_run (argc, argv, "fcfsd", | ||
940 | _("GNUnet GNS first come first serve registration service"), | ||
941 | options, | ||
942 | &run, NULL)) ? 0 : 1; | ||
943 | GNUNET_free ((void*) argv); | ||
944 | return ret; | ||
945 | } | ||
946 | |||
947 | /* end of gnunet-gns-fcfsd.c */ | ||