aboutsummaryrefslogtreecommitdiff
path: root/src/namestore/gnunet-namestore-fcfsd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/namestore/gnunet-namestore-fcfsd.c')
-rw-r--r--src/namestore/gnunet-namestore-fcfsd.c1594
1 files changed, 733 insertions, 861 deletions
diff --git a/src/namestore/gnunet-namestore-fcfsd.c b/src/namestore/gnunet-namestore-fcfsd.c
index 313aea6fc..ec99d13e5 100644
--- a/src/namestore/gnunet-namestore-fcfsd.c
+++ b/src/namestore/gnunet-namestore-fcfsd.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet. 2 This file is part of GNUnet.
3 Copyright (C) 2012-2014 GNUnet e.V. 3 Copyright (C) 2012-2021 GNUnet e.V.
4 4
5 GNUnet is free software: you can redistribute it and/or modify it 5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published 6 under the terms of the GNU Affero General Public License as published
@@ -17,19 +17,13 @@
17 17
18 SPDX-License-Identifier: AGPL3.0-or-later 18 SPDX-License-Identifier: AGPL3.0-or-later
19 */ 19 */
20
20/** 21/**
21 * @file gnunet-namestore-fcfsd.c 22 * @file gnunet-namestore-fcfsd.c
22 * @brief HTTP daemon that offers first-come-first-serve GNS domain registration 23 * @brief HTTP daemon that offers first-come-first-serve GNS domain registration
23 * @author Christian Grothoff 24 * @author Christian Grothoff
24 *
25 * TODO:
26 * - need to track active zone info requests so we can cancel them
27 * during shutdown, right?
28 * - the code currently contains a 'race' between checking that the
29 * domain name is available and allocating it to the new public key
30 * (should this race be solved by namestore or by fcfsd?)
31 * - nicer error reporting to browser
32 */ 25 */
26
33#include "platform.h" 27#include "platform.h"
34#include <microhttpd.h> 28#include <microhttpd.h>
35#include "gnunet_util_lib.h" 29#include "gnunet_util_lib.h"
@@ -37,735 +31,618 @@
37#include "gnunet_gnsrecord_lib.h" 31#include "gnunet_gnsrecord_lib.h"
38#include "gnunet_namestore_service.h" 32#include "gnunet_namestore_service.h"
39#include "gnunet_mhd_compat.h" 33#include "gnunet_mhd_compat.h"
34#include "gnunet_json_lib.h"
40 35
41/** 36/**
42 * Invalid method page. 37 * Structure representing a static page.
43 */ 38 * "Static" means that the server does not process the page before sending it
44#define METHOD_ERROR \ 39 * to the client. Clients can still process the received data, for example
45 "<!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 * because there are scripting elements within.
46
47/**
48 * Front page. (/)
49 */
50#define MAIN_PAGE \
51 "<!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>"
52
53/**
54 * Second page (/S)
55 */
56#define SUBMIT_PAGE \
57 "<!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>"
58
59/**
60 * Fcfs zoneinfo page (/Zoneinfo)
61 */
62#define ZONEINFO_PAGE \
63 "<!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>"
64
65#define FCFS_ZONEINFO_URL "/Zoneinfo"
66
67/**
68 * Mime type for HTML pages.
69 */
70#define MIME_HTML "text/html"
71
72/**
73 * Name of our cookie.
74 */
75#define COOKIE_NAME "namestore-fcfsd"
76
77#define DEFAULT_ZONEINFO_BUFSIZE 2048
78
79/**
80 * Phases a request goes through.
81 */ 41 */
82enum Phase 42struct StaticPage
83{ 43{
84 /** 44 /**
85 * Start phase (parsing POST, checking). 45 * Handle to file on disk.
86 */
87 RP_START = 0,
88
89 /**
90 * Lookup to see if the domain name is taken.
91 */ 46 */
92 RP_LOOKUP, 47 struct GNUNET_DISK_FileHandle *handle;
93 48
94 /** 49 /**
95 * Storing of the record. 50 * Size in bytes of the file.
96 */ 51 */
97 RP_PUT, 52 uint64_t size;
98 53
99 /** 54 /**
100 * We're done with success. 55 * Cached response object to send to clients.
101 */ 56 */
102 RP_SUCCESS, 57 struct MHD_Response *response;
103
104 /**
105 * Send failure message.
106 */
107 RP_FAIL
108}; 58};
109 59
110
111/** 60/**
112 * Data kept per request. 61 * Structure containing some request-specific data.
113 */ 62 */
114struct Request 63struct RequestData
115{ 64{
116 /** 65 /**
117 * Associated session. 66 * The connection this request was sent in.
118 */
119 // FIXME: struct Session *session;
120
121 /**
122 * Post processor handling form data (IF this is
123 * a POST request).
124 */ 67 */
125 struct MHD_PostProcessor *pp; 68 struct MHD_Connection *c;
126 69
127 /** 70 /**
128 * MHD Connection 71 * Body of the response object.
129 */
130 struct MHD_Connection *con;
131 /**
132 * URL to serve in response to this POST (if this request
133 * was a 'POST')
134 */ 72 */
135 const char *post_url; 73 char *body;
136 74
137 /** 75 /**
138 * Active request with the namestore. 76 * Length in bytes of the body.
139 */ 77 */
140 struct GNUNET_NAMESTORE_QueueEntry *qe; 78 size_t body_length;
141 79
142 /** 80 /**
143 * Active lookup iterator 81 * Response code.
144 * TODO: deprecate or fix lookup by label and use above member
145 */ 82 */
146 struct GNUNET_NAMESTORE_ZoneIterator *lookup_it; 83 int code;
147 /**
148 * Active iteration with the namestore.
149 */
150 struct GNUNET_NAMESTORE_ZoneIterator *zi;
151 84
152 /** 85 /**
153 * Current processing phase. 86 * Task started to search for an entry in the namestore.
154 */ 87 */
155 enum Phase phase; 88 struct GNUNET_NAMESTORE_QueueEntry *searching;
156 89
157 /** 90 /**
158 * Domain name submitted via form. 91 * Task started to iterate over the namestore.
159 */ 92 */
160 char domain_name[64]; 93 struct GNUNET_NAMESTORE_ZoneIterator *iterating;
161 94
162 /** 95 /**
163 * Public key submitted via form. 96 * Pointer used while processing POST data.
164 */
165 char public_key[128];
166
167 struct GNUNET_IDENTITY_PublicKey pub;
168};
169
170/**
171 * Zoneinfo request
172 */
173struct ZoneinfoRequest
174{
175 /**
176 * List iterator
177 */ 97 */
178 struct GNUNET_NAMESTORE_ZoneIterator *list_it; 98 void *ptr;
179 99
180 /** 100 /**
181 * Buffer 101 * Name requested to be registered.
182 */ 102 */
183 char*zoneinfo; 103 char *register_name;
184 104
185 /** 105 /**
186 * Buffer length 106 * Key (encoded as a string) to be associated with the requested name.
187 */ 107 */
188 size_t buf_len; 108 char *register_key;
189 109
190 /** 110 /**
191 * Buffer write offset 111 * Key to be associated with the requested name.
192 */ 112 */
193 size_t write_offset; 113 struct GNUNET_IDENTITY_PublicKey key;
194}; 114};
195 115
196/** 116/**
197 * MHD daemon reference. 117 * Name of the zone being managed.
198 */ 118 */
199static struct MHD_Daemon *httpd; 119static char *zone = NULL;
200 120
201/** 121/**
202 * Main HTTP task. 122 * The port the daemon is listening to for HTTP requests.
203 */ 123 */
204static struct GNUNET_SCHEDULER_Task *httpd_task; 124static unsigned long long port = 18080;
205 125
206/** 126/**
207 * Handle to the namestore. 127 * Connection with the namestore service.
208 */ 128 */
209static struct GNUNET_NAMESTORE_Handle *ns; 129static struct GNUNET_NAMESTORE_Handle *namestore = NULL;
210 130
211/** 131/**
212 * Private key for the fcfsd zone. 132 * Connection with the identity service.
213 */ 133 */
214static struct GNUNET_IDENTITY_PrivateKey fcfs_zone_pkey; 134static struct GNUNET_IDENTITY_Handle *identity = NULL;
215 135
216/** 136/**
217 * Connection to identity service. 137 * Private key of the zone.
218 */ 138 */
219static struct GNUNET_IDENTITY_Handle *identity; 139static const struct GNUNET_IDENTITY_PrivateKey *zone_key = NULL;
220 140
221/** 141/**
222 * Zoneinfo page we currently use. 142 * The HTTP daemon.
223 */ 143 */
224static struct MHD_Response *info_page; 144static struct MHD_Daemon *httpd = NULL;
225 145
226/** 146/**
227 * Task that runs #update_zoneinfo_page peridicially. 147 * Task executing the HTTP daemon.
228 */ 148 */
229static struct GNUNET_SCHEDULER_Task *uzp_task; 149static struct GNUNET_SCHEDULER_Task *httpd_task = NULL;
230 150
231/** 151/**
232 * Request for our ego. 152 * The main page, a.k.a. "index.html"
233 */ 153 */
234static struct GNUNET_IDENTITY_Operation *id_op; 154static struct StaticPage *main_page = NULL;
235 155
236/** 156/**
237 * Port we use for the HTTP server. 157 * Page indicating the requested resource could not be found.
238 */ 158 */
239static unsigned long long port; 159static struct StaticPage *notfound_page = NULL;
240 160
241/** 161/**
242 * Name of the zone we manage. 162 * Page indicating the requested resource could not be accessed, and other
163 * errors.
243 */ 164 */
244static char *zone; 165static struct StaticPage *forbidden_page = NULL;
245
246 166
247/** 167/**
248 * Task run whenever HTTP server operations are pending. 168 * Task ran at shutdown to clean up everything.
249 * 169 *
250 * @param cls unused 170 * @param cls unused
251 */ 171 */
252static void 172static void
253do_httpd (void *cls); 173do_shutdown (void *cls)
174{
175 /* We cheat a bit here: the file descriptor is implicitly closed by MHD, so
176 calling `GNUNET_DISK_file_close' would generate a spurious warning message
177 in the log. Since that function does nothing but close the descriptor and
178 free the allocated memory, After destroying the response all that's left to
179 do is call `GNUNET_free'. */
180 if (NULL != main_page)
181 {
182 MHD_destroy_response (main_page->response);
183 GNUNET_free (main_page->handle);
184 GNUNET_free (main_page);
185 }
186 if (NULL != notfound_page)
187 {
188 MHD_destroy_response (main_page->response);
189 GNUNET_free (main_page->handle);
190 GNUNET_free (main_page);
191 }
192 if (NULL != forbidden_page)
193 {
194 MHD_destroy_response (main_page->response);
195 GNUNET_free (main_page->handle);
196 GNUNET_free (main_page);
197 }
254 198
199 if (NULL != namestore)
200 {
201 GNUNET_NAMESTORE_disconnect (namestore);
202 }
255 203
256/** 204 if (NULL != identity)
257 * Schedule task to run MHD server now.
258 */
259static void
260run_httpd_now ()
261{
262 if (NULL != httpd_task)
263 { 205 {
264 GNUNET_SCHEDULER_cancel (httpd_task); 206 GNUNET_IDENTITY_disconnect (identity);
265 httpd_task = NULL;
266 } 207 }
267 httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL);
268} 208}
269 209
270
271/** 210/**
272 * Create fresh version of zone information. 211 * Called when the HTTP server has some pending operations.
212 *
213 * @param cls unused
273 */ 214 */
274static void 215static void
275update_zoneinfo_page (void *cls); 216do_httpd (void *cls);
276
277 217
278/** 218/**
279 * Function called on error in zone iteration. 219 * Schedule a task to run MHD.
280 */ 220 */
281static void 221static void
282zone_iteration_error (void *cls) 222run_httpd (void)
283{ 223{
284 struct ZoneinfoRequest *zr = cls; 224 fd_set rs;
225 fd_set ws;
226 fd_set es;
285 227
286 zr->list_it = NULL; 228 struct GNUNET_NETWORK_FDSet *grs = GNUNET_NETWORK_fdset_create ();
287 GNUNET_free (zr->zoneinfo); 229 struct GNUNET_NETWORK_FDSet *gws = GNUNET_NETWORK_fdset_create ();
288 GNUNET_SCHEDULER_cancel (uzp_task); 230 struct GNUNET_NETWORK_FDSet *ges = GNUNET_NETWORK_fdset_create ();
289 uzp_task = GNUNET_SCHEDULER_add_now (&update_zoneinfo_page,
290 NULL);
291}
292 231
232 FD_ZERO (&rs);
233 FD_ZERO (&ws);
234 FD_ZERO (&es);
235
236 int max = -1;
237 GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
238
239 unsigned MHD_LONG_LONG timeout = 0;
240 struct GNUNET_TIME_Relative gtime = GNUNET_TIME_UNIT_FOREVER_REL;
241 if (MHD_YES == MHD_get_timeout (httpd, &timeout))
242 {
243 gtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
244 timeout);
245 }
246
247 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
248 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
249 GNUNET_NETWORK_fdset_copy_native (ges, &es, max + 1);
250
251 httpd_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
252 gtime,
253 grs,
254 gws,
255 &do_httpd,
256 NULL);
257 GNUNET_NETWORK_fdset_destroy (grs);
258 GNUNET_NETWORK_fdset_destroy (gws);
259 GNUNET_NETWORK_fdset_destroy (ges);
260}
293 261
294/** 262/**
295 * Function called once the zone iteration is done. 263 * Called when the HTTP server has some pending operations.
264 *
265 * @param cls unused
296 */ 266 */
297static void 267static void
298zone_iteration_end (void *cls) 268do_httpd (void *cls)
299{ 269{
300 struct ZoneinfoRequest *zr = cls; 270 httpd_task = NULL;
301 struct MHD_Response *response; 271 MHD_run (httpd);
302 char*full_page; 272 run_httpd ();
303
304 zr->list_it = NULL;
305
306 /* return static form */
307 GNUNET_asprintf (&full_page,
308 ZONEINFO_PAGE,
309 zr->zoneinfo);
310 response = MHD_create_response_from_buffer (strlen (full_page),
311 (void *) full_page,
312 MHD_RESPMEM_MUST_FREE);
313 MHD_add_response_header (response,
314 MHD_HTTP_HEADER_CONTENT_TYPE,
315 MIME_HTML);
316 MHD_destroy_response (info_page);
317 info_page = response;
318 GNUNET_free (zr->zoneinfo);
319} 273}
320 274
275static void
276run_httpd_now (void)
277{
278 if (NULL != httpd_task)
279 {
280 GNUNET_SCHEDULER_cancel (httpd_task);
281 httpd_task = NULL;
282 }
283 httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL);
284}
321 285
322/** 286/**
323 * Process a record that was stored in the namestore, adding 287 * Generate a JSON object.
324 * the information to the HTML.
325 * 288 *
326 * @param cls closure with the `struct ZoneinfoRequest *` 289 * @param key the key for the first element
327 * @param zone_key private key of the zone; NULL on disconnect 290 * @param value the value for the first element
328 * @param name label of the records; NULL on disconnect 291 * @param ... key-value pairs of the object, terminated by NULL
329 * @param rd_len number of entries in @a rd array, 0 if label was deleted 292 * @return a JSON string (allocated)
330 * @param rd array of records with data to store
331 */ 293 */
332static void 294static char *
333iterate_cb (void *cls, 295make_json (const char *key, const char *value, ...)
334 const struct GNUNET_IDENTITY_PrivateKey *zone_key,
335 const char *name,
336 unsigned int rd_len,
337 const struct GNUNET_GNSRECORD_Data *rd)
338{ 296{
339 struct ZoneinfoRequest *zr = cls; 297 va_list args;
340 size_t bytes_free; 298 va_start(args, value);
341 char*pkey; 299
342 char*new_buf; 300 json_t *obj = NULL;
343 301
344 (void) zone_key; 302 obj = json_object ();
345 if (1 != rd_len) 303 if (NULL == key || NULL == value)
346 { 304 {
347 GNUNET_NAMESTORE_zone_iterator_next (zr->list_it, 305 va_end (args);
348 1); 306 return json_dumps (obj, JSON_COMPACT);
349 return;
350 } 307 }
351 308
352 if ((GNUNET_GNSRECORD_TYPE_PKEY != rd->record_type) && 309 json_object_set (obj, key, json_string (value));
353 (GNUNET_GNSRECORD_TYPE_EDKEY != rd->record_type)) 310
311 char *k = va_arg (args, char *);
312 if (NULL == k)
354 { 313 {
355 GNUNET_NAMESTORE_zone_iterator_next (zr->list_it, 314 va_end (args);
356 1); 315 return json_dumps (obj, JSON_COMPACT);
357 return;
358 } 316 }
359 317 char *v = va_arg (args, char *);
360 bytes_free = zr->buf_len - zr->write_offset; 318 if (NULL == v)
361 pkey = GNUNET_GNSRECORD_value_to_string (rd->record_type,
362 rd->data,
363 rd->data_size);
364 if (NULL == pkey)
365 { 319 {
366 GNUNET_break (0); 320 va_end (args);
367 GNUNET_NAMESTORE_zone_iterator_next (zr->list_it, 321 return json_dumps (obj, JSON_COMPACT);
368 1);
369 return;
370 } 322 }
371 if (bytes_free < (strlen (name) + strlen (pkey) + 40)) 323
324 while (NULL != k && NULL != v)
372 { 325 {
373 new_buf = GNUNET_malloc (zr->buf_len * 2); 326 json_object_set (obj, k, json_string (v));
374 GNUNET_memcpy (new_buf, zr->zoneinfo, zr->write_offset); 327 k = va_arg (args, char *);
375 GNUNET_free (zr->zoneinfo); 328 if (NULL != k)
376 zr->zoneinfo = new_buf; 329 {
377 zr->buf_len *= 2; 330 v = va_arg (args, char *);
331 }
378 } 332 }
379 sprintf (zr->zoneinfo + zr->write_offset,
380 "<tr><td>%s</td><td>%s</td></tr>",
381 name,
382 pkey);
383 zr->write_offset = strlen (zr->zoneinfo);
384 GNUNET_NAMESTORE_zone_iterator_next (zr->list_it,
385 1);
386 GNUNET_free (pkey);
387}
388 333
334 va_end (args);
389 335
390/** 336 char *json = json_dumps (obj, JSON_COMPACT);
391 * Handler that returns FCFS zoneinfo page. 337 json_decref (obj);
392 *
393 * @param connection connection to use
394 */
395static int
396serve_zoneinfo_page (struct MHD_Connection *connection)
397{
398 return MHD_queue_response (connection,
399 MHD_HTTP_OK,
400 info_page);
401}
402 338
339 return json;
340}
403 341
404/** 342/**
405 * Create fresh version of zone information. 343 * The namestore search task failed.
344 *
345 * @param cls the request data
406 */ 346 */
407static void 347static void
408update_zoneinfo_page (void *cls) 348search_error_cb (void *cls)
409{ 349{
410 static struct ZoneinfoRequest zr; 350 struct RequestData *rd = cls;
411 351 MHD_resume_connection (rd->c);
412 (void) cls; 352 rd->searching = NULL;
413 uzp_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, 353 rd->body = make_json ("error", "true",
414 &update_zoneinfo_page, 354 "message", _ ("can not search the namestore"),
415 NULL); 355 NULL);
416 if (NULL != zr.list_it) 356 rd->body_length = strlen (rd->body);
417 return; 357 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
418 zr.zoneinfo = GNUNET_malloc (DEFAULT_ZONEINFO_BUFSIZE); 358 run_httpd_now ();
419 zr.buf_len = DEFAULT_ZONEINFO_BUFSIZE;
420 zr.write_offset = 0;
421 zr.list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
422 &fcfs_zone_pkey,
423 &zone_iteration_error,
424 &zr,
425 &iterate_cb,
426 &zr,
427 &zone_iteration_end,
428 &zr);
429} 359}
430 360
431
432/** 361/**
433 * Handler that returns a simple static HTTP page. 362 * The lookup terminated with some results.
434 * 363 *
435 * @param connection connection to use 364 * @param cls closure
436 * @return #MHD_YES on success 365 * @param zone the private key of the zone
366 * @param label the result label
367 * @param count number of records found
368 * @param d records found
437 */ 369 */
438static int 370static void
439serve_main_page (struct MHD_Connection *connection) 371search_done_cb (void *cls,
372 const struct GNUNET_IDENTITY_PrivateKey *zone,
373 const char *label,
374 unsigned int count,
375 const struct GNUNET_GNSRECORD_Data *d)
440{ 376{
441 int ret; 377 (void) zone;
442 struct MHD_Response *response; 378 (void) d;
443 379
444 /* return static form */ 380 struct RequestData *rd = cls;
445 response = MHD_create_response_from_buffer (strlen (MAIN_PAGE), 381 MHD_resume_connection (rd->c);
446 (void *) MAIN_PAGE,
447 MHD_RESPMEM_PERSISTENT);
448 MHD_add_response_header (response,
449 MHD_HTTP_HEADER_CONTENT_TYPE,
450 MIME_HTML);
451 ret = MHD_queue_response (connection,
452 MHD_HTTP_OK,
453 response);
454 MHD_destroy_response (response);
455 return ret;
456}
457 382
383 rd->searching = NULL;
384 rd->body = make_json ("error", "false",
385 "free", (0 == count) ? "true" : "false",
386 NULL);
387 rd->body_length = strlen (rd->body);
388 rd->code = MHD_HTTP_OK;
458 389
459/** 390 run_httpd_now ();
460 * Send the 'SUBMIT_PAGE'.
461 *
462 * @param info information string to send to the user
463 * @param request request information
464 * @param connection connection to use
465 */
466static int
467fill_s_reply (const char *info,
468 struct Request *request,
469 struct MHD_Connection *connection)
470{
471 int ret;
472 char *reply;
473 struct MHD_Response *response;
474
475 (void) request;
476 GNUNET_asprintf (&reply,
477 SUBMIT_PAGE,
478 info,
479 info);
480 /* return static form */
481 response = MHD_create_response_from_buffer (strlen (reply),
482 (void *) reply,
483 MHD_RESPMEM_MUST_FREE);
484 MHD_add_response_header (response,
485 MHD_HTTP_HEADER_CONTENT_TYPE,
486 MIME_HTML);
487 ret = MHD_queue_response (connection,
488 MHD_HTTP_OK,
489 response);
490 MHD_destroy_response (response);
491 return ret;
492} 391}
493 392
494
495/** 393/**
496 * Iterator over key-value pairs where the value 394 * An error occurred while registering a name.
497 * maybe made available in increments and/or may
498 * not be zero-terminated. Used for processing
499 * POST data.
500 * 395 *
501 * @param cls user-specified closure 396 * @param cls the connection
502 * @param kind type of the value
503 * @param key 0-terminated key for the value
504 * @param filename name of the uploaded file, NULL if not known
505 * @param content_type mime-type of the data, NULL if not known
506 * @param transfer_encoding encoding of the data, NULL if not known
507 * @param data pointer to size bytes of data at the
508 * specified offset
509 * @param off offset of data in the overall value
510 * @param size number of bytes in data available
511 * @return #MHD_YES to continue iterating,
512 * #MHD_NO to abort the iteration
513 */ 397 */
514static MHD_RESULT 398static void
515post_iterator (void *cls, 399register_error_cb (void *cls)
516 enum MHD_ValueKind kind,
517 const char *key,
518 const char *filename,
519 const char *content_type,
520 const char *transfer_encoding,
521 const char *data,
522 uint64_t off,
523 size_t size)
524{ 400{
525 struct Request *request = cls; 401 struct RequestData *rd = cls;
526 402
527 (void) kind; 403 MHD_resume_connection (rd->c);
528 (void) filename; 404 rd->searching = NULL;
529 (void) content_type; 405 rd->body = make_json ("error", "true",
530 (void) transfer_encoding; 406 "message", _ ("unable to scan namestore"));
531 if (0 == strcmp ("domain", key)) 407 rd->body_length = strlen (rd->body);
532 { 408 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
533 if (size + off >= sizeof(request->domain_name)) 409 run_httpd_now ();
534 size = sizeof(request->domain_name) - off - 1;
535 GNUNET_memcpy (&request->domain_name[off],
536 data,
537 size);
538 request->domain_name[size + off] = '\0';
539 return MHD_YES;
540 }
541 if (0 == strcmp ("pkey", key))
542 {
543 if (size + off >= sizeof(request->public_key))
544 size = sizeof(request->public_key) - off - 1;
545 GNUNET_memcpy (&request->public_key[off],
546 data,
547 size);
548 request->public_key[size + off] = '\0';
549 return MHD_YES;
550 }
551 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
552 _ ("Unsupported form value `%s'\n"),
553 key);
554 return MHD_YES;
555} 410}
556 411
557
558/** 412/**
559 * Continuation called to notify client about result of the 413 * A name/key pair has been successfully registered, or maybe not.
560 * operation.
561 * 414 *
562 * @param cls closure 415 * @param cls the connection
563 * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate) 416 * @param status result of the operation
564 * #GNUNET_NO if content was already there 417 * @param emsg error message if any
565 * #GNUNET_YES (or other positive value) on success
566 * @param emsg NULL on success, otherwise an error message
567 */ 418 */
568static void 419static void
569put_continuation (void *cls, 420register_done_cb (void *cls,
570 int32_t success, 421 int32_t status,
571 const char *emsg) 422 const char *emsg)
572{ 423{
573 struct Request *request = cls; 424 struct RequestData *rd = cls;
425
426 MHD_resume_connection (rd->c);
427 rd->searching = NULL;
574 428
575 request->qe = NULL; 429 if (GNUNET_SYSERR == status || GNUNET_NO == status)
576 if (0 >= success)
577 { 430 {
578 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 431 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
579 _ ("Failed to create record for domain `%s': %s\n"), 432 _ ("Failed to create record for `%s': %s\n"),
580 request->domain_name, 433 rd->register_name,
581 emsg); 434 emsg);
582 request->phase = RP_FAIL; 435 rd->body = make_json ("error", "true",
436 "message", emsg);
437 rd->body_length = strlen (rd->body);
438 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
583 } 439 }
584 else 440 else
585 request->phase = RP_SUCCESS; 441 {
586 MHD_resume_connection (request->con); 442 rd->body = make_json ("error", "false",
443 "message", _ ("no errors"));
444 rd->body_length = strlen (rd->body);
445 rd->code = MHD_HTTP_OK;
446 }
447
587 run_httpd_now (); 448 run_httpd_now ();
588} 449}
589 450
590
591/** 451/**
592 * Function called if we had an error in zone-to-name mapping. 452 * Attempt to register the requested name.
453 *
454 * @param cls the connection
455 * @param key the zone key
456 * @param label name of the record
457 * @param count number of records found
458 * @param d records
593 */ 459 */
594static void 460static void
595zone_to_name_error (void *cls) 461register_do_cb (void *cls,
462 const struct GNUNET_IDENTITY_PrivateKey *key,
463 const char *label,
464 unsigned int count,
465 const struct GNUNET_GNSRECORD_Data *d)
596{ 466{
597 struct Request *request = cls; 467 (void) key;
468 (void) d;
598 469
599 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 470 struct RequestData *rd = cls;
600 _ ("Error when mapping zone to name\n"));
601 request->phase = RP_FAIL;
602 MHD_resume_connection (request->con);
603 run_httpd_now ();
604}
605 471
472 rd->searching = NULL;
606 473
607/** 474 if (0 != count)
608 * Test if a name mapping was found, if so, refuse. If not, initiate storing of the record.
609 *
610 * @param cls closure
611 * @param zone_key public key of the zone
612 * @param name name that is being mapped (at most 255 characters long)
613 * @param rd_count number of entries in @a rd array
614 * @param rd array of records with data to store
615 */
616static void
617zone_to_name_cb (void *cls,
618 const struct GNUNET_IDENTITY_PrivateKey *zone_key,
619 const char *name,
620 unsigned int rd_count,
621 const struct GNUNET_GNSRECORD_Data *rd)
622{
623 struct Request *request = cls;
624 struct GNUNET_GNSRECORD_Data r;
625 char *rdata;
626
627 (void) rd;
628 (void) zone_key;
629 request->qe = NULL;
630 if (0 != rd_count)
631 { 475 {
632 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 476 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
633 _ ("Found existing name `%s' for the given key\n"), 477 _ ("The requested key `%s' exists as `%s'\n"),
634 name); 478 rd->register_key,
635 request->phase = RP_FAIL; 479 label);
636 MHD_resume_connection (request->con); 480
481 MHD_resume_connection (rd->c);
482 rd->searching = NULL;
483 rd->body = make_json ("error", "true",
484 "message", _ ("key exists"));
485 rd->body_length = strlen (rd->body);
486 rd->code = MHD_HTTP_FORBIDDEN;
637 run_httpd_now (); 487 run_httpd_now ();
638 return; 488 return;
639 } 489 }
640 if (GNUNET_OK != GNUNET_GNSRECORD_data_from_identity (&request->pub, 490
641 &rdata, 491 struct GNUNET_GNSRECORD_Data gd;
642 &r.data_size, 492 char *gdraw = NULL;
643 &r.record_type)) 493
494 if (GNUNET_OK != GNUNET_GNSRECORD_data_from_identity (&(rd->key),
495 &gdraw,
496 &(gd.data_size),
497 &(gd.record_type)))
644 { 498 {
645 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 499 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
646 _ ("Error creating record data.\n")); 500 _ ("Error creating record data\n"));
647 request->phase = RP_FAIL; 501 MHD_resume_connection (rd->c);
648 MHD_resume_connection (request->con); 502 rd->searching = NULL;
503 rd->body = make_json ("error", "true",
504 "message", _ ("unable to store record"));
505 rd->body_length = strlen (rd->body);
506 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
649 run_httpd_now (); 507 run_httpd_now ();
650 return; 508 return;
651 } 509 }
652 510
653 r.data = rdata; 511 gd.data = gdraw;
654 r.expiration_time = UINT64_MAX; 512 gd.expiration_time = UINT64_MAX;
655 r.flags = GNUNET_GNSRECORD_RF_NONE; 513 gd.flags = GNUNET_GNSRECORD_RF_NONE;
656 request->qe = GNUNET_NAMESTORE_records_store (ns,
657 &fcfs_zone_pkey,
658 request->domain_name,
659 1, &r,
660 &put_continuation,
661 request);
662 GNUNET_free (rdata);
663}
664 514
515 rd->searching = GNUNET_NAMESTORE_records_store (namestore,
516 zone_key,
517 rd->register_name,
518 1,
519 &gd,
520 &register_done_cb,
521 rd);
522
523 GNUNET_free (gdraw);
524}
665 525
666/** 526/**
667 * We encountered an error in the name lookup. 527 * An error occurred while iterating the namestore.
528 *
529 * @param cls the connection
668 */ 530 */
669static void 531static void
670lookup_it_error (void *cls) 532iterate_error_cb (void *cls)
671{ 533{
672 struct Request *request = cls; 534 struct RequestData *rd = cls;
673 535
674 MHD_resume_connection (request->con); 536 MHD_resume_connection (rd->c);
675 request->qe = NULL; 537 rd->iterating = NULL;
676 request->phase = RP_FAIL; 538 rd->body = make_json ("error", "true",
539 "message", _ ("unable to scan namestore"));
540 rd->body_length = strlen (rd->body);
541 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
677 run_httpd_now (); 542 run_httpd_now ();
678} 543}
679 544
680
681/** 545/**
682 * We got a block back from the namestore. Decrypt it 546 * A block was received from the namestore.
683 * and continue to process the result.
684 * 547 *
685 * @param cls the 'struct Request' we are processing 548 * @param cls the connection
686 * @param zonekey private key of the zone; NULL on disconnect 549 * @param key the zone key
687 * @param label label of the records; NULL on disconnect 550 * @param label the records' label
688 * @param rd_count number of entries in @a rd array, 0 if label was deleted 551 * @param count number of records found
689 * @param rd array of records with data to store 552 * @param d the found records
690 */ 553 */
691static void 554static void
692lookup_it_processor (void *cls, 555iterate_do_cb (void *cls,
693 const struct GNUNET_IDENTITY_PrivateKey *zonekey, 556 const struct GNUNET_IDENTITY_PrivateKey *key,
694 const char *label, 557 const char *label,
695 unsigned int rd_count, 558 unsigned int count,
696 const struct GNUNET_GNSRECORD_Data *rd) 559 const struct GNUNET_GNSRECORD_Data *d)
697{ 560{
698 struct Request *request = cls; 561 (void) key;
699
700 (void) label; 562 (void) label;
701 (void) rd; 563 (void) d;
702 (void) zonekey; 564
703 if (0 == strcmp (label, request->domain_name)) 565 struct RequestData *rd = cls;
566
567 if (0 == strcmp (label, rd->register_name))
704 { 568 {
705 GNUNET_break (0 != rd_count); 569 GNUNET_break (0 != count);
706 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 570 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
707 _ ("Found %u existing records for domain `%s'\n"), 571 _ ("Requested name `%s' exists with `%u' records\n"),
708 rd_count, 572 rd->register_name,
709 request->domain_name); 573 count);
710 request->phase = RP_FAIL; 574
575 MHD_resume_connection (rd->c);
576 rd->body = make_json ("error", "true",
577 "message", _ ("name exists\n"));
578 rd->body_length = strlen (rd->body);
579 rd->code = MHD_HTTP_FORBIDDEN;
580 GNUNET_NAMESTORE_zone_iteration_stop (rd->iterating);
581 run_httpd_now ();
582 return;
711 } 583 }
712 GNUNET_NAMESTORE_zone_iterator_next (request->lookup_it, 1);
713}
714 584
585 GNUNET_NAMESTORE_zone_iterator_next (rd->iterating, 1);
586}
715 587
588/**
589 * All entries in the namestore have been iterated over.
590 *
591 * @param cls the connection
592 */
716static void 593static void
717lookup_it_finished (void *cls) 594iterate_done_cb (void *cls)
718{ 595{
719 struct Request *request = cls; 596 struct RequestData *rd = cls;
720 597
721 if (RP_FAIL == request->phase) 598 rd->iterating = NULL;
722 { 599
723 MHD_resume_connection (request->con); 600 /* See if the key was not registered already */
724 run_httpd_now (); 601 rd->searching = GNUNET_NAMESTORE_zone_to_name (namestore,
725 return; 602 zone_key,
726 } 603 &(rd->key),
727 if (GNUNET_OK != 604 &register_error_cb,
728 GNUNET_IDENTITY_public_key_from_string (request->public_key, 605 rd,
729 &request->pub)) 606 &register_do_cb,
730 { 607 rd);
731 GNUNET_break (0);
732 request->phase = RP_FAIL;
733 MHD_resume_connection (request->con);
734 run_httpd_now ();
735 return;
736 }
737 request->qe = GNUNET_NAMESTORE_zone_to_name (ns,
738 &fcfs_zone_pkey,
739 &request->pub,
740 &zone_to_name_error,
741 request,
742 &zone_to_name_cb,
743 request);
744} 608}
745 609
610/**
611 * Generate a response containing JSON and send it to the client.
612 *
613 * @param c the connection
614 * @param body the response body
615 * @param length the body length in bytes
616 * @param code the response code
617 * @return MHD_NO on error
618 */
619static MHD_RESULT
620serve_json (struct MHD_Connection *c,
621 char *body,
622 size_t length,
623 int code)
624{
625 struct MHD_Response *response =
626 MHD_create_response_from_buffer (length,
627 body,
628 MHD_RESPMEM_PERSISTENT);
629 MHD_RESULT r = MHD_queue_response (c, code, response);
630 MHD_destroy_response (response);
631 return r;
632}
746 633
747/** 634/**
748 * Main MHD callback for handling requests. 635 * Send a response back to a connected client.
749 * 636 *
750 * @param cls unused 637 * @param cls unused
751 * @param connection MHD connection handle 638 * @param connection the connection with the client
752 * @param url the requested url 639 * @param url the requested address
753 * @param method the HTTP method used ("GET", "PUT", etc.) 640 * @param method the HTTP method used
754 * @param version the HTTP version string ("HTTP/1.1" for version 1.1, etc.) 641 * @param version the protocol version (including the "HTTP/" part)
755 * @param upload_data the data being uploaded (excluding HEADERS, 642 * @param upload_data data sent with a POST request
756 * for a POST that fits into memory and that is encoded 643 * @param upload_data_size length in bytes of the POST data
757 * with a supported encoding, the POST data will NOT be 644 * @param ptr used to pass data between request handling phases
758 * given in upload_data and is instead available as 645 * @return MHD_NO on error
759 * part of MHD_get_connection_values; very large POST
760 * data *will* be made available incrementally in
761 * upload_data)
762 * @param upload_data_size set initially to the size of the
763 * @a upload_data provided; the method must update this
764 * value to the number of bytes NOT processed;
765 * @param ptr pointer to location where we store the 'struct Request'
766 * @return #MHD_YES if the connection was handled successfully,
767 * #MHD_NO if the socket must be closed due to a serious
768 * error while handling the request
769 */ 646 */
770static MHD_RESULT 647static MHD_RESULT
771create_response (void *cls, 648create_response (void *cls,
@@ -777,300 +654,249 @@ create_response (void *cls,
777 size_t *upload_data_size, 654 size_t *upload_data_size,
778 void **ptr) 655 void **ptr)
779{ 656{
780 struct MHD_Response *response;
781 struct Request *request;
782 struct GNUNET_IDENTITY_PublicKey pub;
783 MHD_RESULT ret;
784
785 (void) cls; 657 (void) cls;
786 (void) version; 658 (void) version;
787 if ((0 == strcmp (method, MHD_HTTP_METHOD_GET)) || 659
788 (0 == strcmp (method, MHD_HTTP_METHOD_HEAD))) 660 struct RequestData *rd = *ptr;
789 { 661
790 if (0 == strcmp (url, FCFS_ZONEINFO_URL)) 662 if (0 == strcmp (method, MHD_HTTP_METHOD_GET))
791 ret = serve_zoneinfo_page (connection);
792 else
793 ret = serve_main_page (connection);
794 if (ret != MHD_YES)
795 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
796 _ ("Failed to create page for `%s'\n"),
797 url);
798 return ret;
799 }
800 if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
801 { 663 {
802 request = *ptr; 664 /* Handle a previously suspended request */
803 if (NULL == request) 665 if (NULL != rd)
666 {
667 return serve_json (rd->c, rd->body, rd->body_length, rd->code);
668 }
669
670 if (0 == strcmp ("/", url))
804 { 671 {
805 request = GNUNET_new (struct Request); 672 return MHD_queue_response (connection,
806 request->con = connection; 673 MHD_HTTP_OK,
807 *ptr = request; 674 main_page->response);
808 request->pp = MHD_create_post_processor (connection, 675 }
809 1024, 676
810 &post_iterator, 677 if (0 == strcmp ("/search", url))
811 request); 678 {
812 if (NULL == request->pp) 679 const char *name = MHD_lookup_connection_value (connection,
680 MHD_GET_ARGUMENT_KIND,
681 "name");
682 if (NULL == name)
813 { 683 {
814 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 684 return MHD_queue_response (connection,
815 _ ("Failed to setup post processor for `%s'\n"), 685 MHD_HTTP_BAD_REQUEST,
816 url); 686 forbidden_page->response);
817 return MHD_NO; /* internal error */
818 } 687 }
688
689 MHD_suspend_connection (connection);
690 rd = GNUNET_new (struct RequestData);
691 rd->c = connection;
692 rd->searching = GNUNET_NAMESTORE_records_lookup (namestore,
693 zone_key,
694 name,
695 &search_error_cb,
696 rd,
697 &search_done_cb,
698 rd);
699 *ptr = rd;
819 return MHD_YES; 700 return MHD_YES;
820 } 701 }
821 if (NULL != request->pp) 702
703 return MHD_queue_response (connection,
704 MHD_HTTP_NOT_FOUND,
705 notfound_page->response);
706 }
707
708 if (0 == strcmp (method, MHD_HTTP_METHOD_HEAD))
709 {
710 /* We take a shortcut here by always serving the main page: starting a
711 namestore lookup, allocating the necessary resources, waiting for the
712 lookup to complete and then discard everything just because it was a HEAD
713 and thus only the headers are significative, is an unnecessary waste of
714 resources. The handling of this method could be smarter, for example by
715 sending a proper content type header based on the endpoint, but this is
716 not a service in which HEAD requests are significant, so there's no need
717 to spend too much time here. */
718 return MHD_queue_response (connection,
719 MHD_HTTP_OK,
720 main_page->response);
721 }
722
723 if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
724 {
725 if (0 == strcmp ("/register", url))
822 { 726 {
823 /* evaluate POST data */ 727 /* Handle a previously suspended request */
824 MHD_post_process (request->pp, 728 if (NULL != rd && NULL != rd->body)
825 upload_data,
826 *upload_data_size);
827 if (0 != *upload_data_size)
828 { 729 {
829 *upload_data_size = 0; 730 return serve_json (rd->c, rd->body, rd->body_length, rd->code);
830 return MHD_YES;
831 } 731 }
832 /* done with POST data, serve response */ 732
833 MHD_destroy_post_processor (request->pp); 733 if (NULL == rd)
834 request->pp = NULL;
835 }
836 if (GNUNET_OK !=
837 GNUNET_IDENTITY_public_key_from_string (request->public_key,
838 &pub))
839 {
840 /* parse error */
841 return fill_s_reply ("Failed to parse given public key",
842 request, connection);
843 }
844 switch (request->phase)
845 {
846 case RP_START:
847 if (NULL != strchr (request->domain_name, (int) '.'))
848 { 734 {
849 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 735 rd = GNUNET_new (struct RequestData);
850 _ ("Domain name must not contain `.'\n")); 736 rd->c = connection;
851 request->phase = RP_FAIL; 737 rd->body = NULL;
852 return fill_s_reply ("Domain name must not contain `.', sorry.", 738 rd->ptr = NULL;
853 request, 739 *ptr = rd;
854 connection);
855 } 740 }
856 if (NULL != strchr (request->domain_name, (int) '+')) 741
742 json_t *json = NULL;
743 enum GNUNET_JSON_PostResult result =
744 GNUNET_JSON_post_parser (32 * 1024,
745 connection,
746 &(rd->ptr),
747 upload_data,
748 upload_data_size,
749 &json);
750
751 switch (result)
857 { 752 {
858 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 753 case GNUNET_JSON_PR_CONTINUE:
859 _ ("Domain name must not contain `+'\n")); 754 /* Keep processing POST data */
860 request->phase = RP_FAIL; 755 return MHD_YES;
861 return fill_s_reply ("Domain name must not contain `+', sorry.", 756 case GNUNET_JSON_PR_OUT_OF_MEMORY:
862 request, connection); 757 case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
758 rd->body = make_json ("error", "true",
759 "message", _ ("unable to process submitted data"));
760 rd->body_length = strlen (rd->body);
761 rd->code = MHD_HTTP_PAYLOAD_TOO_LARGE;
762 return MHD_YES;
763 case GNUNET_JSON_PR_JSON_INVALID:
764 rd->body = make_json ("error", "true",
765 "message", _ ("the submitted data is invalid"));
766 rd->body_length = strlen (rd->body);
767 rd->code = MHD_HTTP_BAD_REQUEST;
768 return MHD_YES;
769 default:
770 break;
863 } 771 }
864 request->phase = RP_LOOKUP;
865 MHD_suspend_connection (request->con);
866 request->lookup_it
867 = GNUNET_NAMESTORE_zone_iteration_start (ns,
868 &fcfs_zone_pkey,
869 &lookup_it_error,
870 request,
871 &lookup_it_processor,
872 request,
873 &lookup_it_finished,
874 request);
875 break;
876
877 case RP_LOOKUP:
878 break;
879
880 case RP_PUT:
881 break;
882
883 case RP_FAIL:
884 return fill_s_reply ("Request failed, sorry.",
885 request, connection);
886
887 case RP_SUCCESS:
888 return fill_s_reply ("Success.",
889 request, connection);
890
891 default:
892 GNUNET_break (0);
893 return MHD_NO;
894 }
895 return MHD_YES; /* will have a reply later... */
896 }
897 /* unsupported HTTP method */
898 response = MHD_create_response_from_buffer (strlen (METHOD_ERROR),
899 (void *) METHOD_ERROR,
900 MHD_RESPMEM_PERSISTENT);
901 ret = MHD_queue_response (connection,
902 MHD_HTTP_NOT_ACCEPTABLE,
903 response);
904 MHD_destroy_response (response);
905 return ret;
906}
907 772
773 /* POST data has been read in its entirety */
908 774
909/** 775 const char *name = json_string_value(json_object_get(json, "name"));
910 * Callback called upon completion of a request. 776 const char *key = json_string_value(json_object_get(json, "key"));
911 * Decrements session reference counter. 777 if (NULL == name || NULL == key || 0 == strlen (name) || 0 == strlen (key))
912 * 778 {
913 * @param cls not used 779 json_decref (json);
914 * @param connection connection that completed 780 rd->body = make_json ("error", "true",
915 * @param con_cls session handle 781 "message", _ ("invalid parameters"));
916 * @param toe status code 782 rd->body_length = strlen (rd->body);
917 */ 783 rd->code = MHD_HTTP_BAD_REQUEST;
918static void 784 return MHD_YES;
919request_completed_callback (void *cls, 785 }
920 struct MHD_Connection *connection,
921 void **con_cls,
922 enum MHD_RequestTerminationCode toe)
923{
924 struct Request *request = *con_cls;
925 786
926 (void) cls; 787 rd->register_name = strdup (name);
927 (void) connection; 788 rd->register_key = strdup (key);
928 (void) toe;
929 if (NULL == request)
930 return;
931 if (NULL != request->pp)
932 MHD_destroy_post_processor (request->pp);
933 if (NULL != request->qe)
934 GNUNET_NAMESTORE_cancel (request->qe);
935 GNUNET_free (request);
936}
937 789
790 json_decref (json);
791 GNUNET_JSON_post_parser_cleanup (rd->ptr);
938 792
939#define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG 793 if (NULL != strchr (rd->register_name, '.') ||
794 NULL != strchr (rd->register_name, '+'))
795 {
796 rd->body = make_json ("error", "true",
797 "message", _ ("invalid name"));
798 rd->body_length = strlen (rd->body);
799 rd->code = MHD_HTTP_BAD_REQUEST;
800 }
940 801
802 if (GNUNET_OK != GNUNET_IDENTITY_public_key_from_string (rd->register_key,
803 &(rd->key)))
804 {
805 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
806 _ ("Unable to parse key %s\n"),
807 rd->register_key);
808
809 rd->body = make_json ("error", "true",
810 "message", _ ("unable to parse key"));
811 rd->body_length = strlen (rd->body);
812 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
813 return MHD_YES;
814 }
941 815
942/** 816 MHD_suspend_connection (connection);
943 * Schedule tasks to run MHD server. 817 /* See if the requested name is free */
944 */ 818 rd->iterating =
945static void 819 GNUNET_NAMESTORE_zone_iteration_start (namestore,
946run_httpd () 820 zone_key,
947{ 821 &iterate_error_cb,
948 fd_set rs; 822 rd,
949 fd_set ws; 823 &iterate_do_cb,
950 fd_set es; 824 rd,
951 struct GNUNET_NETWORK_FDSet *wrs; 825 &iterate_done_cb,
952 struct GNUNET_NETWORK_FDSet *wws; 826 rd);
953 struct GNUNET_NETWORK_FDSet *wes; 827 return MHD_YES;
954 int max; 828 }
955 int haveto;
956 UNSIGNED_MHD_LONG_LONG timeout;
957 struct GNUNET_TIME_Relative tv;
958 829
959 FD_ZERO (&rs); 830 return MHD_queue_response (connection,
960 FD_ZERO (&ws); 831 MHD_HTTP_FORBIDDEN,
961 FD_ZERO (&es); 832 forbidden_page->response);
962 wrs = GNUNET_NETWORK_fdset_create (); 833 }
963 wes = GNUNET_NETWORK_fdset_create ();
964 wws = GNUNET_NETWORK_fdset_create ();
965 max = -1;
966 GNUNET_assert (MHD_YES ==
967 MHD_get_fdset (httpd,
968 &rs,
969 &ws,
970 &es,
971 &max));
972 haveto = MHD_get_timeout (httpd,
973 &timeout);
974 if (haveto == MHD_YES)
975 tv.rel_value_us = (uint64_t) timeout * 1000LL;
976 else
977 tv = GNUNET_TIME_UNIT_FOREVER_REL;
978 GNUNET_NETWORK_fdset_copy_native (wrs,
979 &rs,
980 max + 1);
981 GNUNET_NETWORK_fdset_copy_native (wws,
982 &ws,
983 max + 1);
984 GNUNET_NETWORK_fdset_copy_native (wes,
985 &es,
986 max + 1);
987 httpd_task =
988 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
989 tv,
990 wrs,
991 wws,
992 &do_httpd,
993 NULL);
994 GNUNET_NETWORK_fdset_destroy (wrs);
995 GNUNET_NETWORK_fdset_destroy (wws);
996 GNUNET_NETWORK_fdset_destroy (wes);
997}
998 834
835 return MHD_queue_response (connection,
836 MHD_HTTP_NOT_IMPLEMENTED,
837 forbidden_page->response);
838}
999 839
1000/** 840/**
1001 * Task run whenever HTTP server operations are pending. 841 * Called when a request is completed.
1002 * 842 *
1003 * @param cls unused 843 * @param cls unused
844 * @param connection the connection
845 * @param ptr connection-specific data
846 * @param status status code
1004 */ 847 */
1005static void 848static void
1006do_httpd (void *cls) 849completed_cb (void *cls,
850 struct MHD_Connection *connection,
851 void **ptr,
852 enum MHD_RequestTerminationCode status)
1007{ 853{
1008 (void) cls; 854 (void) cls;
1009 httpd_task = NULL; 855 (void) connection;
1010 MHD_run (httpd); 856 (void) status;
1011 run_httpd ();
1012}
1013 857
858 struct RequestData *rd = *ptr;
1014 859
1015/** 860 if (NULL == rd)
1016 * Task run on shutdown. Cleans up everything.
1017 *
1018 * @param cls unused
1019 */
1020static void
1021do_shutdown (void *cls)
1022{
1023 (void) cls;
1024 if (NULL != httpd_task)
1025 { 861 {
1026 GNUNET_SCHEDULER_cancel (httpd_task); 862 return;
1027 httpd_task = NULL;
1028 } 863 }
1029 if (NULL != uzp_task) 864
865 if (NULL == rd->body)
1030 { 866 {
1031 GNUNET_SCHEDULER_cancel (uzp_task); 867 GNUNET_free (rd->body);
1032 uzp_task = NULL;
1033 } 868 }
1034 if (NULL != ns) 869
870 if (NULL != rd->searching)
1035 { 871 {
1036 GNUNET_NAMESTORE_disconnect (ns); 872 GNUNET_NAMESTORE_cancel (rd->searching);
1037 ns = NULL;
1038 } 873 }
1039 if (NULL != httpd) 874
875 if (NULL != rd->register_name)
1040 { 876 {
1041 MHD_stop_daemon (httpd); 877 GNUNET_free (rd->register_name);
1042 httpd = NULL;
1043 } 878 }
1044 if (NULL != id_op) 879
880 if (NULL != rd->register_key)
1045 { 881 {
1046 GNUNET_IDENTITY_cancel (id_op); 882 GNUNET_free (rd->register_key);
1047 id_op = NULL;
1048 } 883 }
1049 if (NULL != identity) 884
885 if (NULL != rd->iterating)
1050 { 886 {
1051 GNUNET_IDENTITY_disconnect (identity); 887 GNUNET_NAMESTORE_zone_iteration_stop (rd->iterating);
1052 identity = NULL;
1053 } 888 }
1054}
1055 889
890 GNUNET_free (rd);
891}
1056 892
1057/** 893/**
1058 * Method called to inform about the egos of this peer. 894 * Called for each ego provided by the identity service.
1059 *
1060 * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get, this
1061 * function is only called ONCE, and 'NULL' being passed in @a ego does
1062 * indicate an error (for example because name is taken or no default value is
1063 * known). If @a ego is non-NULL and if '*ctx' is set in those callbacks, the
1064 * value WILL be passed to a subsequent call to the identity callback of
1065 * #GNUNET_IDENTITY_connect (if that one was not NULL).
1066 * 895 *
1067 * @param cls closure, NULL 896 * @param cls closure
1068 * @param ego ego handle 897 * @param ego the ego
1069 * @param ctx context for application to store data for this ego 898 * @param ctx application-provided data for the ego
1070 * (during the lifetime of this process, initially NULL) 899 * @param name the ego name
1071 * @param name name assigned by the user for this ego,
1072 * NULL if the user just deleted the ego and it
1073 * must thus no longer be used
1074 */ 900 */
1075static void 901static void
1076identity_cb (void *cls, 902identity_cb (void *cls,
@@ -1078,15 +904,14 @@ identity_cb (void *cls,
1078 void **ctx, 904 void **ctx,
1079 const char *name) 905 const char *name)
1080{ 906{
1081 int options;
1082
1083 (void) cls; 907 (void) cls;
1084 (void) ctx; 908 (void) ctx;
1085 if (NULL == name) 909
1086 return; 910 if (NULL == name || 0 != strcmp (name, zone))
1087 if (0 != strcmp (name, 911 {
1088 zone))
1089 return; 912 return;
913 }
914
1090 if (NULL == ego) 915 if (NULL == ego)
1091 { 916 {
1092 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 917 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -1094,30 +919,24 @@ identity_cb (void *cls,
1094 GNUNET_SCHEDULER_shutdown (); 919 GNUNET_SCHEDULER_shutdown ();
1095 return; 920 return;
1096 } 921 }
1097 fcfs_zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
1098 922
1099 options = MHD_USE_DUAL_STACK | MHD_USE_DEBUG | MHD_ALLOW_SUSPEND_RESUME; 923 zone_key = GNUNET_IDENTITY_ego_get_private_key (ego);
924
925 int flags = MHD_USE_DUAL_STACK | MHD_USE_DEBUG | MHD_ALLOW_SUSPEND_RESUME;
1100 do 926 do
1101 { 927 {
1102 httpd = MHD_start_daemon (options, 928 httpd = MHD_start_daemon (flags,
1103 (uint16_t) port, 929 (uint16_t) port,
1104 NULL, NULL, 930 NULL, NULL,
1105 &create_response, NULL, 931 &create_response, NULL,
1106 MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128, 932 MHD_OPTION_CONNECTION_LIMIT, 128,
1107 MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned 933 MHD_OPTION_PER_IP_CONNECTION_LIMIT, 1,
1108 int) 1, 934 MHD_OPTION_CONNECTION_TIMEOUT, 4 * 1024,
1109 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16, 935 MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
1110 MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (4
1111 *
1112 1024),
1113 MHD_OPTION_NOTIFY_COMPLETED,
1114 &request_completed_callback, NULL,
1115 MHD_OPTION_END); 936 MHD_OPTION_END);
1116 if (MHD_USE_DEBUG == options) 937 flags = MHD_USE_DEBUG;
1117 break; 938 } while (NULL == httpd && flags != MHD_USE_DEBUG);
1118 options = MHD_USE_DEBUG; 939
1119 }
1120 while (NULL == httpd);
1121 if (NULL == httpd) 940 if (NULL == httpd)
1122 { 941 {
1123 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 942 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -1125,105 +944,158 @@ identity_cb (void *cls,
1125 GNUNET_SCHEDULER_shutdown (); 944 GNUNET_SCHEDULER_shutdown ();
1126 return; 945 return;
1127 } 946 }
947
1128 run_httpd (); 948 run_httpd ();
1129} 949}
1130 950
951/**
952 * Open a file on disk and generate a response object for it.
953 *
954 * @param name name of the file to open
955 * @param basedir directory where the file is located
956 * @return #GNUNET_SYSERR on error
957 */
958static struct StaticPage *
959open_static_page (const char *name, const char *basedir)
960{
961 char *fullname = NULL;
962 GNUNET_asprintf (&fullname, "%s/fcfsd-%s", basedir, name);
963
964 struct GNUNET_DISK_FileHandle *f =
965 GNUNET_DISK_file_open (fullname,
966 GNUNET_DISK_OPEN_READ,
967 GNUNET_DISK_PERM_NONE);
968 GNUNET_free (fullname);
969
970 if (NULL == f)
971 {
972 return NULL;
973 }
974
975 off_t size = 0;
976 if (GNUNET_SYSERR == GNUNET_DISK_file_handle_size (f, &size))
977 {
978 GNUNET_DISK_file_close (f);
979 return NULL;
980 }
981
982 struct MHD_Response *response =
983 MHD_create_response_from_fd64 (size,
984 f->fd);
985
986 if (NULL == response)
987 {
988 GNUNET_DISK_file_close (f);
989 return NULL;
990 }
991
992 struct StaticPage *page = GNUNET_new (struct StaticPage);
993 page->handle = f;
994 page->size = (uint64_t) size;
995 page->response = response;
996 return page;
997}
1131 998
1132/** 999/**
1133 * Main function that will be run. 1000 * Called after the service is up.
1134 * 1001 *
1135 * @param cls closure 1002 * @param cls closure
1136 * @param args remaining command-line arguments 1003 * @param args remaining command line arguments
1137 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 1004 * @param cfgfile name of the configuration file
1138 * @param cfg configuration 1005 * @param cfg the service configuration
1139 */ 1006 */
1140static void 1007static void
1141run (void *cls, 1008run_service (void *cls,
1142 char *const *args, 1009 char *const *args,
1143 const char *cfgfile, 1010 const char *cfgfile,
1144 const struct GNUNET_CONFIGURATION_Handle *cfg) 1011 const struct GNUNET_CONFIGURATION_Handle *cfg)
1145{ 1012{
1146 (void) cls; 1013 (void) cls;
1147 (void) args; 1014 (void) args;
1148 (void) cfgfile; 1015 (void) cfgfile;
1149 if (GNUNET_OK != 1016
1150 GNUNET_CONFIGURATION_get_value_number (cfg, 1017 GNUNET_log_setup ("fcfsd", "WARNING", NULL);
1151 "fcfsd", 1018
1152 "HTTPPORT", 1019 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
1153 &port)) 1020 "fcfsd",
1021 "HTTPPORT",
1022 &port))
1154 { 1023 {
1155 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1024 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1156 "fcfsd", "HTTPPORT"); 1025 _ ("No port specified, using default value\n"));
1157 return;
1158 } 1026 }
1159 ns = GNUNET_NAMESTORE_connect (cfg); 1027
1160 if (NULL == ns) 1028 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
1029
1030 namestore = GNUNET_NAMESTORE_connect (cfg);
1031 if (NULL == namestore)
1161 { 1032 {
1162 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1033 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1163 _ ("Failed to connect to namestore\n")); 1034 _ ("Failed to connect to namestore\n"));
1035 GNUNET_SCHEDULER_shutdown ();
1164 return; 1036 return;
1165 } 1037 }
1166 identity = GNUNET_IDENTITY_connect (cfg, 1038
1167 &identity_cb, 1039 identity = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL);
1168 NULL);
1169 if (NULL == identity) 1040 if (NULL == identity)
1170 { 1041 {
1171 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1042 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1172 _ ("Failed to connect to identity\n")); 1043 _ ("Failed to connect to identity\n"));
1044 GNUNET_SCHEDULER_shutdown ();
1173 return; 1045 return;
1174 } 1046 }
1175 uzp_task = GNUNET_SCHEDULER_add_now (&update_zoneinfo_page,
1176 NULL);
1177 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1178 NULL);
1179}
1180 1047
1048 char *basedir = NULL;
1049 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
1050 "fcfsd",
1051 "HTMLDIR",
1052 &basedir))
1053 {
1054 basedir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
1055 }
1056
1057 main_page = open_static_page ("index.html", basedir);
1058 notfound_page = open_static_page ("notfound.html", basedir);
1059 forbidden_page = open_static_page ("forbidden.html", basedir);
1060
1061 GNUNET_free (basedir);
1062
1063 if (NULL == main_page || NULL == notfound_page || NULL == forbidden_page)
1064 {
1065 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1066 _ ("Unable to set up the daemon\n"));
1067 GNUNET_SCHEDULER_shutdown ();
1068 return;
1069 }
1070}
1181 1071
1182/** 1072/**
1183 * The main function for the fcfs daemon. 1073 * The main function of the fcfs daemon.
1184 * 1074 *
1185 * @param argc number of arguments from the command line 1075 * @param argc number of arguments from the command line
1186 * @param argv command line arguments 1076 * @parsm argv the command line argumens
1187 * @return 0 ok, 1 on error 1077 * @return 0 successful exit, a different value otherwise
1188 */ 1078 */
1189int 1079int
1190main (int argc, 1080main (int argc, char *const *argv)
1191 char *const *argv)
1192{ 1081{
1193 struct GNUNET_GETOPT_CommandLineOption options[] = { 1082 struct GNUNET_GETOPT_CommandLineOption options[] = {
1194 GNUNET_GETOPT_option_mandatory 1083 GNUNET_GETOPT_option_mandatory
1195 (GNUNET_GETOPT_option_string ('z', 1084 (GNUNET_GETOPT_option_string ('z',
1196 "zone", 1085 "zone",
1197 "EGO", 1086 "EGO",
1198 gettext_noop ( 1087 gettext_noop ("name of the zone managed by FCFSD"),
1199 "name of the zone that is to be managed by FCFSD"), 1088 &zone)),
1200 &zone)),
1201 GNUNET_GETOPT_OPTION_END 1089 GNUNET_GETOPT_OPTION_END
1202 }; 1090 };
1203 int ret;
1204
1205 if (GNUNET_OK !=
1206 GNUNET_STRINGS_get_utf8_args (argc, argv,
1207 &argc, &argv))
1208 return 2;
1209
1210 GNUNET_log_setup ("fcfsd",
1211 "WARNING",
1212 NULL);
1213 ret =
1214 (GNUNET_OK ==
1215 GNUNET_PROGRAM_run (argc,
1216 argv,
1217 "gnunet-namestore-fcfsd",
1218 _ (
1219 "GNU Name System First Come First Serve name registration service"),
1220 options,
1221 &run, NULL)) ? 0 : 1;
1222 GNUNET_free_nz ((void *) argv);
1223 // FIXME
1224 // GNUNET_CRYPTO_ecdsa_key_clear (&fcfs_zone_pkey);
1225 return ret;
1226}
1227 1091
1228 1092 return ((GNUNET_OK == GNUNET_PROGRAM_run (argc,
1229/* end of gnunet-namestore-fcfsd.c */ 1093 argv,
1094 "gnunet-namestore-fcfsd",
1095 _ ("GNU Name System First-Come-First-Served name registration service"),
1096 options,
1097 &run_service,
1098 NULL)) ?
1099 0 :
1100 1);
1101}