diff options
author | Alessio Vanni <vannilla@firemail.cc> | 2021-05-02 19:02:26 +0200 |
---|---|---|
committer | Alessio Vanni <vannilla@firemail.cc> | 2021-05-07 22:38:02 +0200 |
commit | 2bca4006c49c7b3875034f6fd6530cbcfe20bcb1 (patch) | |
tree | 391f0b63a81385b8ad7fabd7cb120c4de9a86ef6 /src/namestore | |
parent | a5082240f035f1851715771b0265e25088bb687c (diff) | |
download | gnunet-2bca4006c49c7b3875034f6fd6530cbcfe20bcb1.tar.gz gnunet-2bca4006c49c7b3875034f6fd6530cbcfe20bcb1.zip |
[FCFSD] Provide a better user experience
The motivations behind these changes are as following.
To begin with, at the most superficial level, the form is given a better
appearance, instead of some plain XHTML. Additionally, the served pages can
be substituted with something else by using an entry in the configuration
value, altough with some limitations.
The page listing all the registered zones has been removed in favour of a
search function. A configuration entry could've been used to let service
operators choose between showing the full listing or not, but at the same
time, being presented with a (possibly) giant list of names is not that great
from a usability point of view. Having a search function is, at the very
least, faster than having to wait for the full list to be displayed before
being able to use the user agent's page search feature.
Other than the above, people registering names with the service might not want
to be known by everyone. Even though checking if a certain name or key was
registered already can be known simply by querying the service, it's not
straightforward to associate a name with a specific key (or viceversa).
Last but not least, the service was restructured to be more "route-oriented"
instead of the traditional (X)HTML document format. The main purpose of this
change is to decouple usage of the service from the tools used to access it.
With a traditional document, users are pretty much forced to use a web
browsers as data submission is carried through the standard HTML form
handling. Now, it is possible to access the service using any tool capable of
speaking HTTP, regardless of wether it's a web browser, cURL or even a custom
tool specific for this service.
Another advantage of this approach is that it allows adding "layers" to the
service, for example an authentication check before letting users register a
name. As long as the layer immediately on top of the service is able to send
some JSON using HTTP, there is no need to have users access the service
itself: just put a "proxy" inbetween and run the service locally, while the
proxy handles other administrative tasks before a name can be registered.
By using layers, the service can keep being small feature-wise (i.e. provide
only searching and registering), while everything else is provided by other
applications, including access through protocols other than HTTP.
Diffstat (limited to 'src/namestore')
-rw-r--r-- | src/namestore/Makefile.am | 3 | ||||
-rw-r--r-- | src/namestore/gnunet-namestore-fcfsd.c | 1594 |
2 files changed, 735 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 | ||
186 | gnunet_namestore_fcfsd_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS) | 187 | gnunet_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..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 | */ |
82 | enum Phase | 42 | struct 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 | */ |
114 | struct Request | 63 | struct 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 | */ | ||
173 | struct 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 | */ |
199 | static struct MHD_Daemon *httpd; | 119 | static char *zone = NULL; |
200 | 120 | ||
201 | /** | 121 | /** |
202 | * Main HTTP task. | 122 | * The port the daemon is listening to for HTTP requests. |
203 | */ | 123 | */ |
204 | static struct GNUNET_SCHEDULER_Task *httpd_task; | 124 | static unsigned long long port = 18080; |
205 | 125 | ||
206 | /** | 126 | /** |
207 | * Handle to the namestore. | 127 | * Connection with the namestore service. |
208 | */ | 128 | */ |
209 | static struct GNUNET_NAMESTORE_Handle *ns; | 129 | static 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 | */ |
214 | static struct GNUNET_IDENTITY_PrivateKey fcfs_zone_pkey; | 134 | static struct GNUNET_IDENTITY_Handle *identity = NULL; |
215 | 135 | ||
216 | /** | 136 | /** |
217 | * Connection to identity service. | 137 | * Private key of the zone. |
218 | */ | 138 | */ |
219 | static struct GNUNET_IDENTITY_Handle *identity; | 139 | static const struct GNUNET_IDENTITY_PrivateKey *zone_key = NULL; |
220 | 140 | ||
221 | /** | 141 | /** |
222 | * Zoneinfo page we currently use. | 142 | * The HTTP daemon. |
223 | */ | 143 | */ |
224 | static struct MHD_Response *info_page; | 144 | static 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 | */ |
229 | static struct GNUNET_SCHEDULER_Task *uzp_task; | 149 | static 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 | */ |
234 | static struct GNUNET_IDENTITY_Operation *id_op; | 154 | static 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 | */ |
239 | static unsigned long long port; | 159 | static 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 | */ |
244 | static char *zone; | 165 | static 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 | */ |
252 | static void | 172 | static void |
253 | do_httpd (void *cls); | 173 | do_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 | */ | ||
259 | static void | ||
260 | run_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 | */ |
274 | static void | 215 | static void |
275 | update_zoneinfo_page (void *cls); | 216 | do_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 | */ |
281 | static void | 221 | static void |
282 | zone_iteration_error (void *cls) | 222 | run_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 | */ |
297 | static void | 267 | static void |
298 | zone_iteration_end (void *cls) | 268 | do_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 | ||
275 | static void | ||
276 | run_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 | */ |
332 | static void | 294 | static char * |
333 | iterate_cb (void *cls, | 295 | make_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 | */ | ||
395 | static int | ||
396 | serve_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 | */ |
407 | static void | 347 | static void |
408 | update_zoneinfo_page (void *cls) | 348 | search_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 | */ |
438 | static int | 370 | static void |
439 | serve_main_page (struct MHD_Connection *connection) | 371 | search_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 | */ | ||
466 | static int | ||
467 | fill_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 | */ |
514 | static MHD_RESULT | 398 | static void |
515 | post_iterator (void *cls, | 399 | register_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 | */ |
568 | static void | 419 | static void |
569 | put_continuation (void *cls, | 420 | register_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 | */ |
594 | static void | 460 | static void |
595 | zone_to_name_error (void *cls) | 461 | register_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 | */ | ||
616 | static void | ||
617 | zone_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 | ®ister_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 | */ |
669 | static void | 531 | static void |
670 | lookup_it_error (void *cls) | 532 | iterate_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 | */ |
691 | static void | 554 | static void |
692 | lookup_it_processor (void *cls, | 555 | iterate_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 | */ | ||
716 | static void | 593 | static void |
717 | lookup_it_finished (void *cls) | 594 | iterate_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 | ®ister_error_cb, |
728 | GNUNET_IDENTITY_public_key_from_string (request->public_key, | 605 | rd, |
729 | &request->pub)) | 606 | ®ister_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 | */ | ||
619 | static MHD_RESULT | ||
620 | serve_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 | */ |
770 | static MHD_RESULT | 647 | static MHD_RESULT |
771 | create_response (void *cls, | 648 | create_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; |
918 | static void | 784 | return MHD_YES; |
919 | request_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 = |
945 | static void | 819 | GNUNET_NAMESTORE_zone_iteration_start (namestore, |
946 | run_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 | */ |
1005 | static void | 848 | static void |
1006 | do_httpd (void *cls) | 849 | completed_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 | */ | ||
1020 | static void | ||
1021 | do_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 | */ |
1075 | static void | 901 | static void |
1076 | identity_cb (void *cls, | 902 | identity_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 | */ | ||
958 | static struct StaticPage * | ||
959 | open_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 | */ |
1140 | static void | 1007 | static void |
1141 | run (void *cls, | 1008 | run_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 | */ |
1189 | int | 1079 | int |
1190 | main (int argc, | 1080 | main (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 | } | ||