123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734 |
- /*
- This file is part of GNUnet.
- Copyright (C) 2009-2013 GNUnet e.V.
- GNUnet is free software: you can redistribute it and/or modify it
- under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License,
- or (at your option) any later version.
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- SPDX-License-Identifier: AGPL3.0-or-later
- */
- /**
- * @file util/server.c
- * @brief library for building GNUnet network servers
- * @author Christian Grothoff
- */
- #include "platform.h"
- #include "gnunet_util_lib.h"
- #include "gnunet_protocols.h"
- #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util-server", syscall, filename)
- /**
- * List of arrays of message handlers.
- */
- struct HandlerList
- {
- /**
- * This is a linked list.
- */
- struct HandlerList *next;
- /**
- * NULL-terminated array of handlers.
- */
- const struct GNUNET_SERVER_MessageHandler *handlers;
- };
- /**
- * List of arrays of message handlers.
- */
- struct NotifyList
- {
- /**
- * This is a doubly linked list.
- */
- struct NotifyList *next;
- /**
- * This is a doubly linked list.
- */
- struct NotifyList *prev;
- /**
- * Function to call.
- */
- GNUNET_SERVER_DisconnectCallback callback;
- /**
- * Closure for callback.
- */
- void *callback_cls;
- };
- /**
- * @brief handle for a server
- */
- struct GNUNET_SERVER_Handle
- {
- /**
- * List of handlers for incoming messages.
- */
- struct HandlerList *handlers;
- /**
- * Head of list of our current clients.
- */
- struct GNUNET_SERVER_Client *clients_head;
- /**
- * Head of list of our current clients.
- */
- struct GNUNET_SERVER_Client *clients_tail;
- /**
- * Head of linked list of functions to call on disconnects by clients.
- */
- struct NotifyList *disconnect_notify_list_head;
- /**
- * Tail of linked list of functions to call on disconnects by clients.
- */
- struct NotifyList *disconnect_notify_list_tail;
- /**
- * Head of linked list of functions to call on connects by clients.
- */
- struct NotifyList *connect_notify_list_head;
- /**
- * Tail of linked list of functions to call on connects by clients.
- */
- struct NotifyList *connect_notify_list_tail;
- /**
- * Function to call for access control.
- */
- GNUNET_CONNECTION_AccessCheck access_cb;
- /**
- * Closure for @e access_cb.
- */
- void *access_cb_cls;
- /**
- * NULL-terminated array of sockets used to listen for new
- * connections.
- */
- struct GNUNET_NETWORK_Handle **listen_sockets;
- /**
- * After how long should an idle connection time
- * out (on write).
- */
- struct GNUNET_TIME_Relative idle_timeout;
- /**
- * Task scheduled to do the listening.
- */
- struct GNUNET_SCHEDULER_Task * listen_task;
- /**
- * Alternative function to create a MST instance.
- */
- GNUNET_SERVER_MstCreateCallback mst_create;
- /**
- * Alternative function to destroy a MST instance.
- */
- GNUNET_SERVER_MstDestroyCallback mst_destroy;
- /**
- * Alternative function to give data to a MST instance.
- */
- GNUNET_SERVER_MstReceiveCallback mst_receive;
- /**
- * Closure for 'mst_'-callbacks.
- */
- void *mst_cls;
- /**
- * Do we ignore messages of types that we do not understand or do we
- * require that a handler is found (and if not kill the connection)?
- */
- int require_found;
- /**
- * Set to #GNUNET_YES once we are in 'soft' shutdown where we wait for
- * all non-monitor clients to disconnect before we call
- * #GNUNET_SERVER_destroy. See test_monitor_clients(). Set to
- * #GNUNET_SYSERR once the final destroy task has been scheduled
- * (we cannot run it in the same task).
- */
- int in_soft_shutdown;
- };
- /**
- * Handle server returns for aborting transmission to a client.
- */
- struct GNUNET_SERVER_TransmitHandle
- {
- /**
- * Function to call to get the message.
- */
- GNUNET_CONNECTION_TransmitReadyNotify callback;
- /**
- * Closure for @e callback
- */
- void *callback_cls;
- /**
- * Active connection transmission handle.
- */
- struct GNUNET_CONNECTION_TransmitHandle *cth;
- };
- /**
- * @brief handle for a client of the server
- */
- struct GNUNET_SERVER_Client
- {
- /**
- * This is a doubly linked list.
- */
- struct GNUNET_SERVER_Client *next;
- /**
- * This is a doubly linked list.
- */
- struct GNUNET_SERVER_Client *prev;
- /**
- * Processing of incoming data.
- */
- void *mst;
- /**
- * Server that this client belongs to.
- */
- struct GNUNET_SERVER_Handle *server;
- /**
- * Client closure for callbacks.
- */
- struct GNUNET_CONNECTION_Handle *connection;
- /**
- * User context value, manipulated using
- * 'GNUNET_SERVER_client_{get/set}_user_context' functions.
- */
- void *user_context;
- /**
- * ID of task used to restart processing.
- */
- struct GNUNET_SCHEDULER_Task * restart_task;
- /**
- * Task that warns about missing calls to #GNUNET_SERVER_receive_done.
- */
- struct GNUNET_SCHEDULER_Task * warn_task;
- /**
- * Time when the warn task was started.
- */
- struct GNUNET_TIME_Absolute warn_start;
- /**
- * Last activity on this socket (used to time it out
- * if reference_count == 0).
- */
- struct GNUNET_TIME_Absolute last_activity;
- /**
- * Transmission handle we return for this client from
- * #GNUNET_SERVER_notify_transmit_ready.
- */
- struct GNUNET_SERVER_TransmitHandle th;
- /**
- * After how long should an idle connection time
- * out (on write).
- */
- struct GNUNET_TIME_Relative idle_timeout;
- /**
- * Number of external entities with a reference to
- * this client object.
- */
- unsigned int reference_count;
- /**
- * Was processing if incoming messages suspended while
- * we were still processing data already received?
- * This is a counter saying how often processing was
- * suspended (once per handler invoked).
- */
- unsigned int suspended;
- /**
- * Last size given when user context was initialized; used for
- * sanity check.
- */
- size_t user_context_size;
- /**
- * Are we currently in the "process_client_buffer" function (and
- * will hence restart the receive job on exit if suspended == 0 once
- * we are done?). If this is set, then "receive_done" will
- * essentially only decrement suspended; if this is not set, then
- * "receive_done" may need to restart the receive process (either
- * from the side-buffer or via select/recv).
- */
- int in_process_client_buffer;
- /**
- * We're about to close down this client.
- */
- int shutdown_now;
- /**
- * Are we currently trying to receive? (#GNUNET_YES if we are,
- * #GNUNET_NO if we are not, #GNUNET_SYSERR if data is already
- * available in MST).
- */
- int receive_pending;
- /**
- * Persist the file handle for this client no matter what happens,
- * force the OS to close once the process actually dies. Should only
- * be used in special cases!
- */
- int persist;
- /**
- * Is this client a 'monitor' client that should not be counted
- * when deciding on destroying the server during soft shutdown?
- * (see also #GNUNET_SERVICE_start)
- */
- int is_monitor;
- /**
- * Type of last message processed (for warn_no_receive_done).
- */
- uint16_t warn_type;
- };
- /**
- * Return user context associated with the given client.
- * Note: you should probably use the macro (call without the underscore).
- *
- * @param client client to query
- * @param size number of bytes in user context struct (for verification only)
- * @return pointer to user context
- */
- void *
- GNUNET_SERVER_client_get_user_context_ (struct GNUNET_SERVER_Client *client,
- size_t size)
- {
- if ((0 == client->user_context_size) &&
- (NULL == client->user_context))
- return NULL; /* never set */
- GNUNET_assert (size == client->user_context_size);
- return client->user_context;
- }
- /**
- * Set user context to be associated with the given client.
- * Note: you should probably use the macro (call without the underscore).
- *
- * @param client client to query
- * @param ptr pointer to user context
- * @param size number of bytes in user context struct (for verification only)
- */
- void
- GNUNET_SERVER_client_set_user_context_ (struct GNUNET_SERVER_Client *client,
- void *ptr,
- size_t size)
- {
- if (NULL == ptr)
- {
- client->user_context_size = 0;
- client->user_context = ptr;
- return;
- }
- client->user_context_size = size;
- client->user_context = ptr;
- }
- /**
- * Scheduler says our listen socket is ready. Process it!
- *
- * @param cls handle to our server for which we are processing the listen
- * socket
- */
- static void
- process_listen_socket (void *cls)
- {
- struct GNUNET_SERVER_Handle *server = cls;
- const struct GNUNET_SCHEDULER_TaskContext *tc;
- struct GNUNET_CONNECTION_Handle *sock;
- unsigned int i;
- server->listen_task = NULL;
- tc = GNUNET_SCHEDULER_get_task_context ();
- for (i = 0; NULL != server->listen_sockets[i]; i++)
- {
- if (GNUNET_NETWORK_fdset_isset (tc->read_ready,
- server->listen_sockets[i]))
- {
- sock =
- GNUNET_CONNECTION_create_from_accept (server->access_cb,
- server->access_cb_cls,
- server->listen_sockets[i]);
- if (NULL != sock)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Server accepted incoming connection.\n");
- (void) GNUNET_SERVER_connect_socket (server,
- sock);
- }
- }
- }
- /* listen for more! */
- GNUNET_SERVER_resume (server);
- }
- /**
- * Create and initialize a listen socket for the server.
- *
- * @param server_addr address to listen on
- * @param socklen length of @a server_addr
- * @return NULL on error, otherwise the listen socket
- */
- static struct GNUNET_NETWORK_Handle *
- open_listen_socket (const struct sockaddr *server_addr,
- socklen_t socklen)
- {
- struct GNUNET_NETWORK_Handle *sock;
- uint16_t port;
- int eno;
- switch (server_addr->sa_family)
- {
- case AF_INET:
- port = ntohs (((const struct sockaddr_in *) server_addr)->sin_port);
- break;
- case AF_INET6:
- port = ntohs (((const struct sockaddr_in6 *) server_addr)->sin6_port);
- break;
- case AF_UNIX:
- port = 0;
- break;
- default:
- GNUNET_break (0);
- port = 0;
- break;
- }
- sock = GNUNET_NETWORK_socket_create (server_addr->sa_family, SOCK_STREAM, 0);
- if (NULL == sock)
- {
- LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket");
- errno = 0;
- return NULL;
- }
- /* bind the socket */
- if (GNUNET_OK != GNUNET_NETWORK_socket_bind (sock, server_addr, socklen))
- {
- eno = errno;
- if (EADDRINUSE != errno)
- {
- /* we don't log 'EADDRINUSE' here since an IPv4 bind may
- * fail if we already took the port on IPv6; if both IPv4 and
- * IPv6 binds fail, then our caller will log using the
- * errno preserved in 'eno' */
- LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
- "bind");
- if (0 != port)
- LOG (GNUNET_ERROR_TYPE_ERROR,
- _("`%s' failed for port %d (%s).\n"),
- "bind",
- port,
- (AF_INET == server_addr->sa_family) ? "IPv4" : "IPv6");
- eno = 0;
- }
- else
- {
- if (0 != port)
- LOG (GNUNET_ERROR_TYPE_WARNING,
- _("`%s' failed for port %d (%s): address already in use\n"),
- "bind", port,
- (AF_INET == server_addr->sa_family) ? "IPv4" : "IPv6");
- else if (AF_UNIX == server_addr->sa_family)
- {
- LOG (GNUNET_ERROR_TYPE_WARNING,
- _("`%s' failed for `%s': address already in use\n"),
- "bind",
- GNUNET_a2s (server_addr, socklen));
- }
- }
- GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
- errno = eno;
- return NULL;
- }
- if (GNUNET_OK != GNUNET_NETWORK_socket_listen (sock, 5))
- {
- LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
- "listen");
- GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
- errno = 0;
- return NULL;
- }
- if (0 != port)
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Server starts to listen on port %u.\n",
- port);
- return sock;
- }
- /**
- * Create a new server.
- *
- * @param access_cb function for access control
- * @param access_cb_cls closure for @a access_cb
- * @param lsocks NULL-terminated array of listen sockets
- * @param idle_timeout after how long should we timeout idle connections?
- * @param require_found if #GNUNET_YES, connections sending messages of unknown type
- * will be closed
- * @return handle for the new server, NULL on error
- * (typically, "port" already in use)
- */
- struct GNUNET_SERVER_Handle *
- GNUNET_SERVER_create_with_sockets (GNUNET_CONNECTION_AccessCheck access_cb,
- void *access_cb_cls,
- struct GNUNET_NETWORK_Handle **lsocks,
- struct GNUNET_TIME_Relative idle_timeout,
- int require_found)
- {
- struct GNUNET_SERVER_Handle *server;
- server = GNUNET_new (struct GNUNET_SERVER_Handle);
- server->idle_timeout = idle_timeout;
- server->listen_sockets = lsocks;
- server->access_cb = access_cb;
- server->access_cb_cls = access_cb_cls;
- server->require_found = require_found;
- if (NULL != lsocks)
- GNUNET_SERVER_resume (server);
- return server;
- }
- /**
- * Create a new server.
- *
- * @param access_cb function for access control
- * @param access_cb_cls closure for @a access_cb
- * @param server_addr address to listen on (including port), NULL terminated array
- * @param socklen length of server_addr
- * @param idle_timeout after how long should we timeout idle connections?
- * @param require_found if YES, connections sending messages of unknown type
- * will be closed
- * @return handle for the new server, NULL on error
- * (typically, "port" already in use)
- */
- struct GNUNET_SERVER_Handle *
- GNUNET_SERVER_create (GNUNET_CONNECTION_AccessCheck access_cb,
- void *access_cb_cls,
- struct sockaddr *const *server_addr,
- const socklen_t * socklen,
- struct GNUNET_TIME_Relative idle_timeout,
- int require_found)
- {
- struct GNUNET_NETWORK_Handle **lsocks;
- unsigned int i;
- unsigned int j;
- unsigned int k;
- int seen;
- i = 0;
- while (NULL != server_addr[i])
- i++;
- if (i > 0)
- {
- lsocks = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (i + 1));
- i = 0;
- j = 0;
- while (NULL != server_addr[i])
- {
- seen = 0;
- for (k=0;k<i;k++)
- if ( (socklen[k] == socklen[i]) &&
- (0 == memcmp (server_addr[k], server_addr[i], socklen[i])) )
- {
- seen = 1;
- break;
- }
- if (0 != seen)
- {
- /* duplicate address, skip */
- i++;
- continue;
- }
- lsocks[j] = open_listen_socket (server_addr[i], socklen[i]);
- if (NULL != lsocks[j])
- j++;
- i++;
- }
- if (0 == j)
- {
- if (0 != errno)
- LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "bind");
- GNUNET_free (lsocks);
- lsocks = NULL;
- }
- }
- else
- {
- lsocks = NULL;
- }
- return GNUNET_SERVER_create_with_sockets (access_cb,
- access_cb_cls,
- lsocks,
- idle_timeout,
- require_found);
- }
- /**
- * Set the 'monitor' flag on this client. Clients which have been
- * marked as 'monitors' won't prevent the server from shutting down
- * once '#GNUNET_SERVER_stop_listening()' has been invoked. The idea is
- * that for "normal" clients we likely want to allow them to process
- * their requests; however, monitor-clients are likely to 'never'
- * disconnect during shutdown and thus will not be considered when
- * determining if the server should continue to exist after
- * #GNUNET_SERVER_destroy() has been called.
- *
- * @param client the client to set the 'monitor' flag on
- */
- void
- GNUNET_SERVER_client_mark_monitor (struct GNUNET_SERVER_Client *client)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Marking client as monitor!\n");
- client->is_monitor = GNUNET_YES;
- }
- /**
- * Helper function for #test_monitor_clients() to trigger
- * #GNUNET_SERVER_destroy() after the stack has unwound.
- *
- * @param cls the `struct GNUNET_SERVER_Handle *` to destroy
- */
- static void
- do_destroy (void *cls)
- {
- struct GNUNET_SERVER_Handle *server = cls;
- GNUNET_SERVER_destroy (server);
- }
- /**
- * Check if only 'monitor' clients are left. If so, destroy the
- * server completely.
- *
- * @param server server to test for full shutdown
- */
- static void
- test_monitor_clients (struct GNUNET_SERVER_Handle *server)
- {
- struct GNUNET_SERVER_Client *client;
- if (GNUNET_YES != server->in_soft_shutdown)
- return;
- for (client = server->clients_head; NULL != client; client = client->next)
- if (GNUNET_NO == client->is_monitor)
- return; /* not done yet */
- server->in_soft_shutdown = GNUNET_SYSERR;
- (void) GNUNET_SCHEDULER_add_now (&do_destroy, server);
- }
- /**
- * Suspend accepting connections from the listen socket temporarily.
- *
- * @param server server to stop accepting connections.
- */
- void
- GNUNET_SERVER_suspend (struct GNUNET_SERVER_Handle *server)
- {
- if (NULL != server->listen_task)
- {
- GNUNET_SCHEDULER_cancel (server->listen_task);
- server->listen_task = NULL;
- }
- }
- /**
- * Resume accepting connections from the listen socket.
- *
- * @param server server to stop accepting connections.
- */
- void
- GNUNET_SERVER_resume (struct GNUNET_SERVER_Handle *server)
- {
- struct GNUNET_NETWORK_FDSet *r;
- unsigned int i;
- if (NULL == server->listen_sockets)
- return;
- if (NULL == server->listen_sockets[0])
- return; /* nothing to do, no listen sockets! */
- if (NULL == server->listen_sockets[1])
- {
- /* simplified method: no fd set needed; this is then much simpler
- and much more efficient */
- server->listen_task =
- GNUNET_SCHEDULER_add_read_net_with_priority (GNUNET_TIME_UNIT_FOREVER_REL,
- GNUNET_SCHEDULER_PRIORITY_HIGH,
- server->listen_sockets[0],
- &process_listen_socket, server);
- return;
- }
- r = GNUNET_NETWORK_fdset_create ();
- i = 0;
- while (NULL != server->listen_sockets[i])
- GNUNET_NETWORK_fdset_set (r, server->listen_sockets[i++]);
- server->listen_task =
- GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
- GNUNET_TIME_UNIT_FOREVER_REL, r, NULL,
- &process_listen_socket, server);
- GNUNET_NETWORK_fdset_destroy (r);
- }
- /**
- * Stop the listen socket and get ready to shutdown the server
- * once only 'monitor' clients are left.
- *
- * @param server server to stop listening on
- */
- void
- GNUNET_SERVER_stop_listening (struct GNUNET_SERVER_Handle *server)
- {
- unsigned int i;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Server in soft shutdown\n");
- if (NULL != server->listen_task)
- {
- GNUNET_SCHEDULER_cancel (server->listen_task);
- server->listen_task = NULL;
- }
- if (NULL != server->listen_sockets)
- {
- i = 0;
- while (NULL != server->listen_sockets[i])
- GNUNET_break (GNUNET_OK ==
- GNUNET_NETWORK_socket_close (server->listen_sockets[i++]));
- GNUNET_free (server->listen_sockets);
- server->listen_sockets = NULL;
- }
- if (GNUNET_NO == server->in_soft_shutdown)
- server->in_soft_shutdown = GNUNET_YES;
- test_monitor_clients (server);
- }
- /**
- * Free resources held by this server.
- *
- * @param server server to destroy
- */
- void
- GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *server)
- {
- struct HandlerList *hpos;
- struct NotifyList *npos;
- unsigned int i;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Server shutting down.\n");
- if (NULL != server->listen_task)
- {
- GNUNET_SCHEDULER_cancel (server->listen_task);
- server->listen_task = NULL;
- }
- if (NULL != server->listen_sockets)
- {
- i = 0;
- while (NULL != server->listen_sockets[i])
- GNUNET_break (GNUNET_OK ==
- GNUNET_NETWORK_socket_close (server->listen_sockets[i++]));
- GNUNET_free (server->listen_sockets);
- server->listen_sockets = NULL;
- }
- while (NULL != server->clients_head)
- GNUNET_SERVER_client_disconnect (server->clients_head);
- while (NULL != (hpos = server->handlers))
- {
- server->handlers = hpos->next;
- GNUNET_free (hpos);
- }
- while (NULL != (npos = server->disconnect_notify_list_head))
- {
- npos->callback (npos->callback_cls,
- NULL);
- GNUNET_CONTAINER_DLL_remove (server->disconnect_notify_list_head,
- server->disconnect_notify_list_tail,
- npos);
- GNUNET_free (npos);
- }
- while (NULL != (npos = server->connect_notify_list_head))
- {
- npos->callback (npos->callback_cls,
- NULL);
- GNUNET_CONTAINER_DLL_remove (server->connect_notify_list_head,
- server->connect_notify_list_tail,
- npos);
- GNUNET_free (npos);
- }
- GNUNET_free (server);
- }
- /**
- * Add additional handlers to an existing server.
- *
- * @param server the server to add handlers to
- * @param handlers array of message handlers for
- * incoming messages; the last entry must
- * have "NULL" for the "callback"; multiple
- * entries for the same type are allowed,
- * they will be called in order of occurence.
- * These handlers can be removed later;
- * the handlers array must exist until removed
- * (or server is destroyed).
- */
- void
- GNUNET_SERVER_add_handlers (struct GNUNET_SERVER_Handle *server,
- const struct GNUNET_SERVER_MessageHandler *handlers)
- {
- struct HandlerList *p;
- p = GNUNET_new (struct HandlerList);
- p->handlers = handlers;
- p->next = server->handlers;
- server->handlers = p;
- }
- /**
- * Change functions used by the server to tokenize the message stream.
- * (very rarely used).
- *
- * @param server server to modify
- * @param create new tokenizer initialization function
- * @param destroy new tokenizer destruction function
- * @param receive new tokenizer receive function
- * @param cls closure for @a create, @a receive, @a destroy
- */
- void
- GNUNET_SERVER_set_callbacks (struct GNUNET_SERVER_Handle *server,
- GNUNET_SERVER_MstCreateCallback create,
- GNUNET_SERVER_MstDestroyCallback destroy,
- GNUNET_SERVER_MstReceiveCallback receive,
- void *cls)
- {
- server->mst_create = create;
- server->mst_destroy = destroy;
- server->mst_receive = receive;
- server->mst_cls = cls;
- }
- /**
- * Task run to warn about missing calls to #GNUNET_SERVER_receive_done.
- *
- * @param cls our `struct GNUNET_SERVER_Client *` to process more requests from
- */
- static void
- warn_no_receive_done (void *cls)
- {
- struct GNUNET_SERVER_Client *client = cls;
- GNUNET_break (0 != client->warn_type); /* type should never be 0 here, as we don't use 0 */
- client->warn_task =
- GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
- &warn_no_receive_done, client);
- LOG (GNUNET_ERROR_TYPE_WARNING,
- _("Processing code for message of type %u did not call `GNUNET_SERVER_receive_done' after %s\n"),
- (unsigned int) client->warn_type,
- GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (client->warn_start),
- GNUNET_YES));
- }
- /**
- * Disable the warning the server issues if a message is not acknowledged
- * in a timely fashion. Use this call if a client is intentionally delayed
- * for a while. Only applies to the current message.
- *
- * @param client client for which to disable the warning
- */
- void
- GNUNET_SERVER_disable_receive_done_warning (struct GNUNET_SERVER_Client *client)
- {
- if (NULL != client->warn_task)
- {
- GNUNET_SCHEDULER_cancel (client->warn_task);
- client->warn_task = NULL;
- }
- }
- /**
- * Inject a message into the server, pretend it came
- * from the specified client. Delivery of the message
- * will happen instantly (if a handler is installed;
- * otherwise the call does nothing).
- *
- * @param server the server receiving the message
- * @param sender the "pretended" sender of the message
- * can be NULL!
- * @param message message to transmit
- * @return #GNUNET_OK if the message was OK and the
- * connection can stay open
- * #GNUNET_SYSERR if the connection to the
- * client should be shut down
- */
- int
- GNUNET_SERVER_inject (struct GNUNET_SERVER_Handle *server,
- struct GNUNET_SERVER_Client *sender,
- const struct GNUNET_MessageHeader *message)
- {
- struct HandlerList *pos;
- const struct GNUNET_SERVER_MessageHandler *mh;
- unsigned int i;
- uint16_t type;
- uint16_t size;
- int found;
- type = ntohs (message->type);
- size = ntohs (message->size);
- LOG (GNUNET_ERROR_TYPE_INFO,
- "Received message of type %u and size %u from client\n",
- type, size);
- found = GNUNET_NO;
- for (pos = server->handlers; NULL != pos; pos = pos->next)
- {
- i = 0;
- while (pos->handlers[i].callback != NULL)
- {
- mh = &pos->handlers[i];
- if ((mh->type == type) || (mh->type == GNUNET_MESSAGE_TYPE_ALL))
- {
- if ((0 != mh->expected_size) && (mh->expected_size != size))
- {
- #if GNUNET8_NETWORK_IS_DEAD
- LOG (GNUNET_ERROR_TYPE_WARNING,
- "Expected %u bytes for message of type %u, got %u\n",
- mh->expected_size, mh->type, size);
- GNUNET_break_op (0);
- #else
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Expected %u bytes for message of type %u, got %u\n",
- mh->expected_size, mh->type, size);
- #endif
- return GNUNET_SYSERR;
- }
- if (NULL != sender)
- {
- if ( (0 == sender->suspended) &&
- (NULL == sender->warn_task) )
- {
- GNUNET_break (0 != type); /* type should never be 0 here, as we don't use 0 */
- sender->warn_start = GNUNET_TIME_absolute_get ();
- sender->warn_task =
- GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
- &warn_no_receive_done,
- sender);
- sender->warn_type = type;
- }
- sender->suspended++;
- }
- mh->callback (mh->callback_cls, sender, message);
- found = GNUNET_YES;
- }
- i++;
- }
- }
- if (GNUNET_NO == found)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
- "Received message of unknown type %d\n", type);
- if (GNUNET_YES == server->require_found)
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
- }
- /**
- * We are receiving an incoming message. Process it.
- *
- * @param cls our closure (handle for the client)
- * @param buf buffer with data received from network
- * @param available number of bytes available in buf
- * @param addr address of the sender
- * @param addrlen length of @a addr
- * @param errCode code indicating errors receiving, 0 for success
- */
- static void
- process_incoming (void *cls,
- const void *buf,
- size_t available,
- const struct sockaddr *addr,
- socklen_t addrlen,
- int errCode);
- /**
- * Process messages from the client's message tokenizer until either
- * the tokenizer is empty (and then schedule receiving more), or
- * until some handler is not immediately done (then wait for restart_processing)
- * or shutdown.
- *
- * @param client the client to process, RC must have already been increased
- * using #GNUNET_SERVER_client_keep and will be decreased by one in this
- * function
- * @param ret #GNUNET_NO to start processing from the buffer,
- * #GNUNET_OK if the mst buffer is drained and we should instantly go back to receiving
- * #GNUNET_SYSERR if we should instantly abort due to error in a previous step
- */
- static void
- process_mst (struct GNUNET_SERVER_Client *client,
- int ret)
- {
- while ((GNUNET_SYSERR != ret) && (NULL != client->server) &&
- (GNUNET_YES != client->shutdown_now) && (0 == client->suspended))
- {
- if (GNUNET_OK == ret)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Server re-enters receive loop, timeout: %s.\n",
- GNUNET_STRINGS_relative_time_to_string (client->idle_timeout, GNUNET_YES));
- client->receive_pending = GNUNET_YES;
- if (GNUNET_OK !=
- GNUNET_CONNECTION_receive (client->connection,
- GNUNET_MAX_MESSAGE_SIZE - 1,
- client->idle_timeout,
- &process_incoming,
- client))
- return;
- break;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Server processes additional messages instantly.\n");
- if (NULL != client->server->mst_receive)
- ret =
- client->server->mst_receive (client->server->mst_cls, client->mst,
- client, NULL, 0, GNUNET_NO, GNUNET_YES);
- else
- ret =
- GNUNET_SERVER_mst_receive (client->mst, client, NULL, 0, GNUNET_NO,
- GNUNET_YES);
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Server leaves instant processing loop: ret = %d, server = %p, shutdown = %d, suspended = %u\n",
- ret, client->server,
- client->shutdown_now,
- client->suspended);
- if (GNUNET_NO == ret)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Server has more data pending but is suspended.\n");
- client->receive_pending = GNUNET_SYSERR; /* data pending */
- }
- if ( (GNUNET_SYSERR == ret) ||
- (GNUNET_YES == client->shutdown_now) )
- GNUNET_SERVER_client_disconnect (client);
- }
- /**
- * We are receiving an incoming message. Process it.
- *
- * @param cls our closure (handle for the client)
- * @param buf buffer with data received from network
- * @param available number of bytes available in buf
- * @param addr address of the sender
- * @param addrlen length of @a addr
- * @param errCode code indicating errors receiving, 0 for success
- */
- static void
- process_incoming (void *cls,
- const void *buf,
- size_t available,
- const struct sockaddr *addr,
- socklen_t addrlen,
- int errCode)
- {
- struct GNUNET_SERVER_Client *client = cls;
- struct GNUNET_SERVER_Handle *server = client->server;
- struct GNUNET_TIME_Absolute end;
- struct GNUNET_TIME_Absolute now;
- int ret;
- GNUNET_assert (GNUNET_YES == client->receive_pending);
- client->receive_pending = GNUNET_NO;
- now = GNUNET_TIME_absolute_get ();
- end = GNUNET_TIME_absolute_add (client->last_activity,
- client->idle_timeout);
- if ( (NULL == buf) &&
- (0 == available) &&
- (NULL == addr) &&
- (0 == errCode) &&
- (GNUNET_YES != client->shutdown_now) &&
- (NULL != server) &&
- (GNUNET_YES == GNUNET_CONNECTION_check (client->connection)) &&
- (end.abs_value_us > now.abs_value_us) )
- {
- /* wait longer, timeout changed (i.e. due to us sending) */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Receive time out, but no disconnect due to sending (%p)\n",
- client);
- client->receive_pending = GNUNET_YES;
- GNUNET_CONNECTION_receive (client->connection,
- GNUNET_MAX_MESSAGE_SIZE - 1,
- GNUNET_TIME_absolute_get_remaining (end),
- &process_incoming,
- client);
- return;
- }
- if ( (NULL == buf) ||
- (0 == available) ||
- (0 != errCode) ||
- (NULL == server) ||
- (GNUNET_YES == client->shutdown_now) ||
- (GNUNET_YES != GNUNET_CONNECTION_check (client->connection)) )
- {
- /* other side closed connection, error connecting, etc. */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Failed to connect or other side closed connection (%p)\n",
- client);
- GNUNET_SERVER_client_disconnect (client);
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Server receives %u bytes from `%s'.\n",
- (unsigned int) available,
- GNUNET_a2s (addr, addrlen));
- GNUNET_SERVER_client_keep (client);
- client->last_activity = now;
- if (NULL != server->mst_receive)
- {
- ret = client->server->mst_receive (client->server->mst_cls,
- client->mst,
- client,
- buf,
- available,
- GNUNET_NO,
- GNUNET_YES);
- }
- else if (NULL != client->mst)
- {
- ret =
- GNUNET_SERVER_mst_receive (client->mst,
- client,
- buf,
- available,
- GNUNET_NO,
- GNUNET_YES);
- }
- else
- {
- GNUNET_break (0);
- return;
- }
- process_mst (client,
- ret);
- GNUNET_SERVER_client_drop (client);
- }
- /**
- * Task run to start again receiving from the network
- * and process requests.
- *
- * @param cls our `struct GNUNET_SERVER_Client *` to process more requests from
- */
- static void
- restart_processing (void *cls)
- {
- struct GNUNET_SERVER_Client *client = cls;
- GNUNET_assert (GNUNET_YES != client->shutdown_now);
- client->restart_task = NULL;
- if (GNUNET_NO == client->receive_pending)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG, "Server begins to read again from client.\n");
- client->receive_pending = GNUNET_YES;
- GNUNET_CONNECTION_receive (client->connection,
- GNUNET_MAX_MESSAGE_SIZE - 1,
- client->idle_timeout,
- &process_incoming,
- client);
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Server continues processing messages still in the buffer.\n");
- GNUNET_SERVER_client_keep (client);
- client->receive_pending = GNUNET_NO;
- process_mst (client,
- GNUNET_NO);
- GNUNET_SERVER_client_drop (client);
- }
- /**
- * This function is called whenever our inbound message tokenizer has
- * received a complete message.
- *
- * @param cls closure (struct GNUNET_SERVER_Handle)
- * @param client identification of the client (`struct GNUNET_SERVER_Client *`)
- * @param message the actual message
- *
- * @return #GNUNET_OK on success, #GNUNET_SYSERR to stop further processing
- */
- static int
- client_message_tokenizer_callback (void *cls,
- void *client,
- const struct GNUNET_MessageHeader *message)
- {
- struct GNUNET_SERVER_Handle *server = cls;
- struct GNUNET_SERVER_Client *sender = client;
- int ret;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Tokenizer gives server message of type %u and size %u from client\n",
- ntohs (message->type), ntohs (message->size));
- sender->in_process_client_buffer = GNUNET_YES;
- ret = GNUNET_SERVER_inject (server, sender, message);
- sender->in_process_client_buffer = GNUNET_NO;
- if ( (GNUNET_OK != ret) || (GNUNET_YES == sender->shutdown_now) )
- {
- GNUNET_SERVER_client_disconnect (sender);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
- }
- /**
- * Add a TCP socket-based connection to the set of handles managed by
- * this server. Use this function for outgoing (P2P) connections that
- * we initiated (and where this server should process incoming
- * messages).
- *
- * @param server the server to use
- * @param connection the connection to manage (client must
- * stop using this connection from now on)
- * @return the client handle
- */
- struct GNUNET_SERVER_Client *
- GNUNET_SERVER_connect_socket (struct GNUNET_SERVER_Handle *server,
- struct GNUNET_CONNECTION_Handle *connection)
- {
- struct GNUNET_SERVER_Client *client;
- struct NotifyList *n;
- client = GNUNET_new (struct GNUNET_SERVER_Client);
- client->connection = connection;
- client->server = server;
- client->last_activity = GNUNET_TIME_absolute_get ();
- client->idle_timeout = server->idle_timeout;
- GNUNET_CONTAINER_DLL_insert (server->clients_head,
- server->clients_tail,
- client);
- if (NULL != server->mst_create)
- client->mst =
- server->mst_create (server->mst_cls, client);
- else
- client->mst =
- GNUNET_SERVER_mst_create (&client_message_tokenizer_callback,
- server);
- GNUNET_assert (NULL != client->mst);
- for (n = server->connect_notify_list_head; NULL != n; n = n->next)
- n->callback (n->callback_cls, client);
- client->receive_pending = GNUNET_YES;
- if (GNUNET_SYSERR ==
- GNUNET_CONNECTION_receive (client->connection,
- GNUNET_MAX_MESSAGE_SIZE - 1,
- client->idle_timeout,
- &process_incoming,
- client))
- return NULL;
- return client;
- }
- /**
- * Change the timeout for a particular client. Decreasing the timeout
- * may not go into effect immediately (only after the previous timeout
- * times out or activity happens on the socket).
- *
- * @param client the client to update
- * @param timeout new timeout for activities on the socket
- */
- void
- GNUNET_SERVER_client_set_timeout (struct GNUNET_SERVER_Client *client,
- struct GNUNET_TIME_Relative timeout)
- {
- client->idle_timeout = timeout;
- }
- /**
- * Notify the server that the given client handle should
- * be kept (keeps the connection up if possible, increments
- * the internal reference counter).
- *
- * @param client the client to keep
- */
- void
- GNUNET_SERVER_client_keep (struct GNUNET_SERVER_Client *client)
- {
- client->reference_count++;
- }
- /**
- * Notify the server that the given client handle is no
- * longer required. Decrements the reference counter. If
- * that counter reaches zero an inactive connection maybe
- * closed.
- *
- * @param client the client to drop
- */
- void
- GNUNET_SERVER_client_drop (struct GNUNET_SERVER_Client *client)
- {
- GNUNET_assert (client->reference_count > 0);
- client->reference_count--;
- if ((GNUNET_YES == client->shutdown_now) && (0 == client->reference_count))
- GNUNET_SERVER_client_disconnect (client);
- }
- /**
- * Obtain the network address of the other party.
- *
- * @param client the client to get the address for
- * @param addr where to store the address
- * @param addrlen where to store the length of the @a addr
- * @return #GNUNET_OK on success
- */
- int
- GNUNET_SERVER_client_get_address (struct GNUNET_SERVER_Client *client,
- void **addr, size_t * addrlen)
- {
- return GNUNET_CONNECTION_get_address (client->connection, addr, addrlen);
- }
- /**
- * Ask the server to notify us whenever a client disconnects.
- * This function is called whenever the actual network connection
- * is closed; the reference count may be zero or larger than zero
- * at this point.
- *
- * @param server the server manageing the clients
- * @param callback function to call on disconnect
- * @param callback_cls closure for @a callback
- */
- void
- GNUNET_SERVER_disconnect_notify (struct GNUNET_SERVER_Handle *server,
- GNUNET_SERVER_DisconnectCallback callback,
- void *callback_cls)
- {
- struct NotifyList *n;
- n = GNUNET_new (struct NotifyList);
- n->callback = callback;
- n->callback_cls = callback_cls;
- GNUNET_CONTAINER_DLL_insert (server->disconnect_notify_list_head,
- server->disconnect_notify_list_tail,
- n);
- }
- /**
- * Ask the server to notify us whenever a client connects.
- * This function is called whenever the actual network connection
- * is opened. If the server is destroyed before this
- * notification is explicitly cancelled, the 'callback' will
- * once be called with a 'client' argument of NULL to indicate
- * that the server itself is now gone (and that the callback
- * won't be called anymore and also can no longer be cancelled).
- *
- * @param server the server manageing the clients
- * @param callback function to call on sconnect
- * @param callback_cls closure for @a callback
- */
- void
- GNUNET_SERVER_connect_notify (struct GNUNET_SERVER_Handle *server,
- GNUNET_SERVER_ConnectCallback callback,
- void *callback_cls)
- {
- struct NotifyList *n;
- struct GNUNET_SERVER_Client *client;
- n = GNUNET_new (struct NotifyList);
- n->callback = callback;
- n->callback_cls = callback_cls;
- GNUNET_CONTAINER_DLL_insert (server->connect_notify_list_head,
- server->connect_notify_list_tail,
- n);
- for (client = server->clients_head; NULL != client; client = client->next)
- callback (callback_cls, client);
- }
- /**
- * Ask the server to stop notifying us whenever a client connects.
- *
- * @param server the server manageing the clients
- * @param callback function to call on connect
- * @param callback_cls closure for @a callback
- */
- void
- GNUNET_SERVER_disconnect_notify_cancel (struct GNUNET_SERVER_Handle *server,
- GNUNET_SERVER_DisconnectCallback callback,
- void *callback_cls)
- {
- struct NotifyList *pos;
- for (pos = server->disconnect_notify_list_head; NULL != pos; pos = pos->next)
- if ((pos->callback == callback) && (pos->callback_cls == callback_cls))
- break;
- if (NULL == pos)
- {
- GNUNET_break (0);
- return;
- }
- GNUNET_CONTAINER_DLL_remove (server->disconnect_notify_list_head,
- server->disconnect_notify_list_tail,
- pos);
- GNUNET_free (pos);
- }
- /**
- * Ask the server to stop notifying us whenever a client disconnects.
- *
- * @param server the server manageing the clients
- * @param callback function to call on disconnect
- * @param callback_cls closure for @a callback
- */
- void
- GNUNET_SERVER_connect_notify_cancel (struct GNUNET_SERVER_Handle *server,
- GNUNET_SERVER_ConnectCallback callback,
- void *callback_cls)
- {
- struct NotifyList *pos;
- for (pos = server->connect_notify_list_head; NULL != pos; pos = pos->next)
- if ((pos->callback == callback) && (pos->callback_cls == callback_cls))
- break;
- if (NULL == pos)
- {
- GNUNET_break (0);
- return;
- }
- GNUNET_CONTAINER_DLL_remove (server->connect_notify_list_head,
- server->connect_notify_list_tail,
- pos);
- GNUNET_free (pos);
- }
- /**
- * Ask the server to disconnect from the given client.
- * This is the same as returning #GNUNET_SYSERR from a message
- * handler, except that it allows dropping of a client even
- * when not handling a message from that client.
- *
- * @param client the client to disconnect from
- */
- void
- GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client)
- {
- struct GNUNET_SERVER_Handle *server = client->server;
- struct NotifyList *n;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Client is being disconnected from the server.\n");
- if (NULL != client->restart_task)
- {
- GNUNET_SCHEDULER_cancel (client->restart_task);
- client->restart_task = NULL;
- }
- if (NULL != client->warn_task)
- {
- GNUNET_SCHEDULER_cancel (client->warn_task);
- client->warn_task = NULL;
- }
- if (GNUNET_YES == client->receive_pending)
- {
- GNUNET_CONNECTION_receive_cancel (client->connection);
- client->receive_pending = GNUNET_NO;
- }
- client->shutdown_now = GNUNET_YES;
- client->reference_count++; /* make sure nobody else clean up client... */
- if ( (NULL != client->mst) &&
- (NULL != server) )
- {
- GNUNET_CONTAINER_DLL_remove (server->clients_head,
- server->clients_tail,
- client);
- if (NULL != server->mst_destroy)
- server->mst_destroy (server->mst_cls,
- client->mst);
- else
- GNUNET_SERVER_mst_destroy (client->mst);
- client->mst = NULL;
- for (n = server->disconnect_notify_list_head; NULL != n; n = n->next)
- n->callback (n->callback_cls,
- client);
- }
- client->reference_count--;
- if (client->reference_count > 0)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "RC of %p still positive, not destroying everything.\n",
- client);
- client->server = NULL;
- return;
- }
- if (GNUNET_YES == client->in_process_client_buffer)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Still processing inputs of %p, not destroying everything.\n",
- client);
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "RC of %p now zero, destroying everything.\n",
- client);
- if (GNUNET_YES == client->persist)
- GNUNET_CONNECTION_persist_ (client->connection);
- if (NULL != client->th.cth)
- GNUNET_SERVER_notify_transmit_ready_cancel (&client->th);
- GNUNET_CONNECTION_destroy (client->connection);
- /* need to cancel again, as it might have been re-added
- in the meantime (i.e. during callbacks) */
- if (NULL != client->warn_task)
- {
- GNUNET_SCHEDULER_cancel (client->warn_task);
- client->warn_task = NULL;
- }
- if (GNUNET_YES == client->receive_pending)
- {
- GNUNET_CONNECTION_receive_cancel (client->connection);
- client->receive_pending = GNUNET_NO;
- }
- GNUNET_free (client);
- /* we might be in soft-shutdown, test if we're done */
- if (NULL != server)
- test_monitor_clients (server);
- }
- /**
- * Disable the "CORK" feature for communication with the given client,
- * forcing the OS to immediately flush the buffer on transmission
- * instead of potentially buffering multiple messages.
- *
- * @param client handle to the client
- * @return #GNUNET_OK on success
- */
- int
- GNUNET_SERVER_client_disable_corking (struct GNUNET_SERVER_Client *client)
- {
- return GNUNET_CONNECTION_disable_corking (client->connection);
- }
- /**
- * Wrapper for transmission notification that calls the original
- * callback and update the last activity time for our connection.
- *
- * @param cls the `struct GNUNET_SERVER_Client *`
- * @param size number of bytes we can transmit
- * @param buf where to copy the message
- * @return number of bytes actually transmitted
- */
- static size_t
- transmit_ready_callback_wrapper (void *cls, size_t size, void *buf)
- {
- struct GNUNET_SERVER_Client *client = cls;
- GNUNET_CONNECTION_TransmitReadyNotify callback;
- client->th.cth = NULL;
- callback = client->th.callback;
- client->th.callback = NULL;
- client->last_activity = GNUNET_TIME_absolute_get ();
- return callback (client->th.callback_cls, size, buf);
- }
- /**
- * Notify us when the server has enough space to transmit
- * a message of the given size to the given client.
- *
- * @param client client to transmit message to
- * @param size requested amount of buffer space
- * @param timeout after how long should we give up (and call
- * notify with buf NULL and size 0)?
- * @param callback function to call when space is available
- * @param callback_cls closure for @a callback
- * @return non-NULL if the notify callback was queued; can be used
- * to cancel the request using
- * #GNUNET_SERVER_notify_transmit_ready_cancel().
- * NULL if we are already going to notify someone else (busy)
- */
- struct GNUNET_SERVER_TransmitHandle *
- GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client,
- size_t size,
- struct GNUNET_TIME_Relative timeout,
- GNUNET_CONNECTION_TransmitReadyNotify callback,
- void *callback_cls)
- {
- if (NULL != client->th.callback)
- return NULL;
- client->th.callback_cls = callback_cls;
- client->th.callback = callback;
- client->th.cth = GNUNET_CONNECTION_notify_transmit_ready (client->connection, size,
- timeout,
- &transmit_ready_callback_wrapper,
- client);
- return &client->th;
- }
- /**
- * Abort transmission request.
- *
- * @param th request to abort
- */
- void
- GNUNET_SERVER_notify_transmit_ready_cancel (struct GNUNET_SERVER_TransmitHandle *th)
- {
- GNUNET_CONNECTION_notify_transmit_ready_cancel (th->cth);
- th->cth = NULL;
- th->callback = NULL;
- }
- /**
- * Set the persistent flag on this client, used to setup client connection
- * to only be killed when the service it's connected to is actually dead.
- *
- * @param client the client to set the persistent flag on
- */
- void
- GNUNET_SERVER_client_persist_ (struct GNUNET_SERVER_Client *client)
- {
- client->persist = GNUNET_YES;
- }
- /**
- * Resume receiving from this client, we are done processing the
- * current request. This function must be called from within each
- * GNUNET_SERVER_MessageCallback (or its respective continuations).
- *
- * @param client client we were processing a message of
- * @param success #GNUNET_OK to keep the connection open and
- * continue to receive
- * #GNUNET_NO to close the connection (normal behavior)
- * #GNUNET_SYSERR to close the connection (signal
- * serious error)
- */
- void
- GNUNET_SERVER_receive_done (struct GNUNET_SERVER_Client *client,
- int success)
- {
- if (NULL == client)
- return;
- GNUNET_assert (client->suspended > 0);
- client->suspended--;
- if (GNUNET_OK != success)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "GNUNET_SERVER_receive_done called with failure indication\n");
- if ( (client->reference_count > 0) || (client->suspended > 0) )
- client->shutdown_now = GNUNET_YES;
- else
- GNUNET_SERVER_client_disconnect (client);
- return;
- }
- if (client->suspended > 0)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "GNUNET_SERVER_receive_done called, but more clients pending\n");
- return;
- }
- if (NULL != client->warn_task)
- {
- GNUNET_SCHEDULER_cancel (client->warn_task);
- client->warn_task = NULL;
- }
- if (GNUNET_YES == client->in_process_client_buffer)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "GNUNET_SERVER_receive_done called while still in processing loop\n");
- return;
- }
- if ((NULL == client->server) || (GNUNET_YES == client->shutdown_now))
- {
- GNUNET_SERVER_client_disconnect (client);
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "GNUNET_SERVER_receive_done causes restart in reading from the socket\n");
- GNUNET_assert (NULL == client->restart_task);
- client->restart_task = GNUNET_SCHEDULER_add_now (&restart_processing,
- client);
- }
- /* end of server.c */
|