-starting with service_new accept logic
[oweals/gnunet.git] / src / util / service_new.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2016 GNUnet e.V.
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file util/service_new.c
23  * @brief functions related to starting services (redesign)
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_protocols.h"
29 #include "gnunet_constants.h"
30 #include "gnunet_resolver_service.h"
31
32
33 /**
34  * Information the service tracks per listen operation.
35  */
36 struct ServiceListenContext
37 {
38
39   /**
40    * Kept in a DLL.
41    */
42   struct ServiceListenContext *next;
43
44   /**
45    * Kept in a DLL.
46    */
47   struct ServiceListenContext *prev;
48
49   /**
50    * Service this listen context belongs to.
51    */
52   struct GNUNET_SERVICE_Handle *sh;
53
54   /**
55    * Socket we are listening on.
56    */
57   struct GNUNET_NETWORK_Handle *listen_socket;
58
59   /**
60    * Task scheduled to do the listening.
61    */
62   struct GNUNET_SCHEDULER_Task *listen_task;
63
64 };
65
66
67 /**
68  * Handle to a service.
69  */
70 struct GNUNET_SERVICE_Handle
71 {
72   /**
73    * Our configuration.
74    */
75   const struct GNUNET_CONFIGURATION_Handle *cfg;
76
77   /**
78    * Name of our service.
79    */
80   const char *service_name;
81
82   /**
83    * Main service-specific task to run.
84    */
85   GNUNET_SERVICE_InitCallback service_init_cb;
86
87   /**
88    * Function to call when clients connect.
89    */
90   GNUNET_SERVICE_ConnectHandler connect_cb;
91
92   /**
93    * Function to call when clients disconnect / are disconnected.
94    */
95   GNUNET_SERVICE_DisconnectHandler disconnect_cb;
96
97   /**
98    * Closure for @e service_init_cb, @e connect_cb, @e disconnect_cb.
99    */
100   void *cb_cls;
101
102   /**
103    * DLL of listen sockets used to accept new connections.
104    */
105   struct ServiceListenContext *slc_head;
106
107   /**
108    * DLL of listen sockets used to accept new connections.
109    */
110   struct ServiceListenContext *slc_tail;
111
112   /**
113    * Our clients, kept in a DLL.
114    */
115   struct GNUNET_SERVICE_Client *clients_head;
116
117   /**
118    * Our clients, kept in a DLL.
119    */
120   struct GNUNET_SERVICE_Client *clients_tail;
121
122   /**
123    * Message handlers to use for all clients.
124    */
125   const struct GNUNET_MQ_MessageHandler *handlers;
126
127   /**
128    * Closure for @e task.
129    */
130   void *task_cls;
131
132   /**
133    * IPv4 addresses that are not allowed to connect.
134    */
135   struct GNUNET_STRINGS_IPv4NetworkPolicy *v4_denied;
136
137   /**
138    * IPv6 addresses that are not allowed to connect.
139    */
140   struct GNUNET_STRINGS_IPv6NetworkPolicy *v6_denied;
141
142   /**
143    * IPv4 addresses that are allowed to connect (if not
144    * set, all are allowed).
145    */
146   struct GNUNET_STRINGS_IPv4NetworkPolicy *v4_allowed;
147
148   /**
149    * IPv6 addresses that are allowed to connect (if not
150    * set, all are allowed).
151    */
152   struct GNUNET_STRINGS_IPv6NetworkPolicy *v6_allowed;
153
154   /**
155    * Do we require a matching UID for UNIX domain socket connections?
156    * #GNUNET_NO means that the UID does not have to match (however,
157    * @e match_gid may still impose other access control checks).
158    */
159   int match_uid;
160
161   /**
162    * Do we require a matching GID for UNIX domain socket connections?
163    * Ignored if @e match_uid is #GNUNET_YES.  Note that this is about
164    * checking that the client's UID is in our group OR that the
165    * client's GID is our GID.  If both "match_gid" and @e match_uid are
166    * #GNUNET_NO, all users on the local system have access.
167    */
168   int match_gid;
169
170   /**
171    * Set to #GNUNET_YES if we got a shutdown signal and terminate
172    * the service if #have_non_monitor_clients() returns #GNUNET_YES.
173    */
174   int got_shutdown;
175
176   /**
177    * Our options.
178    */
179   enum GNUNET_SERVICE_Options options;
180
181 };
182
183
184 /**
185  * Handle to a client that is connected to a service.
186  */
187 struct GNUNET_SERVICE_Client
188 {
189
190   /**
191    * Kept in a DLL.
192    */
193   struct GNUNET_SERVICE_Client *next;
194
195   /**
196    * Kept in a DLL.
197    */
198   struct GNUNET_SERVICE_Client *prev;
199
200   /**
201    * Server that this client belongs to.
202    */
203   struct GNUNET_SERVER_Handle *sh;
204
205   /**
206    * Socket of this client.
207    */
208   struct GNUNET_NETWORK_Handle *sock;
209
210   /**
211    * Message queue for the client.
212    */
213   struct GNUNET_MQ_Handle *mq;
214
215   /**
216    * Task that warns about missing calls to
217    * #GNUNET_SERVICE_client_continue().
218    */
219   struct GNUNET_SCHEDULER_Task *warn_task;
220
221   /**
222    * User context value, value returned from
223    * the connect callback.
224    */
225   void *user_context;
226
227   /**
228    * Persist the file handle for this client no matter what happens,
229    * force the OS to close once the process actually dies.  Should only
230    * be used in special cases!
231    */
232   int persist;
233
234   /**
235    * Is this client a 'monitor' client that should not be counted
236    * when deciding on destroying the server during soft shutdown?
237    * (see also #GNUNET_SERVICE_start)
238    */
239   int is_monitor;
240
241   /**
242    * Type of last message processed (for warn_no_receive_done).
243    */
244   uint16_t warn_type;
245 };
246
247
248 /**
249  * Check if any of the clients we have left are unrelated to
250  * monitoring.
251  *
252  * @param sh service to check clients for
253  * @return #GNUNET_YES if we have non-monitoring clients left
254  */
255 static int
256 have_non_monitor_clients (struct GNUNET_SERVICE_Handle *sh)
257 {
258   struct GNUNET_SERVICE_Client *client;
259
260   for (client = sh->clients_head;NULL != client; client = client->next)
261   {
262     if (client->is_monitor)
263       continue;
264     return GNUNET_YES;
265   }
266   return GNUNET_NO;
267 }
268
269
270 /**
271  * Shutdown task triggered when a service should be terminated.
272  * This considers active clients and the service options to see
273  * how this specific service is to be terminated, and depending
274  * on this proceeds with the shutdown logic.
275  *
276  * @param cls our `struct GNUNET_SERVICE_Handle`
277  */
278 static void
279 service_main (void *cls)
280 {
281   struct GNUNET_SERVICE_Handle *sh = cls;
282   struct GNUNET_SERVICE_Client *client;
283   int alive;
284
285   switch (sh->options)
286   {
287   case GNUNET_SERVICE_OPTION_NONE:
288     GNUNET_SERVICE_shutdown (sh);
289     break;
290   case GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN:
291     /* This task should never be run if we are using
292        the manual shutdown. */
293     GNUNET_assert (0);
294     break;
295   case GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN:
296     sh->got_shutdown = GNUNET_YES;
297     GNUNET_SERVICE_suspend (sh);
298     if (GNUNET_NO == have_non_monitor_clients (sh))
299       GNUNET_SERVICE_shutdown (sh);
300     break;
301   }
302 }
303
304
305 /**
306  * First task run by any service.  Initializes our shutdown task,
307  * starts the listening operation on our listen sockets and launches
308  * the custom logic of the application service.
309  *
310  * @param cls our `struct GNUNET_SERVICE_Handle`
311  */
312 static void
313 service_main (void *cls)
314 {
315   struct GNUNET_SERVICE_Handle *sh = cls;
316
317   if (GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN != sh->options)
318     GNUNET_SCHEDULER_add_shutdown (&service_shutdown,
319                                    sh);
320   GNUNET_SERVICE_resume (sh);
321   sh->service_init_cb (sh->cb_cls,
322                        sh->cfg,
323                        sh);
324 }
325
326
327 /**
328  * Creates the "main" function for a GNUnet service.  You
329  * should almost always use the #GNUNET_SERVICE_MAIN macro
330  * instead of calling this function directly (except
331  * for ARM, which should call this function directly).
332  *
333  * The function will launch the service with the name @a service_name
334  * using the @a service_options to configure its shutdown
335  * behavior. Once the service is ready, the @a init_cb will be called
336  * for service-specific initialization.  @a init_cb will be given the
337  * service handler which can be used to control the service's
338  * availability.  When clients connect or disconnect, the respective
339  * @a connect_cb or @a disconnect_cb functions will be called. For
340  * messages received from the clients, the respective @a handlers will
341  * be invoked; for the closure of the handlers we use the return value
342  * from the @a connect_cb invocation of the respective client.
343  *
344  * Each handler MUST call #GNUNET_SERVICE_client_continue() after each
345  * message to receive further messages from this client.  If
346  * #GNUNET_SERVICE_client_continue() is not called within a short
347  * time, a warning will be logged. If delays are expected, services
348  * should call #GNUNET_SERVICE_client_disable_continue_warning() to
349  * disable the warning.
350  *
351  * Clients sending invalid messages (based on @a handlers) will be
352  * dropped. Additionally, clients can be dropped at any time using
353  * #GNUNET_SERVICE_client_drop().
354  *
355  * @param argc number of command-line arguments in @a argv
356  * @param argv array of command-line arguments
357  * @param service_name name of the service to run
358  * @param options options controlling shutdown of the service
359  * @param service_init_cb function to call once the service is ready
360  * @param connect_cb function to call whenever a client connects
361  * @param disconnect_cb function to call whenever a client disconnects
362  * @param cls closure argument for @a service_init_cb, @a connect_cb and @a disconnect_cb
363  * @param handlers NULL-terminated array of message handlers for the service,
364  *                 the closure will be set to the value returned by
365  *                 the @a connect_cb for the respective connection
366  * @return 0 on success, non-zero on error
367  */
368 int
369 GNUNET_SERVICE_ruN_ (int argc,
370                      char *const *argv,
371                      const char *service_name,
372                      enum GNUNET_SERVICE_Options options,
373                      GNUNET_SERVICE_InitCallback service_init_cb,
374                      GNUNET_SERVICE_ConnectHandler connect_cb,
375                      GNUNET_SERVICE_DisconnectHandler disconnect_cb,
376                      void *cls,
377                      const struct GNUNET_MQ_MessageHandler *handlers)
378 {
379   struct GNUNET_SERVICE_Handle sh;
380
381   // FIXME: setup (parse command line, configuration, init sh)
382   GNUNET_SCHEDULER_run (&service_main,
383                         &sh);
384   // FIXME: cleanup
385   return 1;
386 }
387
388
389 /**
390  * Suspend accepting connections from the listen socket temporarily.
391  * Resume activity using #GNUNET_SERVICE_resume.
392  *
393  * @param sh service to stop accepting connections.
394  */
395 void
396 GNUNET_SERVICE_suspend (struct GNUNET_SERVICE_Handle *sh)
397 {
398   struct ServiceListenContext *slc;
399
400   for (slc = slc_head; NULL != slc; slc = slc->next)
401   {
402     if (NULL != slc->listen_task)
403       {
404         GNUNET_SCHEDULER_cancel (slc->listen_task);
405         slc->listen_task = NULL;
406       }
407   }
408 }
409
410
411 /**
412  * We have successfully accepted a connection from a client.  Now
413  * setup the client (with the scheduler) and tell the application.
414  *
415  * @param sh service that accepted the client
416  * @param sock socket associated with the client
417  */
418 static void
419 start_client (struct GNUNET_SERVICE_Handle *sh,
420               struct GNUNET_NETWORK_Handle *csock)
421 {
422   struct GNUNET_SERVICE_Client *client;
423
424   client = GNUNET_new (struct GNUNET_SERVICE_Client);
425   GNUNET_CONTAINER_DLL_insert (sh->clients_head,
426                                sh->clients_tail,
427                                client);
428   client->sh = sh;
429   client->sock = csock;
430   client->mq = NULL; // FIXME!
431   client->user_context = sh->connect_cb (sh->cb_cls,
432                                          client,
433                                          client->mq);
434 }
435
436
437 /**
438  * We have a client. Accept the incoming socket(s) (and reschedule
439  * the listen task).
440  *
441  * @param cls the `struct ServiceListenContext` of the ready listen socket
442  */
443 static void
444 accept_client (void *cls)
445 {
446   struct ServiceListenContext *slc = cls;
447
448   slc->listen_task = NULL;
449   while (1)
450     {
451       struct GNUNET_NETWORK_Handle *sock;
452       struct sockaddr_in *v4;
453       struct sockaddr_in6 *v6;
454       struct sockaddr_storage sa;
455       socklen_t addrlen;
456       int ok;
457
458       addrlen = sizeof (sa);
459       sock = GNUNET_NETWORK_socket_accept (slc->listen_socket,
460                                            (struct sockaddr *) &sa,
461                                            &addrlen);
462       if (NULL == sock)
463         break;
464       switch (sa.sa_family)
465       {
466       case AF_INET:
467         GNUNET_assert (addrlen == sizeof (struct sockaddr_in));
468         v4 = (const struct sockaddr_in *) addr;
469         ok = ( ( (NULL == sh->v4_allowed) ||
470                  (check_ipv4_listed (sh->v4_allowed,
471                                      &i4->sin_addr))) &&
472                ( (NULL == sh->v4_denied) ||
473                  (! check_ipv4_listed (sh->v4_denied,
474                                        &i4->sin_addr)) ) );
475         break;
476       case AF_INET6:
477         GNUNET_assert (addrlen == sizeof (struct sockaddr_in6));
478         v6 = (const struct sockaddr_in6 *) addr;
479         ok = ( ( (NULL == sh->v6_allowed) ||
480                  (check_ipv6_listed (sh->v6_allowed,
481                                      &i6->sin6_addr))) &&
482                ( (NULL == sh->v6_denied) ||
483                  (! check_ipv6_listed (sh->v6_denied,
484                                        &i6->sin6_addr)) ) );
485         break;
486 #ifndef WINDOWS
487       case AF_UNIX:
488         ok = GNUNET_OK;            /* controlled using file-system ACL now */
489         break;
490 #endif
491       default:
492         LOG (GNUNET_ERROR_TYPE_WARNING,
493              _("Unknown address family %d\n"),
494              addr->sa_family);
495         return GNUNET_SYSERR;
496       }
497       if (! ok)
498         {
499           LOG (GNUNET_ERROR_TYPE_DEBUG,
500                "Service rejected incoming connection from %s due to policy.\n",
501                GNUNET_a2s ((const struct sockaddr *) &sa,
502                            addrlen));
503           GNUNET_NETWORK_socket_close (sock);
504           continue;
505         }
506       LOG (GNUNET_ERROR_TYPE_DEBUG,
507            "Service accepted incoming connection from %s.\n",
508            GNUNET_a2s ((const struct sockaddr *) &sa,
509                        addrlen));
510       start_client (slc->sh,
511                     sock);
512     }
513   slc->listen_task = GNUNET_SCHEDULER_add_read (slc->listen_socket,
514                                                 &accept_client,
515                                                 slc);
516 }
517
518
519 /**
520  * Resume accepting connections from the listen socket.
521  *
522  * @param sh service to resume accepting connections.
523  */
524 void
525 GNUNET_SERVICE_resume (struct GNUNET_SERVICE_Handle *sh)
526 {
527   struct ServiceListenContext *slc;
528
529   for (slc = slc_head; NULL != slc; slc = slc->next)
530   {
531     GNUNET_assert (NULL == slc->listen_task);
532     slc->listen_task = GNUNET_SCHEDULER_add_read (slc->listen_socket,
533                                                   &accept_client,
534                                                   slc);
535   }
536 }
537
538
539 /**
540  * Continue receiving further messages from the given client.
541  * Must be called after each message received.
542  *
543  * @param c the client to continue receiving from
544  */
545 void
546 GNUNET_SERVICE_client_continue (struct GNUNET_SERVICE_Client *c)
547 {
548   GNUNET_break (0); // not implemented
549 }
550
551
552 /**
553  * Disable the warning the server issues if a message is not
554  * acknowledged in a timely fashion.  Use this call if a client is
555  * intentionally delayed for a while.  Only applies to the current
556  * message.
557  *
558  * @param c client for which to disable the warning
559  */
560 void
561 GNUNET_SERVICE_client_disable_continue_warning (struct GNUNET_SERVICE_Client *c)
562 {
563   GNUNET_break (NULL != c->warn_task);
564   if (NULL != c->warn_task)
565   {
566     GNUNET_SCHEDULER_cancel (c->warn_task);
567     c->warn_task = NULL;
568   }
569 }
570
571
572 /**
573  * Ask the server to disconnect from the given client.  This is the
574  * same as returning #GNUNET_SYSERR within the check procedure when
575  * handling a message, wexcept that it allows dropping of a client even
576  * when not handling a message from that client.  The `disconnect_cb`
577  * will be called on @a c even if the application closes the connection
578  * using this function.
579  *
580  * @param c client to disconnect now
581  */
582 void
583 GNUNET_SERVICE_client_drop (struct GNUNET_SERVICE_Client *c)
584 {
585   struct GNUNET_SERVICE_Handle *sh = c->sh;
586
587   GNUNET_CONTAINER_DLL_remove (sh->clients_head,
588                                sh->clients_tail,
589                                c);
590   sh->disconnect_cb (sh->cb_cls,
591                      c,
592                      c->user_context);
593   if (NULL != c->warn_task)
594   {
595     GNUNET_SCHEDULER_cancel (c->warn_task);
596     c->warn_task = NULL;
597   }
598   GNUNET_MQ_destroy (c->mq);
599   if (GNUNET_NO == c->persist)
600   {
601     GNUNET_NETWORK_socket_close (c->sock);
602   }
603   else
604   {
605     GNUNET_NETWORK_socket_free_memory_only_ (c->sock);
606   }
607   GNUNET_free (c);
608   if ( (GNUNET_YES == sh->got_shutdown) &&
609        (GNUNET_NO == have_non_monitor_clients (sh)) )
610     GNUNET_SERVICE_shutdown (sh);
611 }
612
613
614 /**
615  * Explicitly stops the service.
616  *
617  * @param sh server to shutdown
618  */
619 void
620 GNUNET_SERVICE_shutdown (struct GNUNET_SERVICE_Handle *sh)
621 {
622   struct GNUNET_SERVICE_Client *client;
623
624   GNUNET_SERVICE_suspend (sh);
625   sh->got_shutdown = GNUNET_NO;
626   while (NULL != (client = sh->clients_head))
627     GNUNET_SERVICE_client_drop (client);
628 }
629
630
631 /**
632  * Set the 'monitor' flag on this client.  Clients which have been
633  * marked as 'monitors' won't prevent the server from shutting down
634  * once #GNUNET_SERVICE_stop_listening() has been invoked.  The idea is
635  * that for "normal" clients we likely want to allow them to process
636  * their requests; however, monitor-clients are likely to 'never'
637  * disconnect during shutdown and thus will not be considered when
638  * determining if the server should continue to exist after
639  * shutdown has been triggered.
640  *
641  * @param c client to mark as a monitor
642  */
643 void
644 GNUNET_SERVICE_client_mark_monitor (struct GNUNET_SERVICE_Client *c)
645 {
646   c->is_monitor = GNUNET_YES;
647   if ( (GNUNET_YES == sh->got_shutdown) &&
648        (GNUNET_NO == have_non_monitor_clients (sh)) )
649     GNUNET_SERVICE_shutdown (sh);
650 }
651
652
653 /**
654  * Set the persist option on this client.  Indicates that the
655  * underlying socket or fd should never really be closed.  Used for
656  * indicating process death.
657  *
658  * @param c client to persist the socket (never to be closed)
659  */
660 void
661 GNUNET_SERVICE_client_persist (struct GNUNET_SERVICE_Client *c)
662 {
663   c->persist = GNUNET_YES;
664 }
665
666
667 /* end of service_new.c */