2 This file is part of GNUnet
3 (C) 2013 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file set/gnunet-service-set.c
23 * @brief two-peer set operations
24 * @author Florian Dold
26 #include "gnunet-service-set.h"
27 #include "set_protocol.h"
31 * Configuration of our local peer.
33 const struct GNUNET_CONFIGURATION_Handle *configuration;
36 * Socket listening for other peers via stream.
38 static struct GNUNET_STREAM_ListenSocket *stream_listen_socket;
41 * Sets are held in a doubly linked list.
43 static struct Set *sets_head;
46 * Sets are held in a doubly linked list.
48 static struct Set *sets_tail;
51 * Listeners are held in a doubly linked list.
53 static struct Listener *listeners_head;
56 * Listeners are held in a doubly linked list.
58 static struct Listener *listeners_tail;
61 * Incoming sockets from remote peers are
62 * held in a doubly linked list.
64 static struct Incoming *incoming_head;
67 * Incoming sockets from remote peers are
68 * held in a doubly linked list.
70 static struct Incoming *incoming_tail;
73 * Counter for allocating unique IDs for clients,
74 * used to identify incoming operation requests from remote peers,
75 * that the client can choose to accept or refuse.
77 static uint32_t accept_id = 1;
81 * Get set that is owned by the client, if any.
83 * @param client client to look for
84 * @return set that the client owns, NULL if the client
88 get_set (struct GNUNET_SERVER_Client *client)
91 for (set = sets_head; NULL != set; set = set->next)
92 if (set->client == client)
99 * Get the listener associated to a client, if any.
101 * @param client the client
102 * @return listener associated with the client, NULL
105 static struct Listener *
106 get_listener (struct GNUNET_SERVER_Client *client)
108 struct Listener *listener;
109 for (listener = listeners_head; NULL != listener; listener = listener->next)
110 if (listener->client == client)
117 * Get the incoming socket associated with the given id.
119 * @param id id to look for
120 * @return the incoming socket associated with the id,
121 * or NULL if there is none
123 static struct Incoming *
124 get_incoming (uint32_t id)
126 struct Incoming *incoming;
127 for (incoming = incoming_head; NULL != incoming; incoming = incoming->next)
128 if (incoming->accept_id == id)
135 * Destroy a listener, free all resources associated with it.
137 * @param listener listener to destroy
140 destroy_listener (struct Listener *listener)
142 if (NULL != listener->client_mq)
144 GNUNET_MQ_destroy (listener->client_mq);
145 listener->client_mq = NULL;
147 GNUNET_CONTAINER_DLL_remove (listeners_head, listeners_tail, listener);
148 GNUNET_free (listener);
153 * Destroy a set, and free all resources associated with it.
155 * @param set the set to destroy
158 destroy_set (struct Set *set)
160 switch (set->operation)
162 case GNUNET_SET_OPERATION_INTERSECTION:
165 case GNUNET_SET_OPERATION_UNION:
166 _GSS_union_set_destroy (set);
172 GNUNET_CONTAINER_DLL_remove (sets_head, sets_tail, set);
178 * Clean up after a client after it is
179 * disconnected (either by us or by itself)
181 * @param cls closure, unused
182 * @param client the client to clean up after
185 handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
188 struct Listener *listener;
190 set = get_set (client);
193 listener = get_listener (client);
194 if (NULL != listener)
195 destroy_listener (listener);
200 * Destroy an incoming request from a remote peer
202 * @param incoming remote request to destroy
205 destroy_incoming (struct Incoming *incoming)
207 if (NULL != incoming->mq)
209 GNUNET_MQ_destroy (incoming->mq);
212 if (NULL != incoming->socket)
214 GNUNET_STREAM_close (incoming->socket);
215 incoming->socket = NULL;
217 GNUNET_CONTAINER_DLL_remove (incoming_head, incoming_tail, incoming);
218 GNUNET_free (incoming);
222 static struct Listener *
223 get_listener_by_target (enum GNUNET_SET_OperationType op,
224 const struct GNUNET_HashCode *app_id)
228 for (l = listeners_head; NULL != l; l = l->next)
230 if (l->operation != op)
232 if (0 != GNUNET_CRYPTO_hash_cmp (app_id, &l->app_id))
241 * Handle a request for a set operation from
244 * @param cls the incoming socket
245 * @param mh the message
248 handle_p2p_operation_request (void *cls, const struct GNUNET_MessageHeader *mh)
250 struct Incoming *incoming = cls;
251 const struct OperationRequestMessage *msg = (const struct OperationRequestMessage *) mh;
252 struct GNUNET_MQ_Message *mqm;
253 struct GNUNET_SET_RequestMessage *cmsg;
254 struct Listener *listener;
255 const struct GNUNET_MessageHeader *context_msg;
257 context_msg = GNUNET_MQ_extract_nested_mh (msg);
258 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "received P2P operation request (op %u, app %s)\n",
259 ntohs (msg->operation), GNUNET_h2s (&msg->app_id));
260 listener = get_listener_by_target (ntohs (msg->operation), &msg->app_id);
261 if (NULL == listener)
263 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
264 "set operation request from peer failed: "
265 "no set with matching application ID and operation type\n");
268 mqm = GNUNET_MQ_msg_nested_mh (cmsg, GNUNET_MESSAGE_TYPE_SET_REQUEST, context_msg);
271 /* FIXME: disconnect the peer */
275 incoming->accept_id = accept_id++;
276 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "sending request with accept id %u\n", incoming->accept_id);
277 cmsg->accept_id = htonl (incoming->accept_id);
278 cmsg->peer_id = incoming->peer;
279 GNUNET_MQ_send (listener->client_mq, mqm);
284 * Called when a client wants to create a new set.
287 * @param client client that sent the message
288 * @param m message sent by the client
291 handle_client_create (void *cls,
292 struct GNUNET_SERVER_Client *client,
293 const struct GNUNET_MessageHeader *m)
295 struct GNUNET_SET_CreateMessage *msg = (struct GNUNET_SET_CreateMessage *) m;
298 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "client created new set (operation %u)\n",
299 ntohs (msg->operation));
301 if (NULL != get_set (client))
304 GNUNET_SERVER_client_disconnect (client);
310 switch (ntohs (msg->operation))
312 case GNUNET_SET_OPERATION_INTERSECTION:
316 case GNUNET_SET_OPERATION_UNION:
317 set = _GSS_union_set_create ();
321 GNUNET_SERVER_client_disconnect (client);
325 GNUNET_assert (NULL != set);
327 set->client = client;
328 set->client_mq = GNUNET_MQ_queue_for_server_client (client);
329 GNUNET_CONTAINER_DLL_insert (sets_head, sets_tail, set);
330 GNUNET_SERVER_receive_done (client, GNUNET_OK);
335 * Called when a client wants to create a new listener.
338 * @param client client that sent the message
339 * @param m message sent by the client
342 handle_client_listen (void *cls,
343 struct GNUNET_SERVER_Client *client,
344 const struct GNUNET_MessageHeader *m)
346 struct GNUNET_SET_ListenMessage *msg = (struct GNUNET_SET_ListenMessage *) m;
347 struct Listener *listener;
349 if (NULL != get_listener (client))
352 GNUNET_SERVER_client_disconnect (client);
355 listener = GNUNET_new (struct Listener);
356 listener->client = client;
357 listener->client_mq = GNUNET_MQ_queue_for_server_client (client);
358 listener->app_id = msg->app_id;
359 listener->operation = ntohs (msg->operation);
360 GNUNET_CONTAINER_DLL_insert_tail (listeners_head, listeners_tail, listener);
361 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "new listener created (op %u, app %s)\n",
362 listener->operation, GNUNET_h2s (&listener->app_id));
363 GNUNET_SERVER_receive_done (client, GNUNET_OK);
368 * Called when a client wants to remove an element
369 * from the set it inhabits.
372 * @param client client that sent the message
373 * @param m message sent by the client
376 handle_client_remove (void *cls,
377 struct GNUNET_SERVER_Client *client,
378 const struct GNUNET_MessageHeader *m)
382 set = get_set (client);
386 GNUNET_SERVER_client_disconnect (client);
389 switch (set->operation)
391 case GNUNET_SET_OPERATION_UNION:
392 _GSS_union_remove ((struct GNUNET_SET_ElementMessage *) m, set);
393 case GNUNET_SET_OPERATION_INTERSECTION:
401 GNUNET_SERVER_receive_done (client, GNUNET_OK);
407 * Called when the client wants to reject an operation
408 * request from another peer.
411 * @param client client that sent the message
412 * @param m message sent by the client
415 handle_client_reject (void *cls,
416 struct GNUNET_SERVER_Client *client,
417 const struct GNUNET_MessageHeader *m)
419 struct Incoming *incoming;
420 struct GNUNET_SET_AcceptRejectMessage *msg = (struct GNUNET_SET_AcceptRejectMessage *) m;
422 GNUNET_break (0 == ntohl (msg->request_id));
424 incoming = get_incoming (ntohl (msg->accept_reject_id));
425 if (NULL == incoming)
427 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
430 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "peer request rejected by client\n");
431 destroy_incoming (incoming);
432 GNUNET_SERVER_receive_done (client, GNUNET_OK);
438 * Called when a client wants to add an element to a
442 * @param client client that sent the message
443 * @param m message sent by the client
446 handle_client_add (void *cls,
447 struct GNUNET_SERVER_Client *client,
448 const struct GNUNET_MessageHeader *m)
452 set = get_set (client);
456 GNUNET_SERVER_client_disconnect (client);
459 switch (set->operation)
461 case GNUNET_SET_OPERATION_UNION:
462 _GSS_union_add ((struct GNUNET_SET_ElementMessage *) m, set);
463 case GNUNET_SET_OPERATION_INTERSECTION:
471 GNUNET_SERVER_receive_done (client, GNUNET_OK);
476 * Called when a client wants to evaluate a set operation with another peer.
479 * @param client client that sent the message
480 * @param m message sent by the client
483 handle_client_evaluate (void *cls,
484 struct GNUNET_SERVER_Client *client,
485 const struct GNUNET_MessageHeader *m)
489 set = get_set (client);
493 GNUNET_SERVER_client_disconnect (client);
498 switch (set->operation)
500 case GNUNET_SET_OPERATION_INTERSECTION:
503 case GNUNET_SET_OPERATION_UNION:
504 _GSS_union_evaluate ((struct GNUNET_SET_EvaluateMessage *) m, set);
511 GNUNET_SERVER_receive_done (client, GNUNET_OK);
516 * Handle an ack from a client.
519 * @param client the client
520 * @param m the message
523 handle_client_ack (void *cls,
524 struct GNUNET_SERVER_Client *client,
525 const struct GNUNET_MessageHeader *m)
527 /* FIXME: implement */
528 GNUNET_SERVER_receive_done (client, GNUNET_OK);
533 * Handle a request from the client to accept
534 * a set operation that came from a remote peer.
537 * @param client the client
538 * @param mh the message
541 handle_client_accept (void *cls,
542 struct GNUNET_SERVER_Client *client,
543 const struct GNUNET_MessageHeader *mh)
546 struct Incoming *incoming;
547 struct GNUNET_SET_AcceptRejectMessage *msg = (struct GNUNET_SET_AcceptRejectMessage *) mh;
549 incoming = get_incoming (ntohl (msg->accept_reject_id));
551 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "client accepting %u\n", ntohl (msg->accept_reject_id));
553 if (NULL == incoming)
557 GNUNET_SERVER_client_disconnect (client);
562 set = get_set (client);
567 GNUNET_SERVER_client_disconnect (client);
571 switch (set->operation)
573 case GNUNET_SET_OPERATION_INTERSECTION:
577 case GNUNET_SET_OPERATION_UNION:
578 _GSS_union_accept (msg, set, incoming);
585 /* note: _GSS_*_accept has to make sure the socket and mq are set to NULL,
586 * otherwise they will be destroyed and disconnected */
587 destroy_incoming (incoming);
588 GNUNET_SERVER_receive_done (client, GNUNET_OK);
593 * Functions of this type are called upon new stream connection from other peers
594 * or upon binding error which happen when the app_port given in
595 * GNUNET_STREAM_listen() is already taken.
597 * @param cls the closure from GNUNET_STREAM_listen
598 * @param socket the socket representing the stream; NULL on binding error
599 * @param initiator the identity of the peer who wants to establish a stream
600 * with us; NULL on binding error
601 * @return GNUNET_OK to keep the socket open, GNUNET_SYSERR to close the
602 * stream (the socket will be invalid after the call)
605 stream_listen_cb (void *cls,
606 struct GNUNET_STREAM_Socket *socket,
607 const struct GNUNET_PeerIdentity *initiator)
609 struct Incoming *incoming;
610 static const struct GNUNET_MQ_Handler handlers[] = {
611 {handle_p2p_operation_request, GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST},
612 GNUNET_MQ_HANDLERS_END
618 return GNUNET_SYSERR;
621 incoming = GNUNET_new (struct Incoming);
622 incoming->peer = *initiator;
623 incoming->socket = socket;
624 incoming->mq = GNUNET_STREAM_mq_create (incoming->socket, handlers, NULL, incoming);
625 /* FIXME: timeout for peers that only connect but don't send anything */
626 GNUNET_CONTAINER_DLL_insert_tail (incoming_head, incoming_tail, incoming);
632 * Called to clean up, after a shutdown has been requested.
635 * @param tc context information (why was this task triggered now)
638 shutdown_task (void *cls,
639 const struct GNUNET_SCHEDULER_TaskContext *tc)
641 if (NULL != stream_listen_socket)
643 GNUNET_STREAM_listen_close (stream_listen_socket);
644 stream_listen_socket = NULL;
647 while (NULL != incoming_head)
649 destroy_incoming (incoming_head);
652 while (NULL != listeners_head)
654 destroy_listener (listeners_head);
657 while (NULL != sets_head)
659 destroy_set (sets_head);
662 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "handled shutdown request\n");
667 * Function called by the service's run
668 * method to run service-specific setup code.
671 * @param server the initialized server
672 * @param cfg configuration to use
675 run (void *cls, struct GNUNET_SERVER_Handle *server,
676 const struct GNUNET_CONFIGURATION_Handle *cfg)
678 static const struct GNUNET_SERVER_MessageHandler server_handlers[] = {
679 {handle_client_accept, NULL, GNUNET_MESSAGE_TYPE_SET_ACCEPT, 0},
680 {handle_client_ack, NULL, GNUNET_MESSAGE_TYPE_SET_ACK, 0},
681 {handle_client_add, NULL, GNUNET_MESSAGE_TYPE_SET_ADD, 0},
682 {handle_client_create, NULL, GNUNET_MESSAGE_TYPE_SET_CREATE, 0},
683 {handle_client_evaluate, NULL, GNUNET_MESSAGE_TYPE_SET_EVALUATE, 0},
684 {handle_client_listen, NULL, GNUNET_MESSAGE_TYPE_SET_LISTEN, 0},
685 {handle_client_reject, NULL, GNUNET_MESSAGE_TYPE_SET_REJECT, 0},
686 {handle_client_remove, NULL, GNUNET_MESSAGE_TYPE_SET_REMOVE, 0},
691 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task, NULL);
692 GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
693 GNUNET_SERVER_add_handlers (server, server_handlers);
694 stream_listen_socket = GNUNET_STREAM_listen (cfg, GNUNET_APPLICATION_TYPE_SET,
695 &stream_listen_cb, NULL,
696 GNUNET_STREAM_OPTION_END);
698 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set service running\n");
703 * The main function for the set service.
705 * @param argc number of arguments from the command line
706 * @param argv command line arguments
707 * @return 0 ok, 1 on error
710 main (int argc, char *const *argv)
713 ret = GNUNET_SERVICE_run (argc, argv, "set", GNUNET_SERVICE_OPTION_NONE, &run, NULL);
714 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "exit\n");
715 return (GNUNET_OK == ret) ? 0 : 1;