aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlessio Vanni <vannilla@firemail.cc>2021-05-19 18:59:55 +0200
committerAlessio Vanni <vannilla@firemail.cc>2021-05-19 18:59:55 +0200
commit0c7e226462d4e96d88189faaf3979c2dacdcfe3f (patch)
treed01954959d577381c7a8ed5b6a89c5fdf42ae197 /src
parent00c21152e81c10dff640ec932127e74ea8bc25ac (diff)
parentc68abd271cce537ced900193e59a7d5373c1bf83 (diff)
downloadgnunet-0c7e226462d4e96d88189faaf3979c2dacdcfe3f.tar.gz
gnunet-0c7e226462d4e96d88189faaf3979c2dacdcfe3f.zip
-Merge branch 'dev/vanni/fcfsd'
Diffstat (limited to 'src')
-rw-r--r--src/namestore/Makefile.am3
-rw-r--r--src/namestore/gnunet-namestore-fcfsd.c1607
2 files changed, 748 insertions, 862 deletions
diff --git a/src/namestore/Makefile.am b/src/namestore/Makefile.am
index df4e5d662..61bcfbaf8 100644
--- a/src/namestore/Makefile.am
+++ b/src/namestore/Makefile.am
@@ -182,7 +182,8 @@ gnunet_namestore_fcfsd_LDADD = $(MHD_LIBS) \
182 $(top_builddir)/src/identity/libgnunetidentity.la \ 182 $(top_builddir)/src/identity/libgnunetidentity.la \
183 libgnunetnamestore.la \ 183 libgnunetnamestore.la \
184 $(top_builddir)/src/util/libgnunetutil.la \ 184 $(top_builddir)/src/util/libgnunetutil.la \
185 $(GN_LIBINTL) 185 $(top_builddir)/src/json/libgnunetjson.la \
186 $(GN_LIBINTL) -ljansson
186gnunet_namestore_fcfsd_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS) 187gnunet_namestore_fcfsd_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS)
187 188
188 189
diff --git a/src/namestore/gnunet-namestore-fcfsd.c b/src/namestore/gnunet-namestore-fcfsd.c
index 313aea6fc..e1fa0a0a6 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,625 @@
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 NULL);
532 { 408 rd->body_length = strlen (rd->body);
533 if (size + off >= sizeof(request->domain_name)) 409 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
534 size = sizeof(request->domain_name) - off - 1; 410 run_httpd_now ();
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} 411}
556 412
557
558/** 413/**
559 * Continuation called to notify client about result of the 414 * A name/key pair has been successfully registered, or maybe not.
560 * operation.
561 * 415 *
562 * @param cls closure 416 * @param cls the connection
563 * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate) 417 * @param status result of the operation
564 * #GNUNET_NO if content was already there 418 * @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 */ 419 */
568static void 420static void
569put_continuation (void *cls, 421register_done_cb (void *cls,
570 int32_t success, 422 int32_t status,
571 const char *emsg) 423 const char *emsg)
572{ 424{
573 struct Request *request = cls; 425 struct RequestData *rd = cls;
426
427 MHD_resume_connection (rd->c);
428 rd->searching = NULL;
574 429
575 request->qe = NULL; 430 if (GNUNET_SYSERR == status || GNUNET_NO == status)
576 if (0 >= success)
577 { 431 {
578 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 432 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
579 _ ("Failed to create record for domain `%s': %s\n"), 433 _ ("Failed to create record for `%s': %s\n"),
580 request->domain_name, 434 rd->register_name,
581 emsg); 435 emsg);
582 request->phase = RP_FAIL; 436 rd->body = make_json ("error", "true",
437 "message", emsg,
438 NULL);
439 rd->body_length = strlen (rd->body);
440 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
583 } 441 }
584 else 442 else
585 request->phase = RP_SUCCESS; 443 {
586 MHD_resume_connection (request->con); 444 rd->body = make_json ("error", "false",
445 "message", _ ("no errors"),
446 NULL);
447 rd->body_length = strlen (rd->body);
448 rd->code = MHD_HTTP_OK;
449 }
450
587 run_httpd_now (); 451 run_httpd_now ();
588} 452}
589 453
590
591/** 454/**
592 * Function called if we had an error in zone-to-name mapping. 455 * Attempt to register the requested name.
456 *
457 * @param cls the connection
458 * @param key the zone key
459 * @param label name of the record
460 * @param count number of records found
461 * @param d records
593 */ 462 */
594static void 463static void
595zone_to_name_error (void *cls) 464register_do_cb (void *cls,
465 const struct GNUNET_IDENTITY_PrivateKey *key,
466 const char *label,
467 unsigned int count,
468 const struct GNUNET_GNSRECORD_Data *d)
596{ 469{
597 struct Request *request = cls; 470 (void) key;
471 (void) d;
598 472
599 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 473 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 474
475 rd->searching = NULL;
606 476
607/** 477 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 { 478 {
632 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 479 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
633 _ ("Found existing name `%s' for the given key\n"), 480 _ ("The requested key `%s' exists as `%s'\n"),
634 name); 481 rd->register_key,
635 request->phase = RP_FAIL; 482 label);
636 MHD_resume_connection (request->con); 483
484 MHD_resume_connection (rd->c);
485 rd->searching = NULL;
486 rd->body = make_json ("error", "true",
487 "message", _ ("key exists"),
488 NULL);
489 rd->body_length = strlen (rd->body);
490 rd->code = MHD_HTTP_FORBIDDEN;
637 run_httpd_now (); 491 run_httpd_now ();
638 return; 492 return;
639 } 493 }
640 if (GNUNET_OK != GNUNET_GNSRECORD_data_from_identity (&request->pub, 494
641 &rdata, 495 struct GNUNET_GNSRECORD_Data gd;
642 &r.data_size, 496 char *gdraw = NULL;
643 &r.record_type)) 497
498 if (GNUNET_OK != GNUNET_GNSRECORD_data_from_identity (&(rd->key),
499 &gdraw,
500 &(gd.data_size),
501 &(gd.record_type)))
644 { 502 {
645 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 503 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
646 _ ("Error creating record data.\n")); 504 _ ("Error creating record data\n"));
647 request->phase = RP_FAIL; 505 MHD_resume_connection (rd->c);
648 MHD_resume_connection (request->con); 506 rd->searching = NULL;
507 rd->body = make_json ("error", "true",
508 "message", _ ("unable to store record"),
509 NULL);
510 rd->body_length = strlen (rd->body);
511 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
649 run_httpd_now (); 512 run_httpd_now ();
650 return; 513 return;
651 } 514 }
652 515
653 r.data = rdata; 516 gd.data = gdraw;
654 r.expiration_time = UINT64_MAX; 517 gd.expiration_time = UINT64_MAX;
655 r.flags = GNUNET_GNSRECORD_RF_NONE; 518 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 519
520 rd->searching = GNUNET_NAMESTORE_records_store (namestore,
521 zone_key,
522 rd->register_name,
523 1,
524 &gd,
525 &register_done_cb,
526 rd);
527
528 GNUNET_free (gdraw);
529}
665 530
666/** 531/**
667 * We encountered an error in the name lookup. 532 * An error occurred while iterating the namestore.
533 *
534 * @param cls the connection
668 */ 535 */
669static void 536static void
670lookup_it_error (void *cls) 537iterate_error_cb (void *cls)
671{ 538{
672 struct Request *request = cls; 539 struct RequestData *rd = cls;
673 540
674 MHD_resume_connection (request->con); 541 MHD_resume_connection (rd->c);
675 request->qe = NULL; 542 rd->iterating = NULL;
676 request->phase = RP_FAIL; 543 rd->body = make_json ("error", "true",
544 "message", _ ("unable to scan namestore"),
545 NULL);
546 rd->body_length = strlen (rd->body);
547 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
677 run_httpd_now (); 548 run_httpd_now ();
678} 549}
679 550
680
681/** 551/**
682 * We got a block back from the namestore. Decrypt it 552 * A block was received from the namestore.
683 * and continue to process the result.
684 * 553 *
685 * @param cls the 'struct Request' we are processing 554 * @param cls the connection
686 * @param zonekey private key of the zone; NULL on disconnect 555 * @param key the zone key
687 * @param label label of the records; NULL on disconnect 556 * @param label the records' label
688 * @param rd_count number of entries in @a rd array, 0 if label was deleted 557 * @param count number of records found
689 * @param rd array of records with data to store 558 * @param d the found records
690 */ 559 */
691static void 560static void
692lookup_it_processor (void *cls, 561iterate_do_cb (void *cls,
693 const struct GNUNET_IDENTITY_PrivateKey *zonekey, 562 const struct GNUNET_IDENTITY_PrivateKey *key,
694 const char *label, 563 const char *label,
695 unsigned int rd_count, 564 unsigned int count,
696 const struct GNUNET_GNSRECORD_Data *rd) 565 const struct GNUNET_GNSRECORD_Data *d)
697{ 566{
698 struct Request *request = cls; 567 (void) key;
699
700 (void) label; 568 (void) label;
701 (void) rd; 569 (void) d;
702 (void) zonekey; 570
703 if (0 == strcmp (label, request->domain_name)) 571 struct RequestData *rd = cls;
572
573 if (0 == strcmp (label, rd->register_name))
704 { 574 {
705 GNUNET_break (0 != rd_count); 575 GNUNET_break (0 != count);
706 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 576 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
707 _ ("Found %u existing records for domain `%s'\n"), 577 _ ("Requested name `%s' exists with `%u' records\n"),
708 rd_count, 578 rd->register_name,
709 request->domain_name); 579 count);
710 request->phase = RP_FAIL; 580
581 MHD_resume_connection (rd->c);
582 rd->body = make_json ("error", "true",
583 "message", _ ("name exists\n"),
584 NULL);
585 rd->body_length = strlen (rd->body);
586 rd->code = MHD_HTTP_FORBIDDEN;
587 GNUNET_NAMESTORE_zone_iteration_stop (rd->iterating);
588 run_httpd_now ();
589 return;
711 } 590 }
712 GNUNET_NAMESTORE_zone_iterator_next (request->lookup_it, 1);
713}
714 591
592 GNUNET_NAMESTORE_zone_iterator_next (rd->iterating, 1);
593}
715 594
595/**
596 * All entries in the namestore have been iterated over.
597 *
598 * @param cls the connection
599 */
716static void 600static void
717lookup_it_finished (void *cls) 601iterate_done_cb (void *cls)
718{ 602{
719 struct Request *request = cls; 603 struct RequestData *rd = cls;
720 604
721 if (RP_FAIL == request->phase) 605 rd->iterating = NULL;
722 { 606
723 MHD_resume_connection (request->con); 607 /* See if the key was not registered already */
724 run_httpd_now (); 608 rd->searching = GNUNET_NAMESTORE_zone_to_name (namestore,
725 return; 609 zone_key,
726 } 610 &(rd->key),
727 if (GNUNET_OK != 611 &register_error_cb,
728 GNUNET_IDENTITY_public_key_from_string (request->public_key, 612 rd,
729 &request->pub)) 613 &register_do_cb,
730 { 614 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} 615}
745 616
617/**
618 * Generate a response containing JSON and send it to the client.
619 *
620 * @param c the connection
621 * @param body the response body
622 * @param length the body length in bytes
623 * @param code the response code
624 * @return MHD_NO on error
625 */
626static MHD_RESULT
627serve_json (struct MHD_Connection *c,
628 char *body,
629 size_t length,
630 int code)
631{
632 struct MHD_Response *response =
633 MHD_create_response_from_buffer (length,
634 body,
635 MHD_RESPMEM_PERSISTENT);
636 MHD_RESULT r = MHD_queue_response (c, code, response);
637 MHD_destroy_response (response);
638 return r;
639}
746 640
747/** 641/**
748 * Main MHD callback for handling requests. 642 * Send a response back to a connected client.
749 * 643 *
750 * @param cls unused 644 * @param cls unused
751 * @param connection MHD connection handle 645 * @param connection the connection with the client
752 * @param url the requested url 646 * @param url the requested address
753 * @param method the HTTP method used ("GET", "PUT", etc.) 647 * @param method the HTTP method used
754 * @param version the HTTP version string ("HTTP/1.1" for version 1.1, etc.) 648 * @param version the protocol version (including the "HTTP/" part)
755 * @param upload_data the data being uploaded (excluding HEADERS, 649 * @param upload_data data sent with a POST request
756 * for a POST that fits into memory and that is encoded 650 * @param upload_data_size length in bytes of the POST data
757 * with a supported encoding, the POST data will NOT be 651 * @param ptr used to pass data between request handling phases
758 * given in upload_data and is instead available as 652 * @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 */ 653 */
770static MHD_RESULT 654static MHD_RESULT
771create_response (void *cls, 655create_response (void *cls,
@@ -777,300 +661,255 @@ create_response (void *cls,
777 size_t *upload_data_size, 661 size_t *upload_data_size,
778 void **ptr) 662 void **ptr)
779{ 663{
780 struct MHD_Response *response;
781 struct Request *request;
782 struct GNUNET_IDENTITY_PublicKey pub;
783 MHD_RESULT ret;
784
785 (void) cls; 664 (void) cls;
786 (void) version; 665 (void) version;
787 if ((0 == strcmp (method, MHD_HTTP_METHOD_GET)) || 666
788 (0 == strcmp (method, MHD_HTTP_METHOD_HEAD))) 667 struct RequestData *rd = *ptr;
789 { 668
790 if (0 == strcmp (url, FCFS_ZONEINFO_URL)) 669 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 { 670 {
802 request = *ptr; 671 /* Handle a previously suspended request */
803 if (NULL == request) 672 if (NULL != rd)
673 {
674 return serve_json (rd->c, rd->body, rd->body_length, rd->code);
675 }
676
677 if (0 == strcmp ("/", url))
804 { 678 {
805 request = GNUNET_new (struct Request); 679 return MHD_queue_response (connection,
806 request->con = connection; 680 MHD_HTTP_OK,
807 *ptr = request; 681 main_page->response);
808 request->pp = MHD_create_post_processor (connection, 682 }
809 1024, 683
810 &post_iterator, 684 if (0 == strcmp ("/search", url))
811 request); 685 {
812 if (NULL == request->pp) 686 const char *name = MHD_lookup_connection_value (connection,
687 MHD_GET_ARGUMENT_KIND,
688 "name");
689 if (NULL == name)
813 { 690 {
814 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 691 return MHD_queue_response (connection,
815 _ ("Failed to setup post processor for `%s'\n"), 692 MHD_HTTP_BAD_REQUEST,
816 url); 693 forbidden_page->response);
817 return MHD_NO; /* internal error */
818 } 694 }
695
696 MHD_suspend_connection (connection);
697 rd = GNUNET_new (struct RequestData);
698 rd->c = connection;
699 rd->searching = GNUNET_NAMESTORE_records_lookup (namestore,
700 zone_key,
701 name,
702 &search_error_cb,
703 rd,
704 &search_done_cb,
705 rd);
706 *ptr = rd;
819 return MHD_YES; 707 return MHD_YES;
820 } 708 }
821 if (NULL != request->pp) 709
710 return MHD_queue_response (connection,
711 MHD_HTTP_NOT_FOUND,
712 notfound_page->response);
713 }
714
715 if (0 == strcmp (method, MHD_HTTP_METHOD_HEAD))
716 {
717 /* We take a shortcut here by always serving the main page: starting a
718 namestore lookup, allocating the necessary resources, waiting for the
719 lookup to complete and then discard everything just because it was a HEAD
720 and thus only the headers are significative, is an unnecessary waste of
721 resources. The handling of this method could be smarter, for example by
722 sending a proper content type header based on the endpoint, but this is
723 not a service in which HEAD requests are significant, so there's no need
724 to spend too much time here. */
725 return MHD_queue_response (connection,
726 MHD_HTTP_OK,
727 main_page->response);
728 }
729
730 if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
731 {
732 if (0 == strcmp ("/register", url))
822 { 733 {
823 /* evaluate POST data */ 734 /* Handle a previously suspended request */
824 MHD_post_process (request->pp, 735 if (NULL != rd && NULL != rd->body)
825 upload_data,
826 *upload_data_size);
827 if (0 != *upload_data_size)
828 { 736 {
829 *upload_data_size = 0; 737 return serve_json (rd->c, rd->body, rd->body_length, rd->code);
830 return MHD_YES;
831 } 738 }
832 /* done with POST data, serve response */ 739
833 MHD_destroy_post_processor (request->pp); 740 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 { 741 {
849 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 742 rd = GNUNET_new (struct RequestData);
850 _ ("Domain name must not contain `.'\n")); 743 rd->c = connection;
851 request->phase = RP_FAIL; 744 rd->body = NULL;
852 return fill_s_reply ("Domain name must not contain `.', sorry.", 745 rd->ptr = NULL;
853 request, 746 *ptr = rd;
854 connection);
855 } 747 }
856 if (NULL != strchr (request->domain_name, (int) '+')) 748
749 json_t *json = NULL;
750 enum GNUNET_JSON_PostResult result =
751 GNUNET_JSON_post_parser (32 * 1024,
752 connection,
753 &(rd->ptr),
754 upload_data,
755 upload_data_size,
756 &json);
757
758 switch (result)
857 { 759 {
858 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 760 case GNUNET_JSON_PR_CONTINUE:
859 _ ("Domain name must not contain `+'\n")); 761 /* Keep processing POST data */
860 request->phase = RP_FAIL; 762 return MHD_YES;
861 return fill_s_reply ("Domain name must not contain `+', sorry.", 763 case GNUNET_JSON_PR_OUT_OF_MEMORY:
862 request, connection); 764 case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
765 rd->body = make_json ("error", "true",
766 "message", _ ("unable to process submitted data"),
767 NULL);
768 rd->body_length = strlen (rd->body);
769 rd->code = MHD_HTTP_PAYLOAD_TOO_LARGE;
770 return MHD_YES;
771 case GNUNET_JSON_PR_JSON_INVALID:
772 rd->body = make_json ("error", "true",
773 "message", _ ("the submitted data is invalid"),
774 NULL);
775 rd->body_length = strlen (rd->body);
776 rd->code = MHD_HTTP_BAD_REQUEST;
777 return MHD_YES;
778 default:
779 break;
863 } 780 }
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 781
782 /* POST data has been read in its entirety */
908 783
909/** 784 const char *name = json_string_value(json_object_get(json, "name"));
910 * Callback called upon completion of a request. 785 const char *key = json_string_value(json_object_get(json, "key"));
911 * Decrements session reference counter. 786 if (NULL == name || NULL == key || 0 == strlen (name) || 0 == strlen (key))
912 * 787 {
913 * @param cls not used 788 json_decref (json);
914 * @param connection connection that completed 789 rd->body = make_json ("error", "true",
915 * @param con_cls session handle 790 "message", _ ("invalid parameters"),
916 * @param toe status code 791 NULL);
917 */ 792 rd->body_length = strlen (rd->body);
918static void 793 rd->code = MHD_HTTP_BAD_REQUEST;
919request_completed_callback (void *cls, 794 return MHD_YES;
920 struct MHD_Connection *connection, 795 }
921 void **con_cls,
922 enum MHD_RequestTerminationCode toe)
923{
924 struct Request *request = *con_cls;
925 796
926 (void) cls; 797 rd->register_name = strdup (name);
927 (void) connection; 798 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 799
800 json_decref (json);
801 GNUNET_JSON_post_parser_cleanup (rd->ptr);
938 802
939#define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG 803 if (NULL != strchr (rd->register_name, '.') ||
804 NULL != strchr (rd->register_name, '+'))
805 {
806 rd->body = make_json ("error", "true",
807 "message", _ ("invalid name"),
808 NULL);
809 rd->body_length = strlen (rd->body);
810 rd->code = MHD_HTTP_BAD_REQUEST;
811 return MHD_YES;
812 }
940 813
814 if (GNUNET_OK != GNUNET_IDENTITY_public_key_from_string (rd->register_key,
815 &(rd->key)))
816 {
817 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
818 _ ("Unable to parse key %s\n"),
819 rd->register_key);
820
821 rd->body = make_json ("error", "true",
822 "message", _ ("unable to parse key"),
823 NULL);
824 rd->body_length = strlen (rd->body);
825 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
826 return MHD_YES;
827 }
941 828
942/** 829 MHD_suspend_connection (connection);
943 * Schedule tasks to run MHD server. 830 /* See if the requested name is free */
944 */ 831 rd->iterating =
945static void 832 GNUNET_NAMESTORE_zone_iteration_start (namestore,
946run_httpd () 833 zone_key,
947{ 834 &iterate_error_cb,
948 fd_set rs; 835 rd,
949 fd_set ws; 836 &iterate_do_cb,
950 fd_set es; 837 rd,
951 struct GNUNET_NETWORK_FDSet *wrs; 838 &iterate_done_cb,
952 struct GNUNET_NETWORK_FDSet *wws; 839 rd);
953 struct GNUNET_NETWORK_FDSet *wes; 840 return MHD_YES;
954 int max; 841 }
955 int haveto;
956 UNSIGNED_MHD_LONG_LONG timeout;
957 struct GNUNET_TIME_Relative tv;
958 842
959 FD_ZERO (&rs); 843 return MHD_queue_response (connection,
960 FD_ZERO (&ws); 844 MHD_HTTP_FORBIDDEN,
961 FD_ZERO (&es); 845 forbidden_page->response);
962 wrs = GNUNET_NETWORK_fdset_create (); 846 }
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 847
848 return MHD_queue_response (connection,
849 MHD_HTTP_NOT_IMPLEMENTED,
850 forbidden_page->response);
851}
999 852
1000/** 853/**
1001 * Task run whenever HTTP server operations are pending. 854 * Called when a request is completed.
1002 * 855 *
1003 * @param cls unused 856 * @param cls unused
857 * @param connection the connection
858 * @param ptr connection-specific data
859 * @param status status code
1004 */ 860 */
1005static void 861static void
1006do_httpd (void *cls) 862completed_cb (void *cls,
863 struct MHD_Connection *connection,
864 void **ptr,
865 enum MHD_RequestTerminationCode status)
1007{ 866{
1008 (void) cls; 867 (void) cls;
1009 httpd_task = NULL; 868 (void) connection;
1010 MHD_run (httpd); 869 (void) status;
1011 run_httpd ();
1012}
1013 870
871 struct RequestData *rd = *ptr;
1014 872
1015/** 873 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 { 874 {
1026 GNUNET_SCHEDULER_cancel (httpd_task); 875 return;
1027 httpd_task = NULL;
1028 } 876 }
1029 if (NULL != uzp_task) 877
878 if (NULL == rd->body)
1030 { 879 {
1031 GNUNET_SCHEDULER_cancel (uzp_task); 880 GNUNET_free (rd->body);
1032 uzp_task = NULL;
1033 } 881 }
1034 if (NULL != ns) 882
883 if (NULL != rd->searching)
1035 { 884 {
1036 GNUNET_NAMESTORE_disconnect (ns); 885 GNUNET_NAMESTORE_cancel (rd->searching);
1037 ns = NULL;
1038 } 886 }
1039 if (NULL != httpd) 887
888 if (NULL != rd->register_name)
1040 { 889 {
1041 MHD_stop_daemon (httpd); 890 GNUNET_free (rd->register_name);
1042 httpd = NULL;
1043 } 891 }
1044 if (NULL != id_op) 892
893 if (NULL != rd->register_key)
1045 { 894 {
1046 GNUNET_IDENTITY_cancel (id_op); 895 GNUNET_free (rd->register_key);
1047 id_op = NULL;
1048 } 896 }
1049 if (NULL != identity) 897
898 if (NULL != rd->iterating)
1050 { 899 {
1051 GNUNET_IDENTITY_disconnect (identity); 900 GNUNET_NAMESTORE_zone_iteration_stop (rd->iterating);
1052 identity = NULL;
1053 } 901 }
1054}
1055 902
903 GNUNET_free (rd);
904}
1056 905
1057/** 906/**
1058 * Method called to inform about the egos of this peer. 907 * 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 * 908 *
1067 * @param cls closure, NULL 909 * @param cls closure
1068 * @param ego ego handle 910 * @param ego the ego
1069 * @param ctx context for application to store data for this ego 911 * @param ctx application-provided data for the ego
1070 * (during the lifetime of this process, initially NULL) 912 * @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 */ 913 */
1075static void 914static void
1076identity_cb (void *cls, 915identity_cb (void *cls,
@@ -1078,15 +917,14 @@ identity_cb (void *cls,
1078 void **ctx, 917 void **ctx,
1079 const char *name) 918 const char *name)
1080{ 919{
1081 int options;
1082
1083 (void) cls; 920 (void) cls;
1084 (void) ctx; 921 (void) ctx;
1085 if (NULL == name) 922
1086 return; 923 if (NULL == name || 0 != strcmp (name, zone))
1087 if (0 != strcmp (name, 924 {
1088 zone))
1089 return; 925 return;
926 }
927
1090 if (NULL == ego) 928 if (NULL == ego)
1091 { 929 {
1092 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 930 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -1094,30 +932,24 @@ identity_cb (void *cls,
1094 GNUNET_SCHEDULER_shutdown (); 932 GNUNET_SCHEDULER_shutdown ();
1095 return; 933 return;
1096 } 934 }
1097 fcfs_zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
1098 935
1099 options = MHD_USE_DUAL_STACK | MHD_USE_DEBUG | MHD_ALLOW_SUSPEND_RESUME; 936 zone_key = GNUNET_IDENTITY_ego_get_private_key (ego);
937
938 int flags = MHD_USE_DUAL_STACK | MHD_USE_DEBUG | MHD_ALLOW_SUSPEND_RESUME;
1100 do 939 do
1101 { 940 {
1102 httpd = MHD_start_daemon (options, 941 httpd = MHD_start_daemon (flags,
1103 (uint16_t) port, 942 (uint16_t) port,
1104 NULL, NULL, 943 NULL, NULL,
1105 &create_response, NULL, 944 &create_response, NULL,
1106 MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128, 945 MHD_OPTION_CONNECTION_LIMIT, 128,
1107 MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned 946 MHD_OPTION_PER_IP_CONNECTION_LIMIT, 1,
1108 int) 1, 947 MHD_OPTION_CONNECTION_TIMEOUT, 4 * 1024,
1109 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16, 948 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); 949 MHD_OPTION_END);
1116 if (MHD_USE_DEBUG == options) 950 flags = MHD_USE_DEBUG;
1117 break; 951 } while (NULL == httpd && flags != MHD_USE_DEBUG);
1118 options = MHD_USE_DEBUG; 952
1119 }
1120 while (NULL == httpd);
1121 if (NULL == httpd) 953 if (NULL == httpd)
1122 { 954 {
1123 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 955 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -1125,105 +957,158 @@ identity_cb (void *cls,
1125 GNUNET_SCHEDULER_shutdown (); 957 GNUNET_SCHEDULER_shutdown ();
1126 return; 958 return;
1127 } 959 }
960
1128 run_httpd (); 961 run_httpd ();
1129} 962}
1130 963
964/**
965 * Open a file on disk and generate a response object for it.
966 *
967 * @param name name of the file to open
968 * @param basedir directory where the file is located
969 * @return #GNUNET_SYSERR on error
970 */
971static struct StaticPage *
972open_static_page (const char *name, const char *basedir)
973{
974 char *fullname = NULL;
975 GNUNET_asprintf (&fullname, "%s/fcfsd-%s", basedir, name);
976
977 struct GNUNET_DISK_FileHandle *f =
978 GNUNET_DISK_file_open (fullname,
979 GNUNET_DISK_OPEN_READ,
980 GNUNET_DISK_PERM_NONE);
981 GNUNET_free (fullname);
982
983 if (NULL == f)
984 {
985 return NULL;
986 }
987
988 off_t size = 0;
989 if (GNUNET_SYSERR == GNUNET_DISK_file_handle_size (f, &size))
990 {
991 GNUNET_DISK_file_close (f);
992 return NULL;
993 }
994
995 struct MHD_Response *response =
996 MHD_create_response_from_fd64 (size,
997 f->fd);
998
999 if (NULL == response)
1000 {
1001 GNUNET_DISK_file_close (f);
1002 return NULL;
1003 }
1004
1005 struct StaticPage *page = GNUNET_new (struct StaticPage);
1006 page->handle = f;
1007 page->size = (uint64_t) size;
1008 page->response = response;
1009 return page;
1010}
1131 1011
1132/** 1012/**
1133 * Main function that will be run. 1013 * Called after the service is up.
1134 * 1014 *
1135 * @param cls closure 1015 * @param cls closure
1136 * @param args remaining command-line arguments 1016 * @param args remaining command line arguments
1137 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 1017 * @param cfgfile name of the configuration file
1138 * @param cfg configuration 1018 * @param cfg the service configuration
1139 */ 1019 */
1140static void 1020static void
1141run (void *cls, 1021run_service (void *cls,
1142 char *const *args, 1022 char *const *args,
1143 const char *cfgfile, 1023 const char *cfgfile,
1144 const struct GNUNET_CONFIGURATION_Handle *cfg) 1024 const struct GNUNET_CONFIGURATION_Handle *cfg)
1145{ 1025{
1146 (void) cls; 1026 (void) cls;
1147 (void) args; 1027 (void) args;
1148 (void) cfgfile; 1028 (void) cfgfile;
1149 if (GNUNET_OK != 1029
1150 GNUNET_CONFIGURATION_get_value_number (cfg, 1030 GNUNET_log_setup ("fcfsd", "WARNING", NULL);
1151 "fcfsd", 1031
1152 "HTTPPORT", 1032 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
1153 &port)) 1033 "fcfsd",
1034 "HTTPPORT",
1035 &port))
1154 { 1036 {
1155 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1037 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1156 "fcfsd", "HTTPPORT"); 1038 _ ("No port specified, using default value\n"));
1157 return;
1158 } 1039 }
1159 ns = GNUNET_NAMESTORE_connect (cfg); 1040
1160 if (NULL == ns) 1041 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
1042
1043 namestore = GNUNET_NAMESTORE_connect (cfg);
1044 if (NULL == namestore)
1161 { 1045 {
1162 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1046 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1163 _ ("Failed to connect to namestore\n")); 1047 _ ("Failed to connect to namestore\n"));
1048 GNUNET_SCHEDULER_shutdown ();
1164 return; 1049 return;
1165 } 1050 }
1166 identity = GNUNET_IDENTITY_connect (cfg, 1051
1167 &identity_cb, 1052 identity = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL);
1168 NULL);
1169 if (NULL == identity) 1053 if (NULL == identity)
1170 { 1054 {
1171 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1055 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1172 _ ("Failed to connect to identity\n")); 1056 _ ("Failed to connect to identity\n"));
1057 GNUNET_SCHEDULER_shutdown ();
1173 return; 1058 return;
1174 } 1059 }
1175 uzp_task = GNUNET_SCHEDULER_add_now (&update_zoneinfo_page,
1176 NULL);
1177 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1178 NULL);
1179}
1180 1060
1061 char *basedir = NULL;
1062 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
1063 "fcfsd",
1064 "HTMLDIR",
1065 &basedir))
1066 {
1067 basedir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
1068 }
1069
1070 main_page = open_static_page ("index.html", basedir);
1071 notfound_page = open_static_page ("notfound.html", basedir);
1072 forbidden_page = open_static_page ("forbidden.html", basedir);
1073
1074 GNUNET_free (basedir);
1075
1076 if (NULL == main_page || NULL == notfound_page || NULL == forbidden_page)
1077 {
1078 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1079 _ ("Unable to set up the daemon\n"));
1080 GNUNET_SCHEDULER_shutdown ();
1081 return;
1082 }
1083}
1181 1084
1182/** 1085/**
1183 * The main function for the fcfs daemon. 1086 * The main function of the fcfs daemon.
1184 * 1087 *
1185 * @param argc number of arguments from the command line 1088 * @param argc number of arguments from the command line
1186 * @param argv command line arguments 1089 * @parsm argv the command line argumens
1187 * @return 0 ok, 1 on error 1090 * @return 0 successful exit, a different value otherwise
1188 */ 1091 */
1189int 1092int
1190main (int argc, 1093main (int argc, char *const *argv)
1191 char *const *argv)
1192{ 1094{
1193 struct GNUNET_GETOPT_CommandLineOption options[] = { 1095 struct GNUNET_GETOPT_CommandLineOption options[] = {
1194 GNUNET_GETOPT_option_mandatory 1096 GNUNET_GETOPT_option_mandatory
1195 (GNUNET_GETOPT_option_string ('z', 1097 (GNUNET_GETOPT_option_string ('z',
1196 "zone", 1098 "zone",
1197 "EGO", 1099 "EGO",
1198 gettext_noop ( 1100 gettext_noop ("name of the zone managed by FCFSD"),
1199 "name of the zone that is to be managed by FCFSD"), 1101 &zone)),
1200 &zone)),
1201 GNUNET_GETOPT_OPTION_END 1102 GNUNET_GETOPT_OPTION_END
1202 }; 1103 };
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 1104
1228 1105 return ((GNUNET_OK == GNUNET_PROGRAM_run (argc,
1229/* end of gnunet-namestore-fcfsd.c */ 1106 argv,
1107 "gnunet-namestore-fcfsd",
1108 _ ("GNU Name System First-Come-First-Served name registration service"),
1109 options,
1110 &run_service,
1111 NULL)) ?
1112 0 :
1113 1);
1114}