diff options
Diffstat (limited to 'src/util/service.c')
-rw-r--r-- | src/util/service.c | 1451 |
1 files changed, 1451 insertions, 0 deletions
diff --git a/src/util/service.c b/src/util/service.c new file mode 100644 index 000000000..af71db692 --- /dev/null +++ b/src/util/service.c | |||
@@ -0,0 +1,1451 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | (C) 2009 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 2, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file util/service.c | ||
23 | * @brief functions related to starting services | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_common.h" | ||
28 | #include "gnunet_configuration_lib.h" | ||
29 | #include "gnunet_crypto_lib.h" | ||
30 | #include "gnunet_directories.h" | ||
31 | #include "gnunet_disk_lib.h" | ||
32 | #include "gnunet_getopt_lib.h" | ||
33 | #include "gnunet_os_lib.h" | ||
34 | #include "gnunet_protocols.h" | ||
35 | #include "gnunet_server_lib.h" | ||
36 | #include "gnunet_service_lib.h" | ||
37 | |||
38 | /* ******************* access control ******************** */ | ||
39 | |||
40 | /** | ||
41 | * @brief IPV4 network in CIDR notation. | ||
42 | */ | ||
43 | struct IPv4NetworkSet | ||
44 | { | ||
45 | struct in_addr network; | ||
46 | struct in_addr netmask; | ||
47 | }; | ||
48 | |||
49 | /** | ||
50 | * @brief network in CIDR notation for IPV6. | ||
51 | */ | ||
52 | struct IPv6NetworkSet | ||
53 | { | ||
54 | struct in6_addr network; | ||
55 | struct in6_addr netmask; | ||
56 | }; | ||
57 | |||
58 | |||
59 | /** | ||
60 | * Parse a network specification. The argument specifies | ||
61 | * a list of networks. The format is | ||
62 | * <tt>[network/netmask;]*</tt> (no whitespace, must be terminated | ||
63 | * with a semicolon). The network must be given in dotted-decimal | ||
64 | * notation. The netmask can be given in CIDR notation (/16) or | ||
65 | * in dotted-decimal (/255.255.0.0). | ||
66 | * <p> | ||
67 | * @param routeList a string specifying the forbidden networks | ||
68 | * @return the converted list, NULL if the synatx is flawed | ||
69 | */ | ||
70 | static struct IPv4NetworkSet * | ||
71 | parse_ipv4_specification (const char *routeList) | ||
72 | { | ||
73 | unsigned int count; | ||
74 | unsigned int i; | ||
75 | unsigned int j; | ||
76 | unsigned int len; | ||
77 | int cnt; | ||
78 | unsigned int pos; | ||
79 | unsigned int temps[8]; | ||
80 | int slash; | ||
81 | struct IPv4NetworkSet *result; | ||
82 | |||
83 | if (routeList == NULL) | ||
84 | return NULL; | ||
85 | len = strlen (routeList); | ||
86 | if (len == 0) | ||
87 | return NULL; | ||
88 | count = 0; | ||
89 | for (i = 0; i < len; i++) | ||
90 | if (routeList[i] == ';') | ||
91 | count++; | ||
92 | result = GNUNET_malloc (sizeof (struct IPv4NetworkSet) * (count + 1)); | ||
93 | /* add termination */ | ||
94 | memset (result, 0, sizeof (struct IPv4NetworkSet) * (count + 1)); | ||
95 | i = 0; | ||
96 | pos = 0; | ||
97 | while (i < count) | ||
98 | { | ||
99 | cnt = sscanf (&routeList[pos], | ||
100 | "%u.%u.%u.%u/%u.%u.%u.%u;", | ||
101 | &temps[0], | ||
102 | &temps[1], | ||
103 | &temps[2], | ||
104 | &temps[3], &temps[4], &temps[5], &temps[6], &temps[7]); | ||
105 | if (cnt == 8) | ||
106 | { | ||
107 | for (j = 0; j < 8; j++) | ||
108 | if (temps[j] > 0xFF) | ||
109 | { | ||
110 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
111 | _("Invalid format for IP: `%s'\n"), | ||
112 | &routeList[pos]); | ||
113 | GNUNET_free (result); | ||
114 | return NULL; | ||
115 | } | ||
116 | result[i].network.s_addr | ||
117 | = | ||
118 | htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) + | ||
119 | temps[3]); | ||
120 | result[i].netmask.s_addr = | ||
121 | htonl ((temps[4] << 24) + (temps[5] << 16) + (temps[6] << 8) + | ||
122 | temps[7]); | ||
123 | while (routeList[pos] != ';') | ||
124 | pos++; | ||
125 | pos++; | ||
126 | i++; | ||
127 | continue; | ||
128 | } | ||
129 | /* try second notation */ | ||
130 | cnt = sscanf (&routeList[pos], | ||
131 | "%u.%u.%u.%u/%u;", | ||
132 | &temps[0], &temps[1], &temps[2], &temps[3], &slash); | ||
133 | if (cnt == 5) | ||
134 | { | ||
135 | for (j = 0; j < 4; j++) | ||
136 | if (temps[j] > 0xFF) | ||
137 | { | ||
138 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
139 | _("Invalid format for IP: `%s'\n"), | ||
140 | &routeList[pos]); | ||
141 | GNUNET_free (result); | ||
142 | return NULL; | ||
143 | } | ||
144 | result[i].network.s_addr | ||
145 | = | ||
146 | htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) + | ||
147 | temps[3]); | ||
148 | if ((slash <= 32) && (slash >= 0)) | ||
149 | { | ||
150 | result[i].netmask.s_addr = 0; | ||
151 | while (slash > 0) | ||
152 | { | ||
153 | result[i].netmask.s_addr | ||
154 | = (result[i].netmask.s_addr >> 1) + 0x80000000; | ||
155 | slash--; | ||
156 | } | ||
157 | result[i].netmask.s_addr = htonl (result[i].netmask.s_addr); | ||
158 | while (routeList[pos] != ';') | ||
159 | pos++; | ||
160 | pos++; | ||
161 | i++; | ||
162 | continue; | ||
163 | } | ||
164 | else | ||
165 | { | ||
166 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
167 | _ | ||
168 | ("Invalid network notation ('/%d' is not legal in IPv4 CIDR)."), | ||
169 | slash); | ||
170 | GNUNET_free (result); | ||
171 | return NULL; /* error */ | ||
172 | } | ||
173 | } | ||
174 | /* try third notation */ | ||
175 | slash = 32; | ||
176 | cnt = sscanf (&routeList[pos], | ||
177 | "%u.%u.%u.%u;", | ||
178 | &temps[0], &temps[1], &temps[2], &temps[3]); | ||
179 | if (cnt == 4) | ||
180 | { | ||
181 | for (j = 0; j < 4; j++) | ||
182 | if (temps[j] > 0xFF) | ||
183 | { | ||
184 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
185 | _("Invalid format for IP: `%s'\n"), | ||
186 | &routeList[pos]); | ||
187 | GNUNET_free (result); | ||
188 | return NULL; | ||
189 | } | ||
190 | result[i].network.s_addr | ||
191 | = | ||
192 | htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) + | ||
193 | temps[3]); | ||
194 | result[i].netmask.s_addr = 0; | ||
195 | while (slash > 0) | ||
196 | { | ||
197 | result[i].netmask.s_addr | ||
198 | = (result[i].netmask.s_addr >> 1) + 0x80000000; | ||
199 | slash--; | ||
200 | } | ||
201 | result[i].netmask.s_addr = htonl (result[i].netmask.s_addr); | ||
202 | while (routeList[pos] != ';') | ||
203 | pos++; | ||
204 | pos++; | ||
205 | i++; | ||
206 | continue; | ||
207 | } | ||
208 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
209 | _("Invalid format for IP: `%s'\n"), &routeList[pos]); | ||
210 | GNUNET_free (result); | ||
211 | return NULL; /* error */ | ||
212 | } | ||
213 | if (pos < strlen (routeList)) | ||
214 | { | ||
215 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
216 | _("Invalid format for IP: `%s'\n"), &routeList[pos]); | ||
217 | GNUNET_free (result); | ||
218 | return NULL; /* oops */ | ||
219 | } | ||
220 | return result; /* ok */ | ||
221 | } | ||
222 | |||
223 | |||
224 | /** | ||
225 | * Parse a network specification. The argument specifies | ||
226 | * a list of networks. The format is | ||
227 | * <tt>[network/netmask;]*</tt> (no whitespace, must be terminated | ||
228 | * with a semicolon). The network must be given in colon-hex | ||
229 | * notation. The netmask must be given in CIDR notation (/16) or | ||
230 | * can be omitted to specify a single host. | ||
231 | * <p> | ||
232 | * @param routeList a string specifying the forbidden networks | ||
233 | * @return the converted list, NULL if the synatx is flawed | ||
234 | */ | ||
235 | static struct IPv6NetworkSet * | ||
236 | parse_ipv6_specification (const char *routeListX) | ||
237 | { | ||
238 | unsigned int count; | ||
239 | unsigned int i; | ||
240 | unsigned int len; | ||
241 | unsigned int pos; | ||
242 | int start; | ||
243 | int slash; | ||
244 | int ret; | ||
245 | char *routeList; | ||
246 | struct IPv6NetworkSet *result; | ||
247 | unsigned int bits; | ||
248 | unsigned int off; | ||
249 | int save; | ||
250 | |||
251 | if (routeListX == NULL) | ||
252 | return NULL; | ||
253 | len = strlen (routeListX); | ||
254 | if (len == 0) | ||
255 | return NULL; | ||
256 | routeList = GNUNET_strdup (routeListX); | ||
257 | count = 0; | ||
258 | for (i = 0; i < len; i++) | ||
259 | if (routeList[i] == ';') | ||
260 | count++; | ||
261 | if (routeList[len - 1] != ';') | ||
262 | { | ||
263 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
264 | _ | ||
265 | ("Invalid network notation (does not end with ';': `%s')\n"), | ||
266 | routeList); | ||
267 | GNUNET_free (routeList); | ||
268 | return NULL; | ||
269 | } | ||
270 | |||
271 | result = GNUNET_malloc (sizeof (struct IPv6NetworkSet) * (count + 1)); | ||
272 | memset (result, 0, sizeof (struct IPv6NetworkSet) * (count + 1)); | ||
273 | i = 0; | ||
274 | pos = 0; | ||
275 | while (i < count) | ||
276 | { | ||
277 | start = pos; | ||
278 | while (routeList[pos] != ';') | ||
279 | pos++; | ||
280 | slash = pos; | ||
281 | while ((slash >= start) && (routeList[slash] != '/')) | ||
282 | slash--; | ||
283 | if (slash < start) | ||
284 | { | ||
285 | memset (&result[i].netmask, 0xFF, sizeof (struct in6_addr)); | ||
286 | slash = pos; | ||
287 | } | ||
288 | else | ||
289 | { | ||
290 | routeList[pos] = '\0'; | ||
291 | ret = inet_pton (AF_INET6, | ||
292 | &routeList[slash + 1], &result[i].netmask); | ||
293 | if (ret <= 0) | ||
294 | { | ||
295 | save = errno; | ||
296 | if ((1 != SSCANF (&routeList[slash + 1], | ||
297 | "%u", &bits)) || (bits >= 128)) | ||
298 | { | ||
299 | if (ret == 0) | ||
300 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
301 | _("Wrong format `%s' for netmask\n"), | ||
302 | &routeList[slash + 1]); | ||
303 | else | ||
304 | { | ||
305 | errno = save; | ||
306 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, | ||
307 | "inet_pton"); | ||
308 | } | ||
309 | GNUNET_free (result); | ||
310 | GNUNET_free (routeList); | ||
311 | return NULL; | ||
312 | } | ||
313 | off = 0; | ||
314 | while (bits > 8) | ||
315 | { | ||
316 | result[i].netmask.s6_addr[off++] = 0xFF; | ||
317 | bits -= 8; | ||
318 | } | ||
319 | while (bits > 0) | ||
320 | { | ||
321 | result[i].netmask.s6_addr[off] | ||
322 | = (result[i].netmask.s6_addr[off] >> 1) + 0x80; | ||
323 | bits--; | ||
324 | } | ||
325 | } | ||
326 | } | ||
327 | routeList[slash] = '\0'; | ||
328 | ret = inet_pton (AF_INET6, &routeList[start], &result[i].network); | ||
329 | if (ret <= 0) | ||
330 | { | ||
331 | if (ret == 0) | ||
332 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
333 | _("Wrong format `%s' for network\n"), | ||
334 | &routeList[slash + 1]); | ||
335 | else | ||
336 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "inet_pton"); | ||
337 | GNUNET_free (result); | ||
338 | GNUNET_free (routeList); | ||
339 | return NULL; | ||
340 | } | ||
341 | pos++; | ||
342 | i++; | ||
343 | } | ||
344 | GNUNET_free (routeList); | ||
345 | return result; | ||
346 | } | ||
347 | |||
348 | |||
349 | /** | ||
350 | * Check if the given IP address is in the list of IP addresses. | ||
351 | * | ||
352 | * @param list a list of networks | ||
353 | * @param ip the IP to check (in network byte order) | ||
354 | * @return GNUNET_NO if the IP is not in the list, GNUNET_YES if it it is | ||
355 | */ | ||
356 | static int | ||
357 | check_ipv4_listed (const struct IPv4NetworkSet *list, | ||
358 | const struct in_addr *add) | ||
359 | { | ||
360 | int i; | ||
361 | |||
362 | i = 0; | ||
363 | if (list == NULL) | ||
364 | return GNUNET_NO; | ||
365 | |||
366 | while ((list[i].network.s_addr != 0) || (list[i].netmask.s_addr != 0)) | ||
367 | { | ||
368 | if ((add->s_addr & list[i].netmask.s_addr) == | ||
369 | (list[i].network.s_addr & list[i].netmask.s_addr)) | ||
370 | return GNUNET_YES; | ||
371 | i++; | ||
372 | } | ||
373 | return GNUNET_NO; | ||
374 | } | ||
375 | |||
376 | /** | ||
377 | * Check if the given IP address is in the list of IP addresses. | ||
378 | * | ||
379 | * @param list a list of networks | ||
380 | * @param ip the IP to check (in network byte order) | ||
381 | * @return GNUNET_NO if the IP is not in the list, GNUNET_YES if it it is | ||
382 | */ | ||
383 | static int | ||
384 | check_ipv6_listed (const struct IPv6NetworkSet *list, | ||
385 | const struct in6_addr *ip) | ||
386 | { | ||
387 | unsigned int i; | ||
388 | unsigned int j; | ||
389 | struct in6_addr zero; | ||
390 | |||
391 | if (list == NULL) | ||
392 | return GNUNET_NO; | ||
393 | |||
394 | memset (&zero, 0, sizeof (struct in6_addr)); | ||
395 | i = 0; | ||
396 | NEXT: | ||
397 | while (memcmp (&zero, &list[i].network, sizeof (struct in6_addr)) != 0) | ||
398 | { | ||
399 | for (j = 0; j < sizeof (struct in6_addr) / sizeof (int); j++) | ||
400 | if (((((int *) ip)[j] & ((int *) &list[i].netmask)[j])) != | ||
401 | (((int *) &list[i].network)[j] & ((int *) &list[i].netmask)[j])) | ||
402 | { | ||
403 | i++; | ||
404 | goto NEXT; | ||
405 | } | ||
406 | return GNUNET_YES; | ||
407 | } | ||
408 | return GNUNET_NO; | ||
409 | } | ||
410 | |||
411 | |||
412 | /* ****************** service struct ****************** */ | ||
413 | |||
414 | |||
415 | /** | ||
416 | * Context for "service_task". | ||
417 | */ | ||
418 | struct GNUNET_SERVICE_Context | ||
419 | { | ||
420 | /** | ||
421 | * Our configuration. | ||
422 | */ | ||
423 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
424 | |||
425 | /** | ||
426 | * Handle for the server. | ||
427 | */ | ||
428 | struct GNUNET_SERVER_Handle *server; | ||
429 | |||
430 | /** | ||
431 | * Scheduler for the server. | ||
432 | */ | ||
433 | struct GNUNET_SCHEDULER_Handle *sched; | ||
434 | |||
435 | /** | ||
436 | * Address to bind to. | ||
437 | */ | ||
438 | struct sockaddr *addr; | ||
439 | |||
440 | /** | ||
441 | * Name of our service. | ||
442 | */ | ||
443 | const char *serviceName; | ||
444 | |||
445 | /** | ||
446 | * Main service-specific task to run. | ||
447 | */ | ||
448 | GNUNET_SERVICE_Main task; | ||
449 | |||
450 | /** | ||
451 | * Closure for task. | ||
452 | */ | ||
453 | void *task_cls; | ||
454 | |||
455 | /** | ||
456 | * IPv4 addresses that are not allowed to connect. | ||
457 | */ | ||
458 | struct IPv4NetworkSet *v4_denied; | ||
459 | |||
460 | /** | ||
461 | * IPv6 addresses that are not allowed to connect. | ||
462 | */ | ||
463 | struct IPv6NetworkSet *v6_denied; | ||
464 | |||
465 | /** | ||
466 | * IPv4 addresses that are allowed to connect (if not | ||
467 | * set, all are allowed). | ||
468 | */ | ||
469 | struct IPv4NetworkSet *v4_allowed; | ||
470 | |||
471 | /** | ||
472 | * IPv6 addresses that are allowed to connect (if not | ||
473 | * set, all are allowed). | ||
474 | */ | ||
475 | struct IPv6NetworkSet *v6_allowed; | ||
476 | |||
477 | /** | ||
478 | * My (default) message handlers. Adjusted copy | ||
479 | * of "defhandlers". | ||
480 | */ | ||
481 | struct GNUNET_SERVER_MessageHandler *my_handlers; | ||
482 | |||
483 | /** | ||
484 | * Idle timeout for server. | ||
485 | */ | ||
486 | struct GNUNET_TIME_Relative timeout; | ||
487 | |||
488 | /** | ||
489 | * Maximum buffer size for the server. | ||
490 | */ | ||
491 | size_t maxbuf; | ||
492 | |||
493 | /** | ||
494 | * Overall success/failure of the service start. | ||
495 | */ | ||
496 | int ret; | ||
497 | |||
498 | /** | ||
499 | * If we are daemonizing, this FD is set to the | ||
500 | * pipe to the parent. Send '.' if we started | ||
501 | * ok, '!' if not. -1 if we are not daemonizing. | ||
502 | */ | ||
503 | int ready_confirm_fd; | ||
504 | |||
505 | /** | ||
506 | * Do we close connections if we receive messages | ||
507 | * for which we have no handler? | ||
508 | */ | ||
509 | int require_found; | ||
510 | |||
511 | /** | ||
512 | * Can clients ask us to initiate a shutdown? | ||
513 | */ | ||
514 | int allow_shutdown; | ||
515 | |||
516 | /** | ||
517 | * Length of addr. | ||
518 | */ | ||
519 | socklen_t addrlen; | ||
520 | |||
521 | }; | ||
522 | |||
523 | |||
524 | /* ****************** message handlers ****************** */ | ||
525 | |||
526 | static size_t | ||
527 | write_test (void *cls, size_t size, void *buf) | ||
528 | { | ||
529 | struct GNUNET_SERVER_Client *client = cls; | ||
530 | struct GNUNET_MessageHeader *msg; | ||
531 | |||
532 | if (size < sizeof (struct GNUNET_MessageHeader)) | ||
533 | { | ||
534 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
535 | return 0; /* client disconnected */ | ||
536 | } | ||
537 | msg = (struct GNUNET_MessageHeader *) buf; | ||
538 | msg->type = htons (GNUNET_MESSAGE_TYPE_TEST); | ||
539 | msg->size = htons (sizeof (struct GNUNET_MessageHeader)); | ||
540 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
541 | return sizeof (struct GNUNET_MessageHeader); | ||
542 | } | ||
543 | |||
544 | /** | ||
545 | * Handler for TEST message. | ||
546 | * | ||
547 | * @param cls closure (refers to service) | ||
548 | * @param server the server handling the message | ||
549 | * @param client identification of the client | ||
550 | * @param message the actual message | ||
551 | */ | ||
552 | static void | ||
553 | handle_test (void *cls, | ||
554 | struct GNUNET_SERVER_Handle *server, | ||
555 | struct GNUNET_SERVER_Client *client, | ||
556 | const struct GNUNET_MessageHeader *message) | ||
557 | { | ||
558 | /* simply bounce message back to acknowledge */ | ||
559 | if (NULL == GNUNET_SERVER_notify_transmit_ready (client, | ||
560 | sizeof (struct | ||
561 | GNUNET_MessageHeader), | ||
562 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
563 | &write_test, client)) | ||
564 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
565 | } | ||
566 | |||
567 | |||
568 | /** | ||
569 | * Handler for SHUTDOWN message. | ||
570 | * | ||
571 | * @param cls closure (refers to service) | ||
572 | * @param server the server handling the message | ||
573 | * @param client identification of the client | ||
574 | * @param message the actual message | ||
575 | */ | ||
576 | static void | ||
577 | handle_shutdown (void *cls, | ||
578 | struct GNUNET_SERVER_Handle *server, | ||
579 | struct GNUNET_SERVER_Client *client, | ||
580 | const struct GNUNET_MessageHeader *message) | ||
581 | { | ||
582 | struct GNUNET_SERVICE_Context *service = cls; | ||
583 | if (!service->allow_shutdown) | ||
584 | { | ||
585 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
586 | _ | ||
587 | ("Received shutdown request, but configured to ignore!\n")); | ||
588 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
589 | return; | ||
590 | } | ||
591 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
592 | _("Initiating shutdown as requested by client.\n")); | ||
593 | GNUNET_assert (service->sched != NULL); | ||
594 | GNUNET_SCHEDULER_shutdown (service->sched); | ||
595 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
596 | } | ||
597 | |||
598 | |||
599 | /** | ||
600 | * Default handlers for all services. Will be copied and the | ||
601 | * "callback_cls" fields will be replaced with the specific service | ||
602 | * struct. | ||
603 | */ | ||
604 | static const struct GNUNET_SERVER_MessageHandler defhandlers[] = { | ||
605 | {&handle_test, NULL, GNUNET_MESSAGE_TYPE_TEST, | ||
606 | sizeof (struct GNUNET_MessageHeader)}, | ||
607 | {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_SHUTDOWN, | ||
608 | sizeof (struct GNUNET_MessageHeader)}, | ||
609 | {NULL, NULL, 0, 0} | ||
610 | }; | ||
611 | |||
612 | |||
613 | |||
614 | /* ****************** service core routines ************** */ | ||
615 | |||
616 | |||
617 | /** | ||
618 | * Check if access to the service is allowed from the given address. | ||
619 | */ | ||
620 | static int | ||
621 | check_access (void *cls, const struct sockaddr *addr, socklen_t addrlen) | ||
622 | { | ||
623 | struct GNUNET_SERVICE_Context *sctx = cls; | ||
624 | const struct sockaddr_in *i4; | ||
625 | const struct sockaddr_in6 *i6; | ||
626 | int ret; | ||
627 | char buf[INET6_ADDRSTRLEN]; | ||
628 | uint16_t port; | ||
629 | |||
630 | switch (addr->sa_family) | ||
631 | { | ||
632 | case AF_INET: | ||
633 | GNUNET_assert (addrlen == sizeof (struct sockaddr_in)); | ||
634 | i4 = (const struct sockaddr_in *) addr; | ||
635 | port = ntohs (i4->sin_port); | ||
636 | ret = ((sctx->v4_allowed == NULL) || | ||
637 | (check_ipv4_listed (sctx->v4_allowed, | ||
638 | &i4->sin_addr))) | ||
639 | && ((sctx->v4_denied == NULL) || | ||
640 | (!check_ipv4_listed (sctx->v4_denied, &i4->sin_addr))); | ||
641 | if (ret != GNUNET_OK) | ||
642 | inet_ntop (AF_INET, &i4->sin_addr, buf, sizeof (buf)); | ||
643 | break; | ||
644 | case AF_INET6: | ||
645 | GNUNET_assert (addrlen == sizeof (struct sockaddr_in6)); | ||
646 | i6 = (const struct sockaddr_in6 *) addr; | ||
647 | port = ntohs (i6->sin6_port); | ||
648 | ret = ((sctx->v6_allowed == NULL) || | ||
649 | (check_ipv6_listed (sctx->v6_allowed, | ||
650 | &i6->sin6_addr))) | ||
651 | && ((sctx->v6_denied == NULL) || | ||
652 | (!check_ipv6_listed (sctx->v6_denied, &i6->sin6_addr))); | ||
653 | if (ret != GNUNET_OK) | ||
654 | inet_ntop (AF_INET6, &i6->sin6_addr, buf, sizeof (buf)); | ||
655 | break; | ||
656 | default: | ||
657 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
658 | _("Unknown address family %d\n"), addr->sa_family); | ||
659 | return GNUNET_SYSERR; | ||
660 | } | ||
661 | if (ret != GNUNET_OK) | ||
662 | { | ||
663 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
664 | _("Access from `%s:%u' denied to service `%s'\n"), | ||
665 | buf, port, sctx->serviceName); | ||
666 | } | ||
667 | return ret; | ||
668 | } | ||
669 | |||
670 | |||
671 | /** | ||
672 | * Get the name of the file where we will | ||
673 | * write the PID of the service. | ||
674 | */ | ||
675 | static char * | ||
676 | get_pid_file_name (struct GNUNET_SERVICE_Context *sctx) | ||
677 | { | ||
678 | |||
679 | char *pif; | ||
680 | |||
681 | if (GNUNET_OK != | ||
682 | GNUNET_CONFIGURATION_get_value_filename (sctx->cfg, | ||
683 | sctx->serviceName, | ||
684 | "PIDFILE", &pif)) | ||
685 | return NULL; | ||
686 | return pif; | ||
687 | } | ||
688 | |||
689 | |||
690 | /** | ||
691 | * Parse an IPv4 access control list. | ||
692 | */ | ||
693 | static int | ||
694 | process_acl4 (struct IPv4NetworkSet **ret, | ||
695 | struct GNUNET_SERVICE_Context *sctx, const char *option) | ||
696 | { | ||
697 | char *opt; | ||
698 | |||
699 | if (!GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->serviceName, option)) | ||
700 | return GNUNET_OK; | ||
701 | GNUNET_break (GNUNET_OK == | ||
702 | GNUNET_CONFIGURATION_get_value_string (sctx->cfg, | ||
703 | sctx->serviceName, | ||
704 | option, &opt)); | ||
705 | if (NULL == (*ret = parse_ipv4_specification (opt))) | ||
706 | { | ||
707 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
708 | _ | ||
709 | ("Could not parse IPv4 network specification `%s' for `%s:%s'\n"), | ||
710 | opt, sctx->serviceName, option); | ||
711 | GNUNET_free (opt); | ||
712 | return GNUNET_SYSERR; | ||
713 | } | ||
714 | GNUNET_free (opt); | ||
715 | return GNUNET_OK; | ||
716 | } | ||
717 | |||
718 | |||
719 | /** | ||
720 | * Parse an IPv4 access control list. | ||
721 | */ | ||
722 | static int | ||
723 | process_acl6 (struct IPv6NetworkSet **ret, | ||
724 | struct GNUNET_SERVICE_Context *sctx, const char *option) | ||
725 | { | ||
726 | char *opt; | ||
727 | if (!GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->serviceName, option)) | ||
728 | return GNUNET_OK; | ||
729 | GNUNET_break (GNUNET_OK == | ||
730 | GNUNET_CONFIGURATION_get_value_string (sctx->cfg, | ||
731 | sctx->serviceName, | ||
732 | option, &opt)); | ||
733 | if (NULL == (*ret = parse_ipv6_specification (opt))) | ||
734 | { | ||
735 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
736 | _ | ||
737 | ("Could not parse IPv6 network specification `%s' for `%s:%s'\n"), | ||
738 | opt, sctx->serviceName, option); | ||
739 | GNUNET_free (opt); | ||
740 | return GNUNET_SYSERR; | ||
741 | } | ||
742 | GNUNET_free (opt); | ||
743 | return GNUNET_OK; | ||
744 | } | ||
745 | |||
746 | |||
747 | /** | ||
748 | * Setup addr, addrlen, maxbuf, idle_timeout | ||
749 | * based on configuration! | ||
750 | * | ||
751 | * Configuration must specify a "PORT". It may | ||
752 | * specify: | ||
753 | * - TIMEOUT (after how many ms does an inactive service timeout); | ||
754 | * - MAXBUF (maximum incoming message size supported) | ||
755 | * - DISABLEV6 (disable support for IPv6, otherwise we use dual-stack) | ||
756 | * - ALLOW_SHUTDOWN (allow clients to shutdown this service) | ||
757 | * - BINDTO (hostname or IP address to bind to, otherwise we take everything) | ||
758 | * - ACCEPT_FROM (only allow connections from specified IPv4 subnets) | ||
759 | * - ACCEPT_FROM6 (only allow connections from specified IPv6 subnets) | ||
760 | * - REJECT_FROM (disallow allow connections from specified IPv4 subnets) | ||
761 | * - REJECT_FROM6 (disallow allow connections from specified IPv6 subnets) | ||
762 | * | ||
763 | * @return GNUNET_OK if configuration succeeded | ||
764 | */ | ||
765 | static int | ||
766 | setup_service (struct GNUNET_SERVICE_Context *sctx) | ||
767 | { | ||
768 | unsigned long long maxbuf; | ||
769 | unsigned long long idleout; | ||
770 | char *hostname; | ||
771 | unsigned long long port; | ||
772 | int disablev6; | ||
773 | struct addrinfo hints; | ||
774 | struct addrinfo *res; | ||
775 | struct addrinfo *pos; | ||
776 | int ret; | ||
777 | int tolerant; | ||
778 | |||
779 | if (GNUNET_CONFIGURATION_have_value (sctx->cfg, | ||
780 | sctx->serviceName, "TIMEOUT")) | ||
781 | { | ||
782 | if (GNUNET_OK != | ||
783 | GNUNET_CONFIGURATION_get_value_number (sctx->cfg, | ||
784 | sctx->serviceName, | ||
785 | "TIMEOUT", &idleout)) | ||
786 | return GNUNET_SYSERR; | ||
787 | |||
788 | sctx->timeout.value = idleout; | ||
789 | } | ||
790 | else | ||
791 | sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL; | ||
792 | if (GNUNET_CONFIGURATION_have_value (sctx->cfg, | ||
793 | sctx->serviceName, "MAXBUF")) | ||
794 | { | ||
795 | if (GNUNET_OK != | ||
796 | GNUNET_CONFIGURATION_get_value_number (sctx->cfg, | ||
797 | sctx->serviceName, | ||
798 | "MAXBUF", &maxbuf)) | ||
799 | return GNUNET_SYSERR; | ||
800 | } | ||
801 | else | ||
802 | maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE; | ||
803 | if (GNUNET_CONFIGURATION_have_value (sctx->cfg, | ||
804 | sctx->serviceName, "DISABLEV6")) | ||
805 | { | ||
806 | if (GNUNET_SYSERR == | ||
807 | (disablev6 = GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, | ||
808 | sctx-> | ||
809 | serviceName, | ||
810 | "DISABLEV6"))) | ||
811 | return GNUNET_SYSERR; | ||
812 | } | ||
813 | else | ||
814 | disablev6 = GNUNET_NO; | ||
815 | if (GNUNET_CONFIGURATION_have_value (sctx->cfg, | ||
816 | sctx->serviceName, "ALLOW_SHUTDOWN")) | ||
817 | { | ||
818 | if (GNUNET_SYSERR == | ||
819 | (sctx->allow_shutdown = | ||
820 | GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->serviceName, | ||
821 | "ALLOW_SHUTDOWN"))) | ||
822 | return GNUNET_SYSERR; | ||
823 | } | ||
824 | else | ||
825 | sctx->allow_shutdown = GNUNET_NO; | ||
826 | |||
827 | if (!disablev6) | ||
828 | { | ||
829 | /* probe IPv6 support */ | ||
830 | ret = SOCKET (PF_INET6, SOCK_STREAM, 0); | ||
831 | if (ret == -1) | ||
832 | { | ||
833 | if ((errno == ENOBUFS) || | ||
834 | (errno == ENOMEM) || (errno == ENFILE) || (errno == EACCES)) | ||
835 | { | ||
836 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); | ||
837 | return GNUNET_SYSERR; | ||
838 | } | ||
839 | ret = SOCKET (PF_INET, SOCK_STREAM, 0); | ||
840 | if (ret != -1) | ||
841 | { | ||
842 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
843 | _ | ||
844 | ("Disabling IPv6 support for service `%s', failed to create IPv6 socket: %s\n"), | ||
845 | sctx->serviceName, strerror (errno)); | ||
846 | disablev6 = GNUNET_YES; | ||
847 | } | ||
848 | } | ||
849 | if (ret != -1) | ||
850 | GNUNET_break (0 == CLOSE (ret)); | ||
851 | } | ||
852 | |||
853 | |||
854 | |||
855 | if (GNUNET_CONFIGURATION_have_value (sctx->cfg, | ||
856 | sctx->serviceName, "TOLERANT")) | ||
857 | { | ||
858 | if (GNUNET_SYSERR == | ||
859 | (tolerant = GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, | ||
860 | sctx->serviceName, | ||
861 | "TOLERANT"))) | ||
862 | return GNUNET_SYSERR; | ||
863 | } | ||
864 | else | ||
865 | tolerant = GNUNET_NO; | ||
866 | sctx->require_found = tolerant ? GNUNET_NO : GNUNET_YES; | ||
867 | |||
868 | |||
869 | if ((GNUNET_OK != | ||
870 | GNUNET_CONFIGURATION_get_value_number (sctx->cfg, | ||
871 | sctx->serviceName, | ||
872 | "PORT", | ||
873 | &port)) || (port > 65535)) | ||
874 | { | ||
875 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
876 | _ | ||
877 | ("Require valid port number for service `%s' in configuration!\n"), | ||
878 | sctx->serviceName); | ||
879 | return GNUNET_SYSERR; | ||
880 | } | ||
881 | if (GNUNET_CONFIGURATION_have_value (sctx->cfg, | ||
882 | sctx->serviceName, "BINDTO")) | ||
883 | { | ||
884 | GNUNET_break (GNUNET_OK == | ||
885 | GNUNET_CONFIGURATION_get_value_string (sctx->cfg, | ||
886 | sctx->serviceName, | ||
887 | "BINDTO", | ||
888 | &hostname)); | ||
889 | } | ||
890 | else | ||
891 | hostname = NULL; | ||
892 | |||
893 | if (hostname != NULL) | ||
894 | { | ||
895 | memset (&hints, 0, sizeof (struct addrinfo)); | ||
896 | if (disablev6) | ||
897 | hints.ai_family = AF_INET; | ||
898 | if ((0 != (ret = getaddrinfo (hostname, | ||
899 | NULL, &hints, &res))) || (res == NULL)) | ||
900 | { | ||
901 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
902 | _("Failed to resolve `%s': %s\n"), | ||
903 | hostname, gai_strerror (ret)); | ||
904 | GNUNET_free (hostname); | ||
905 | return GNUNET_SYSERR; | ||
906 | } | ||
907 | pos = res; | ||
908 | while ((NULL != pos) && | ||
909 | (((disablev6) && | ||
910 | (pos->ai_family != AF_INET)) || | ||
911 | ((pos->ai_family != AF_INET) && (pos->ai_family != AF_INET6)))) | ||
912 | pos = pos->ai_next; | ||
913 | if (pos == NULL) | ||
914 | { | ||
915 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
916 | _("Failed to find IPv4 address for `%s'.\n"), hostname); | ||
917 | freeaddrinfo (res); | ||
918 | GNUNET_free (hostname); | ||
919 | return GNUNET_SYSERR; | ||
920 | } | ||
921 | GNUNET_free (hostname); | ||
922 | if (pos->ai_family == AF_INET) | ||
923 | { | ||
924 | GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in)); | ||
925 | sctx->addrlen = pos->ai_addrlen; | ||
926 | sctx->addr = GNUNET_malloc (sctx->addrlen); | ||
927 | memcpy (sctx->addr, res->ai_addr, sctx->addrlen); | ||
928 | ((struct sockaddr_in *) sctx->addr)->sin_port = htons (port); | ||
929 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
930 | _ | ||
931 | ("Configured to bind to %s address; %s connections to this service will fail!\n"), | ||
932 | "IPv4", "IPv6"); | ||
933 | } | ||
934 | else | ||
935 | { | ||
936 | GNUNET_assert (pos->ai_family == AF_INET6); | ||
937 | GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in6)); | ||
938 | sctx->addrlen = pos->ai_addrlen; | ||
939 | sctx->addr = GNUNET_malloc (sctx->addrlen); | ||
940 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
941 | _ | ||
942 | ("Configured to bind to %s address; %s connections to this service will fail!\n"), | ||
943 | "IPv6", "IPv4"); | ||
944 | ((struct sockaddr_in6 *) sctx->addr)->sin6_port = htons (port); | ||
945 | } | ||
946 | freeaddrinfo (res); | ||
947 | } | ||
948 | else | ||
949 | { | ||
950 | /* will bind against everything, just set port */ | ||
951 | if (disablev6) | ||
952 | { | ||
953 | /* V4-only */ | ||
954 | sctx->addrlen = sizeof (struct sockaddr_in6); | ||
955 | sctx->addr = GNUNET_malloc (sctx->addrlen); | ||
956 | ((struct sockaddr_in *) sctx->addr)->sin_family = AF_INET; | ||
957 | ((struct sockaddr_in *) sctx->addr)->sin_port = htons (port); | ||
958 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
959 | _ | ||
960 | ("Configured to bind to %s address; %s connections to this service will fail!\n"), | ||
961 | "IPv4", "IPv6"); | ||
962 | } | ||
963 | else | ||
964 | { | ||
965 | /* dual stack */ | ||
966 | sctx->addrlen = sizeof (struct sockaddr_in6); | ||
967 | sctx->addr = GNUNET_malloc (sctx->addrlen); | ||
968 | ((struct sockaddr_in6 *) sctx->addr)->sin6_family = AF_INET6; | ||
969 | ((struct sockaddr_in6 *) sctx->addr)->sin6_port = htons (port); | ||
970 | } | ||
971 | } | ||
972 | sctx->maxbuf = (size_t) maxbuf; | ||
973 | if (sctx->maxbuf != maxbuf) | ||
974 | { | ||
975 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
976 | _ | ||
977 | ("Value in configuration for `%s' and service `%s' too large!\n"), | ||
978 | "MAXBUF", sctx->serviceName); | ||
979 | return GNUNET_SYSERR; | ||
980 | } | ||
981 | |||
982 | |||
983 | if ((GNUNET_OK != | ||
984 | process_acl4 (&sctx->v4_denied, | ||
985 | sctx, | ||
986 | "REJECT_FROM")) || | ||
987 | (GNUNET_OK != | ||
988 | process_acl4 (&sctx->v4_allowed, | ||
989 | sctx, | ||
990 | "ACCEPT_FROM")) || | ||
991 | (GNUNET_OK != | ||
992 | process_acl6 (&sctx->v6_denied, | ||
993 | sctx, | ||
994 | "REJECT_FROM6")) || | ||
995 | (GNUNET_OK != process_acl6 (&sctx->v6_allowed, sctx, "ACCEPT_FROM6"))) | ||
996 | return GNUNET_SYSERR; | ||
997 | return GNUNET_OK; | ||
998 | } | ||
999 | |||
1000 | |||
1001 | /** | ||
1002 | * Get the name of the user that'll be used | ||
1003 | * to provide the service. | ||
1004 | */ | ||
1005 | static char * | ||
1006 | get_user_name (struct GNUNET_SERVICE_Context *sctx) | ||
1007 | { | ||
1008 | |||
1009 | char *un; | ||
1010 | |||
1011 | if (GNUNET_OK != | ||
1012 | GNUNET_CONFIGURATION_get_value_filename (sctx->cfg, | ||
1013 | sctx->serviceName, | ||
1014 | "USERNAME", &un)) | ||
1015 | return NULL; | ||
1016 | return un; | ||
1017 | } | ||
1018 | |||
1019 | /** | ||
1020 | * Write PID file. | ||
1021 | */ | ||
1022 | static int | ||
1023 | write_pid_file (struct GNUNET_SERVICE_Context *sctx, pid_t pid) | ||
1024 | { | ||
1025 | FILE *pidfd; | ||
1026 | char *pif; | ||
1027 | char *user; | ||
1028 | char *rdir; | ||
1029 | int len; | ||
1030 | |||
1031 | if (NULL == (pif = get_pid_file_name (sctx))) | ||
1032 | return GNUNET_OK; /* no file desired */ | ||
1033 | user = get_user_name (sctx); | ||
1034 | rdir = GNUNET_strdup (pif); | ||
1035 | len = strlen (rdir); | ||
1036 | while ((len > 0) && (rdir[len] != DIR_SEPARATOR)) | ||
1037 | len--; | ||
1038 | rdir[len] = '\0'; | ||
1039 | if (0 != ACCESS (rdir, F_OK)) | ||
1040 | { | ||
1041 | /* we get to create a directory -- and claim it | ||
1042 | as ours! */ | ||
1043 | GNUNET_DISK_directory_create (rdir); | ||
1044 | if ((user != NULL) && (0 < strlen (user))) | ||
1045 | GNUNET_DISK_file_change_owner (rdir, user); | ||
1046 | } | ||
1047 | if (0 != ACCESS (rdir, W_OK | X_OK)) | ||
1048 | { | ||
1049 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "access", rdir); | ||
1050 | GNUNET_free (rdir); | ||
1051 | GNUNET_free_non_null (user); | ||
1052 | GNUNET_free (pif); | ||
1053 | return GNUNET_SYSERR; | ||
1054 | } | ||
1055 | GNUNET_free (rdir); | ||
1056 | pidfd = FOPEN (pif, "w"); | ||
1057 | if (pidfd == NULL) | ||
1058 | { | ||
1059 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "fopen", pif); | ||
1060 | GNUNET_free (pif); | ||
1061 | GNUNET_free_non_null (user); | ||
1062 | return GNUNET_SYSERR; | ||
1063 | } | ||
1064 | if (0 > FPRINTF (pidfd, "%u", pid)) | ||
1065 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fprintf", pif); | ||
1066 | GNUNET_break (0 == fclose (pidfd)); | ||
1067 | if ((user != NULL) && (0 < strlen (user))) | ||
1068 | GNUNET_DISK_file_change_owner (pif, user); | ||
1069 | GNUNET_free_non_null (user); | ||
1070 | GNUNET_free (pif); | ||
1071 | return GNUNET_OK; | ||
1072 | } | ||
1073 | |||
1074 | |||
1075 | /** | ||
1076 | * Initial task for the service. | ||
1077 | */ | ||
1078 | static void | ||
1079 | service_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
1080 | { | ||
1081 | struct GNUNET_SERVICE_Context *sctx = cls; | ||
1082 | unsigned int i; | ||
1083 | |||
1084 | sctx->sched = tc->sched; | ||
1085 | sctx->server = GNUNET_SERVER_create (tc->sched, | ||
1086 | &check_access, | ||
1087 | sctx, | ||
1088 | sctx->addr, | ||
1089 | sctx->addrlen, | ||
1090 | sctx->maxbuf, | ||
1091 | sctx->timeout, sctx->require_found); | ||
1092 | if (sctx->server == NULL) | ||
1093 | { | ||
1094 | sctx->ret = GNUNET_SYSERR; | ||
1095 | return; | ||
1096 | } | ||
1097 | sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers)); | ||
1098 | memcpy (sctx->my_handlers, defhandlers, sizeof (defhandlers)); | ||
1099 | i = 0; | ||
1100 | while ((sctx->my_handlers[i].callback != NULL)) | ||
1101 | sctx->my_handlers[i++].callback_cls = sctx; | ||
1102 | GNUNET_SERVER_add_handlers (sctx->server, sctx->my_handlers); | ||
1103 | if (sctx->ready_confirm_fd != -1) | ||
1104 | { | ||
1105 | GNUNET_break (1 == WRITE (sctx->ready_confirm_fd, ".", 1)); | ||
1106 | GNUNET_break (0 == CLOSE (sctx->ready_confirm_fd)); | ||
1107 | sctx->ready_confirm_fd = -1; | ||
1108 | write_pid_file (sctx, getpid ()); | ||
1109 | } | ||
1110 | |||
1111 | sctx->task (sctx->task_cls, tc->sched, sctx->server, sctx->cfg); | ||
1112 | } | ||
1113 | |||
1114 | |||
1115 | /** | ||
1116 | * Detach from terminal. | ||
1117 | */ | ||
1118 | static int | ||
1119 | detach_terminal (struct GNUNET_SERVICE_Context *sctx) | ||
1120 | { | ||
1121 | pid_t pid; | ||
1122 | int nullfd; | ||
1123 | int filedes[2]; | ||
1124 | |||
1125 | #ifndef MINGW | ||
1126 | if (0 != PIPE (filedes)) | ||
1127 | { | ||
1128 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe"); | ||
1129 | return GNUNET_SYSERR; | ||
1130 | } | ||
1131 | pid = fork (); | ||
1132 | if (pid < 0) | ||
1133 | { | ||
1134 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork"); | ||
1135 | return GNUNET_SYSERR; | ||
1136 | } | ||
1137 | if (pid != 0) | ||
1138 | { | ||
1139 | /* Parent */ | ||
1140 | char c; | ||
1141 | |||
1142 | GNUNET_break (0 == CLOSE (filedes[1])); | ||
1143 | c = 'X'; | ||
1144 | if (1 != READ (filedes[0], &c, sizeof (char))) | ||
1145 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read"); | ||
1146 | fflush (stdout); | ||
1147 | switch (c) | ||
1148 | { | ||
1149 | case '.': | ||
1150 | exit (0); | ||
1151 | case 'I': | ||
1152 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1153 | _("Service process failed to initialize\n")); | ||
1154 | break; | ||
1155 | case 'S': | ||
1156 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1157 | _ | ||
1158 | ("Service process could not initialize server function\n")); | ||
1159 | break; | ||
1160 | case 'X': | ||
1161 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1162 | _("Service process failed to report status\n")); | ||
1163 | break; | ||
1164 | } | ||
1165 | exit (1); /* child reported error */ | ||
1166 | } | ||
1167 | GNUNET_break (0 == CLOSE (0)); | ||
1168 | GNUNET_break (0 == CLOSE (1)); | ||
1169 | GNUNET_break (0 == CLOSE (filedes[0])); | ||
1170 | nullfd = GNUNET_DISK_file_open ("/dev/null", O_RDWR | O_APPEND); | ||
1171 | if (nullfd < 0) | ||
1172 | return GNUNET_SYSERR; | ||
1173 | /* set stdin/stdout to /dev/null */ | ||
1174 | if ((dup2 (nullfd, 0) < 0) || (dup2 (nullfd, 1) < 0)) | ||
1175 | { | ||
1176 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2"); | ||
1177 | return GNUNET_SYSERR; | ||
1178 | } | ||
1179 | /* Detach from controlling terminal */ | ||
1180 | pid = setsid (); | ||
1181 | if (pid == -1) | ||
1182 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "setsid"); | ||
1183 | sctx->ready_confirm_fd = filedes[1]; | ||
1184 | #else | ||
1185 | /* FIXME: we probably need to do something else | ||
1186 | elsewhere in order to fork the process itself... */ | ||
1187 | FreeConsole (); | ||
1188 | #endif | ||
1189 | return GNUNET_OK; | ||
1190 | } | ||
1191 | |||
1192 | |||
1193 | /** | ||
1194 | * Set user ID. | ||
1195 | */ | ||
1196 | static int | ||
1197 | set_user_id (struct GNUNET_SERVICE_Context *sctx) | ||
1198 | { | ||
1199 | char *user; | ||
1200 | |||
1201 | if (NULL == (user = get_user_name (sctx))) | ||
1202 | return GNUNET_OK; /* keep */ | ||
1203 | #ifndef MINGW | ||
1204 | struct passwd *pws; | ||
1205 | |||
1206 | errno = 0; | ||
1207 | pws = getpwnam (user); | ||
1208 | if (pws == NULL) | ||
1209 | { | ||
1210 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1211 | _("Cannot obtain information about user `%s': %s\n"), | ||
1212 | user, errno == 0 ? _("No such user") : STRERROR (errno)); | ||
1213 | GNUNET_free (user); | ||
1214 | return GNUNET_SYSERR; | ||
1215 | } | ||
1216 | if ((0 != setgid (pws->pw_gid)) || (0 != setegid (pws->pw_gid)) || | ||
1217 | #if HAVE_INITGROUPS | ||
1218 | (0 != initgroups (user, pws->pw_gid)) || | ||
1219 | #endif | ||
1220 | (0 != setuid (pws->pw_uid)) || (0 != seteuid (pws->pw_uid))) | ||
1221 | { | ||
1222 | if ((0 != setregid (pws->pw_gid, pws->pw_gid)) || | ||
1223 | (0 != setreuid (pws->pw_uid, pws->pw_uid))) | ||
1224 | { | ||
1225 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1226 | _("Cannot change user/group to `%s': %s\n"), user, | ||
1227 | STRERROR (errno)); | ||
1228 | GNUNET_free (user); | ||
1229 | return GNUNET_SYSERR; | ||
1230 | } | ||
1231 | } | ||
1232 | #endif | ||
1233 | GNUNET_free (user); | ||
1234 | return GNUNET_OK; | ||
1235 | } | ||
1236 | |||
1237 | |||
1238 | /** | ||
1239 | * Delete the PID file that was created by our parent. | ||
1240 | */ | ||
1241 | static void | ||
1242 | pid_file_delete (struct GNUNET_SERVICE_Context *sctx) | ||
1243 | { | ||
1244 | char *pif = get_pid_file_name (sctx); | ||
1245 | if (pif == NULL) | ||
1246 | return; /* no PID file */ | ||
1247 | if (0 != UNLINK (pif)) | ||
1248 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", pif); | ||
1249 | GNUNET_free (pif); | ||
1250 | } | ||
1251 | |||
1252 | /** | ||
1253 | * Run a standard GNUnet service startup sequence (initialize loggers | ||
1254 | * and configuration, parse options). | ||
1255 | * | ||
1256 | * @param argc number of command line arguments | ||
1257 | * @param argv command line arguments | ||
1258 | * @param serviceName our service name | ||
1259 | * @param task main task of the service | ||
1260 | * @param task_cls closure for task | ||
1261 | * @param term termination task of the service | ||
1262 | * @param term_cls closure for term | ||
1263 | * @return GNUNET_SYSERR on error, GNUNET_OK | ||
1264 | * if we shutdown nicely | ||
1265 | */ | ||
1266 | int | ||
1267 | GNUNET_SERVICE_run (int argc, | ||
1268 | char *const *argv, | ||
1269 | const char *serviceName, | ||
1270 | GNUNET_SERVICE_Main task, | ||
1271 | void *task_cls, GNUNET_SERVICE_Term term, void *term_cls) | ||
1272 | { | ||
1273 | char *cfg_fn; | ||
1274 | char *loglev; | ||
1275 | char *logfile; | ||
1276 | int do_daemonize; | ||
1277 | struct GNUNET_SERVICE_Context sctx; | ||
1278 | struct GNUNET_GETOPT_CommandLineOption service_options[] = { | ||
1279 | GNUNET_GETOPT_OPTION_CFG_FILE (&cfg_fn), | ||
1280 | {'d', "daemonize", NULL, | ||
1281 | gettext_noop ("do daemonize (detach from terminal)"), 0, | ||
1282 | GNUNET_GETOPT_set_one, &do_daemonize}, | ||
1283 | GNUNET_GETOPT_OPTION_HELP (serviceName), | ||
1284 | GNUNET_GETOPT_OPTION_LOGLEVEL (&loglev), | ||
1285 | GNUNET_GETOPT_OPTION_LOGFILE (&logfile), | ||
1286 | GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION), | ||
1287 | GNUNET_GETOPT_OPTION_END | ||
1288 | }; | ||
1289 | do_daemonize = 0; | ||
1290 | logfile = NULL; | ||
1291 | loglev = GNUNET_strdup ("WARNING"); | ||
1292 | cfg_fn = GNUNET_strdup (GNUNET_DEFAULT_DAEMON_CONFIG_FILE); | ||
1293 | memset (&sctx, 0, sizeof (sctx)); | ||
1294 | sctx.ready_confirm_fd = -1; | ||
1295 | sctx.ret = GNUNET_OK; | ||
1296 | sctx.timeout = GNUNET_TIME_UNIT_FOREVER_REL; | ||
1297 | sctx.maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE; | ||
1298 | sctx.task = task; | ||
1299 | sctx.serviceName = serviceName; | ||
1300 | sctx.cfg = GNUNET_CONFIGURATION_create (); | ||
1301 | /* setup subsystems */ | ||
1302 | if ((GNUNET_SYSERR == | ||
1303 | GNUNET_GETOPT_run (serviceName, | ||
1304 | sctx.cfg, | ||
1305 | service_options, | ||
1306 | argc, | ||
1307 | argv)) || | ||
1308 | (GNUNET_OK != | ||
1309 | GNUNET_log_setup (serviceName, loglev, logfile)) || | ||
1310 | (GNUNET_OK != | ||
1311 | GNUNET_CONFIGURATION_load (sctx.cfg, cfg_fn)) || | ||
1312 | (GNUNET_OK != | ||
1313 | setup_service (&sctx)) || | ||
1314 | ((do_daemonize == 1) && | ||
1315 | (GNUNET_OK != detach_terminal (&sctx))) || | ||
1316 | (GNUNET_OK != set_user_id (&sctx))) | ||
1317 | { | ||
1318 | if (sctx.ready_confirm_fd != -1) | ||
1319 | { | ||
1320 | if (1 != WRITE (sctx.ready_confirm_fd, "I", 1)) | ||
1321 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write"); | ||
1322 | GNUNET_break (0 == CLOSE (sctx.ready_confirm_fd)); | ||
1323 | } | ||
1324 | GNUNET_CONFIGURATION_destroy (sctx.cfg); | ||
1325 | GNUNET_free_non_null (sctx.addr); | ||
1326 | GNUNET_free_non_null (logfile); | ||
1327 | GNUNET_free (loglev); | ||
1328 | GNUNET_free (cfg_fn); | ||
1329 | GNUNET_free_non_null (sctx.v4_denied); | ||
1330 | GNUNET_free_non_null (sctx.v6_denied); | ||
1331 | GNUNET_free_non_null (sctx.v4_allowed); | ||
1332 | GNUNET_free_non_null (sctx.v6_allowed); | ||
1333 | return GNUNET_SYSERR; | ||
1334 | } | ||
1335 | |||
1336 | /* actually run service */ | ||
1337 | GNUNET_SCHEDULER_run (&service_task, &sctx); | ||
1338 | if (sctx.ready_confirm_fd != -1) | ||
1339 | { | ||
1340 | if (1 != WRITE (sctx.ready_confirm_fd, "S", 1)) | ||
1341 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write"); | ||
1342 | GNUNET_break (0 == CLOSE (sctx.ready_confirm_fd)); | ||
1343 | } | ||
1344 | |||
1345 | /* shutdown */ | ||
1346 | if (term != NULL) | ||
1347 | term (term_cls, sctx.cfg); | ||
1348 | if ((do_daemonize == 1) && (sctx.server != NULL)) | ||
1349 | pid_file_delete (&sctx); | ||
1350 | if (sctx.server != NULL) | ||
1351 | GNUNET_SERVER_destroy (sctx.server); | ||
1352 | GNUNET_free_non_null (sctx.my_handlers); | ||
1353 | GNUNET_CONFIGURATION_destroy (sctx.cfg); | ||
1354 | GNUNET_free_non_null (sctx.addr); | ||
1355 | GNUNET_free_non_null (logfile); | ||
1356 | GNUNET_free (loglev); | ||
1357 | GNUNET_free (cfg_fn); | ||
1358 | GNUNET_free_non_null (sctx.v4_denied); | ||
1359 | GNUNET_free_non_null (sctx.v6_denied); | ||
1360 | GNUNET_free_non_null (sctx.v4_allowed); | ||
1361 | GNUNET_free_non_null (sctx.v6_allowed); | ||
1362 | return sctx.ret; | ||
1363 | } | ||
1364 | |||
1365 | |||
1366 | /** | ||
1367 | * Run a service startup sequence within an existing | ||
1368 | * initialized system. | ||
1369 | * | ||
1370 | * @param serviceName our service name | ||
1371 | * @param sched scheduler to use | ||
1372 | * @param cfg configuration to use | ||
1373 | * @return NULL on error, service handle | ||
1374 | */ | ||
1375 | struct GNUNET_SERVICE_Context * | ||
1376 | GNUNET_SERVICE_start (const char *serviceName, | ||
1377 | struct GNUNET_SCHEDULER_Handle *sched, | ||
1378 | struct GNUNET_CONFIGURATION_Handle *cfg) | ||
1379 | { | ||
1380 | int i; | ||
1381 | struct GNUNET_SERVICE_Context *sctx; | ||
1382 | |||
1383 | sctx = GNUNET_malloc (sizeof (struct GNUNET_SERVICE_Context)); | ||
1384 | sctx->ready_confirm_fd = -1; /* no daemonizing */ | ||
1385 | sctx->ret = GNUNET_OK; | ||
1386 | sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL; | ||
1387 | sctx->maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE; | ||
1388 | sctx->serviceName = serviceName; | ||
1389 | sctx->cfg = cfg; | ||
1390 | sctx->sched = sched; | ||
1391 | |||
1392 | /* setup subsystems */ | ||
1393 | if ((GNUNET_OK != setup_service (sctx)) || | ||
1394 | (NULL == (sctx->server = GNUNET_SERVER_create (sched, | ||
1395 | &check_access, | ||
1396 | sctx, | ||
1397 | sctx->addr, | ||
1398 | sctx->addrlen, | ||
1399 | sctx->maxbuf, | ||
1400 | sctx->timeout, | ||
1401 | sctx->require_found)))) | ||
1402 | { | ||
1403 | GNUNET_SERVICE_stop (sctx); | ||
1404 | return NULL; | ||
1405 | } | ||
1406 | sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers)); | ||
1407 | memcpy (sctx->my_handlers, defhandlers, sizeof (defhandlers)); | ||
1408 | i = 0; | ||
1409 | while ((sctx->my_handlers[i].callback != NULL)) | ||
1410 | sctx->my_handlers[i++].callback_cls = sctx; | ||
1411 | GNUNET_SERVER_add_handlers (sctx->server, sctx->my_handlers); | ||
1412 | |||
1413 | |||
1414 | return sctx; | ||
1415 | } | ||
1416 | |||
1417 | /** | ||
1418 | * Obtain the server used by a service. Note that the server must NOT | ||
1419 | * be destroyed by the caller. | ||
1420 | * | ||
1421 | * @param ctx the service context returned from the start function | ||
1422 | * @return handle to the server for this service, NULL if there is none | ||
1423 | */ | ||
1424 | struct GNUNET_SERVER_Handle * | ||
1425 | GNUNET_SERVICE_get_server (struct GNUNET_SERVICE_Context *ctx) | ||
1426 | { | ||
1427 | return ctx->server; | ||
1428 | } | ||
1429 | |||
1430 | |||
1431 | /** | ||
1432 | * Stop a service that was started with "GNUNET_SERVICE_start". | ||
1433 | * | ||
1434 | * @param ctx the service context returned from the start function | ||
1435 | */ | ||
1436 | void | ||
1437 | GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Context *sctx) | ||
1438 | { | ||
1439 | if (NULL != sctx->server) | ||
1440 | GNUNET_SERVER_destroy (sctx->server); | ||
1441 | GNUNET_free_non_null (sctx->my_handlers); | ||
1442 | GNUNET_free_non_null (sctx->addr); | ||
1443 | GNUNET_free_non_null (sctx->v4_denied); | ||
1444 | GNUNET_free_non_null (sctx->v6_denied); | ||
1445 | GNUNET_free_non_null (sctx->v4_allowed); | ||
1446 | GNUNET_free_non_null (sctx->v6_allowed); | ||
1447 | GNUNET_free (sctx); | ||
1448 | } | ||
1449 | |||
1450 | |||
1451 | /* end of service.c */ | ||